Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'drm-rust-next-2025-11-18' of https://gitlab.freedesktop.org/drm/rust/kernel into drm-next

Cross-subsystem Changes:

Rust
- Make slice::as_flattened usable on all supported versions of rustc.
- Add FromBytes::from_bytes_prefix() method.

Core Changes:

- Update Tyr in MAINTAINERS file.
- Remove redundant device ptr from Rust GEM object.
- Change how AlwaysRefCounted is implemented for GEM objects.
- Add deferred vm_bo cleanup to GPUVM and use it in Panthor.

Driver Changes:

Nova Core
- Introduction of bitfield! macro, with support for different storage sizes
and custom visibility.
- Introduction of safe converters between integer types for which the
conversion is lossless.
- GSP initialized up to fully booted state on Ampere.
- Use more future-proof register for GPU identification.
- Various simplifications and optimizations.

Nova
- Select NOVA_CORE.
- Depend on CONFIG_64BIT.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/aRxtJC0D1pQUepF4@google.com

+5780 -1130
-30
Documentation/gpu/nova/core/todo.rst
··· 44 44 | Complexity: Beginner 45 45 | Link: https://docs.rs/num/latest/num/trait.FromPrimitive.html 46 46 47 - Conversion from byte slices for types implementing FromBytes [TRSM] 48 - ------------------------------------------------------------------- 49 - 50 - We retrieve several structures from byte streams coming from the BIOS or loaded 51 - firmware. At the moment converting the bytes slice into the proper type require 52 - an inelegant `unsafe` operation; this will go away once `FromBytes` implements 53 - a proper `from_bytes` method. 54 - 55 - | Complexity: Beginner 56 - 57 - CoherentAllocation improvements [COHA] 58 - -------------------------------------- 59 - 60 - `CoherentAllocation` needs a safe way to write into the allocation, and to 61 - obtain slices within the allocation. 62 - 63 - | Complexity: Beginner 64 - | Contact: Abdiel Janulgue 65 - 66 47 Generic register abstraction [REGA] 67 48 ----------------------------------- 68 49 ··· 133 152 134 153 | Complexity: Intermediate 135 154 | Contact: Alexandre Courbot 136 - 137 - Delay / Sleep abstractions [DLAY] 138 - --------------------------------- 139 - 140 - Rust abstractions for the kernel's delay() and sleep() functions. 141 - 142 - FUJITA Tomonori plans to work on abstractions for read_poll_timeout_atomic() 143 - (and friends) [1]. 144 - 145 - | Complexity: Beginner 146 - | Link: https://lore.kernel.org/netdev/20250228.080550.354359820929821928.fujita.tomonori@gmail.com/ [1] 147 155 148 156 IRQ abstractions 149 157 ----------------
+1
MAINTAINERS
··· 8264 8264 W: https://drm.pages.freedesktop.org/maintainer-tools/drm-rust.html 8265 8265 T: git https://gitlab.freedesktop.org/drm/rust/kernel.git 8266 8266 F: drivers/gpu/drm/nova/ 8267 + F: drivers/gpu/drm/tyr/ 8267 8268 F: drivers/gpu/nova-core/ 8268 8269 F: rust/kernel/drm/ 8269 8270
+190
drivers/gpu/drm/drm_gpuvm.c
··· 878 878 } 879 879 880 880 /** 881 + * drm_gpuvm_bo_is_zombie() - check whether this vm_bo is scheduled for cleanup 882 + * @vm_bo: the &drm_gpuvm_bo 883 + * 884 + * When a vm_bo is scheduled for cleanup using the bo_defer list, it is not 885 + * immediately removed from the evict and extobj lists. Therefore, anyone 886 + * iterating these lists should skip entries that are being destroyed. 887 + * 888 + * Checking the refcount without incrementing it is okay as long as the lock 889 + * protecting the evict/extobj list is held for as long as you are using the 890 + * vm_bo, because even if the refcount hits zero while you are using it, freeing 891 + * the vm_bo requires taking the list's lock. 892 + * 893 + * Zombie entries can be observed on the evict and extobj lists regardless of 894 + * whether DRM_GPUVM_RESV_PROTECTED is used, but they remain on the lists for a 895 + * longer time when the resv lock is used because we can't take the resv lock 896 + * during run_job() in immediate mode, meaning that they need to remain on the 897 + * lists until drm_gpuvm_bo_deferred_cleanup() is called. 898 + */ 899 + static bool 900 + drm_gpuvm_bo_is_zombie(struct drm_gpuvm_bo *vm_bo) 901 + { 902 + return !kref_read(&vm_bo->kref); 903 + } 904 + 905 + /** 881 906 * drm_gpuvm_bo_list_add() - insert a vm_bo into the given list 882 907 * @__vm_bo: the &drm_gpuvm_bo 883 908 * @__list_name: the name of the list to insert into ··· 1107 1082 INIT_LIST_HEAD(&gpuvm->evict.list); 1108 1083 spin_lock_init(&gpuvm->evict.lock); 1109 1084 1085 + init_llist_head(&gpuvm->bo_defer); 1086 + 1110 1087 kref_init(&gpuvm->kref); 1111 1088 1112 1089 gpuvm->name = name ? name : "unknown"; ··· 1150 1123 "Extobj list should be empty.\n"); 1151 1124 drm_WARN(gpuvm->drm, !list_empty(&gpuvm->evict.list), 1152 1125 "Evict list should be empty.\n"); 1126 + drm_WARN(gpuvm->drm, !llist_empty(&gpuvm->bo_defer), 1127 + "VM BO cleanup list should be empty.\n"); 1153 1128 1154 1129 drm_gem_object_put(gpuvm->r_obj); 1155 1130 } ··· 1247 1218 1248 1219 drm_gpuvm_resv_assert_held(gpuvm); 1249 1220 list_for_each_entry(vm_bo, &gpuvm->extobj.list, list.entry.extobj) { 1221 + if (drm_gpuvm_bo_is_zombie(vm_bo)) 1222 + continue; 1223 + 1250 1224 ret = exec_prepare_obj(exec, vm_bo->obj, num_fences); 1251 1225 if (ret) 1252 1226 break; ··· 1493 1461 1494 1462 list_for_each_entry_safe(vm_bo, next, &gpuvm->evict.list, 1495 1463 list.entry.evict) { 1464 + if (drm_gpuvm_bo_is_zombie(vm_bo)) 1465 + continue; 1466 + 1496 1467 ret = ops->vm_bo_validate(vm_bo, exec); 1497 1468 if (ret) 1498 1469 break; ··· 1596 1561 1597 1562 INIT_LIST_HEAD(&vm_bo->list.entry.extobj); 1598 1563 INIT_LIST_HEAD(&vm_bo->list.entry.evict); 1564 + init_llist_node(&vm_bo->list.entry.bo_defer); 1599 1565 1600 1566 return vm_bo; 1601 1567 } ··· 1657 1621 return false; 1658 1622 } 1659 1623 EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put); 1624 + 1625 + /* 1626 + * drm_gpuvm_bo_into_zombie() - called when the vm_bo becomes a zombie due to 1627 + * deferred cleanup 1628 + * 1629 + * If deferred cleanup is used, then this must be called right after the vm_bo 1630 + * refcount drops to zero. Must be called with GEM mutex held. After releasing 1631 + * the GEM mutex, drm_gpuvm_bo_defer_zombie_cleanup() must be called. 1632 + */ 1633 + static void 1634 + drm_gpuvm_bo_into_zombie(struct kref *kref) 1635 + { 1636 + struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo, 1637 + kref); 1638 + 1639 + if (!drm_gpuvm_resv_protected(vm_bo->vm)) { 1640 + drm_gpuvm_bo_list_del(vm_bo, extobj, true); 1641 + drm_gpuvm_bo_list_del(vm_bo, evict, true); 1642 + } 1643 + 1644 + list_del(&vm_bo->list.entry.gem); 1645 + } 1646 + 1647 + /* 1648 + * drm_gpuvm_bo_defer_zombie_cleanup() - adds a new zombie vm_bo to the 1649 + * bo_defer list 1650 + * 1651 + * Called after drm_gpuvm_bo_into_zombie(). GEM mutex must not be held. 1652 + * 1653 + * It's important that the GEM stays alive for the duration in which we hold 1654 + * the mutex, but the instant we add the vm_bo to bo_defer, another thread 1655 + * might call drm_gpuvm_bo_deferred_cleanup() and put the GEM. Therefore, to 1656 + * avoid kfreeing a mutex we are holding, the GEM mutex must be released 1657 + * *before* calling this function. 1658 + */ 1659 + static void 1660 + drm_gpuvm_bo_defer_zombie_cleanup(struct drm_gpuvm_bo *vm_bo) 1661 + { 1662 + llist_add(&vm_bo->list.entry.bo_defer, &vm_bo->vm->bo_defer); 1663 + } 1664 + 1665 + static void 1666 + drm_gpuvm_bo_defer_free(struct kref *kref) 1667 + { 1668 + struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo, 1669 + kref); 1670 + 1671 + drm_gpuvm_bo_into_zombie(kref); 1672 + mutex_unlock(&vm_bo->obj->gpuva.lock); 1673 + drm_gpuvm_bo_defer_zombie_cleanup(vm_bo); 1674 + } 1675 + 1676 + /** 1677 + * drm_gpuvm_bo_put_deferred() - drop a struct drm_gpuvm_bo reference with 1678 + * deferred cleanup 1679 + * @vm_bo: the &drm_gpuvm_bo to release the reference of 1680 + * 1681 + * This releases a reference to @vm_bo. 1682 + * 1683 + * This might take and release the GEMs GPUVA lock. You should call 1684 + * drm_gpuvm_bo_deferred_cleanup() later to complete the cleanup process. 1685 + * 1686 + * Returns: true if vm_bo is being destroyed, false otherwise. 1687 + */ 1688 + bool 1689 + drm_gpuvm_bo_put_deferred(struct drm_gpuvm_bo *vm_bo) 1690 + { 1691 + if (!vm_bo) 1692 + return false; 1693 + 1694 + drm_WARN_ON(vm_bo->vm->drm, !drm_gpuvm_immediate_mode(vm_bo->vm)); 1695 + 1696 + return !!kref_put_mutex(&vm_bo->kref, 1697 + drm_gpuvm_bo_defer_free, 1698 + &vm_bo->obj->gpuva.lock); 1699 + } 1700 + EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put_deferred); 1701 + 1702 + /** 1703 + * drm_gpuvm_bo_deferred_cleanup() - clean up BOs in the deferred list 1704 + * deferred cleanup 1705 + * @gpuvm: the VM to clean up 1706 + * 1707 + * Cleans up &drm_gpuvm_bo instances in the deferred cleanup list. 1708 + */ 1709 + void 1710 + drm_gpuvm_bo_deferred_cleanup(struct drm_gpuvm *gpuvm) 1711 + { 1712 + const struct drm_gpuvm_ops *ops = gpuvm->ops; 1713 + struct drm_gpuvm_bo *vm_bo; 1714 + struct drm_gem_object *obj; 1715 + struct llist_node *bo_defer; 1716 + 1717 + bo_defer = llist_del_all(&gpuvm->bo_defer); 1718 + if (!bo_defer) 1719 + return; 1720 + 1721 + if (drm_gpuvm_resv_protected(gpuvm)) { 1722 + dma_resv_lock(drm_gpuvm_resv(gpuvm), NULL); 1723 + llist_for_each_entry(vm_bo, bo_defer, list.entry.bo_defer) { 1724 + drm_gpuvm_bo_list_del(vm_bo, extobj, false); 1725 + drm_gpuvm_bo_list_del(vm_bo, evict, false); 1726 + } 1727 + dma_resv_unlock(drm_gpuvm_resv(gpuvm)); 1728 + } 1729 + 1730 + while (bo_defer) { 1731 + vm_bo = llist_entry(bo_defer, struct drm_gpuvm_bo, list.entry.bo_defer); 1732 + bo_defer = bo_defer->next; 1733 + obj = vm_bo->obj; 1734 + if (ops && ops->vm_bo_free) 1735 + ops->vm_bo_free(vm_bo); 1736 + else 1737 + kfree(vm_bo); 1738 + 1739 + drm_gpuvm_put(gpuvm); 1740 + drm_gem_object_put(obj); 1741 + } 1742 + } 1743 + EXPORT_SYMBOL_GPL(drm_gpuvm_bo_deferred_cleanup); 1660 1744 1661 1745 static struct drm_gpuvm_bo * 1662 1746 __drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm, ··· 2104 1948 drm_gpuvm_bo_put(vm_bo); 2105 1949 } 2106 1950 EXPORT_SYMBOL_GPL(drm_gpuva_unlink); 1951 + 1952 + /** 1953 + * drm_gpuva_unlink_defer() - unlink a &drm_gpuva with deferred vm_bo cleanup 1954 + * @va: the &drm_gpuva to unlink 1955 + * 1956 + * Similar to drm_gpuva_unlink(), but uses drm_gpuvm_bo_put_deferred() and takes 1957 + * the lock for the caller. 1958 + */ 1959 + void 1960 + drm_gpuva_unlink_defer(struct drm_gpuva *va) 1961 + { 1962 + struct drm_gem_object *obj = va->gem.obj; 1963 + struct drm_gpuvm_bo *vm_bo = va->vm_bo; 1964 + bool should_defer_bo; 1965 + 1966 + if (unlikely(!obj)) 1967 + return; 1968 + 1969 + drm_WARN_ON(vm_bo->vm->drm, !drm_gpuvm_immediate_mode(vm_bo->vm)); 1970 + 1971 + mutex_lock(&obj->gpuva.lock); 1972 + list_del_init(&va->gem.entry); 1973 + 1974 + /* 1975 + * This is drm_gpuvm_bo_put_deferred() except we already hold the mutex. 1976 + */ 1977 + should_defer_bo = kref_put(&vm_bo->kref, drm_gpuvm_bo_into_zombie); 1978 + mutex_unlock(&obj->gpuva.lock); 1979 + if (should_defer_bo) 1980 + drm_gpuvm_bo_defer_zombie_cleanup(vm_bo); 1981 + 1982 + va->vm_bo = NULL; 1983 + } 1984 + EXPORT_SYMBOL_GPL(drm_gpuva_unlink_defer); 2107 1985 2108 1986 /** 2109 1987 * drm_gpuva_find_first() - find the first &drm_gpuva in the given range
+2
drivers/gpu/drm/nova/Kconfig
··· 1 1 config DRM_NOVA 2 2 tristate "Nova DRM driver" 3 + depends on 64BIT 3 4 depends on DRM=y 4 5 depends on PCI 5 6 depends on RUST 6 7 select AUXILIARY_BUS 8 + select NOVA_CORE 7 9 default n 8 10 help 9 11 Choose this if you want to build the Nova DRM driver for Nvidia
+19 -91
drivers/gpu/drm/panthor/panthor_mmu.c
··· 182 182 u64 range; 183 183 } va; 184 184 185 - /** 186 - * @returned_vmas: List of panthor_vma objects returned after a VM operation. 187 - * 188 - * For unmap operations, this will contain all VMAs that were covered by the 189 - * specified VA range. 190 - * 191 - * For map operations, this will contain all VMAs that previously mapped to 192 - * the specified VA range. 193 - * 194 - * Those VMAs, and the resources they point to will be released as part of 195 - * the op_ctx cleanup operation. 196 - */ 197 - struct list_head returned_vmas; 198 - 199 185 /** @map: Fields specific to a map operation. */ 200 186 struct { 201 187 /** @map.vm_bo: Buffer object to map. */ ··· 1068 1082 mutex_unlock(&vm->mm_lock); 1069 1083 } 1070 1084 1071 - static void panthor_vm_bo_put(struct drm_gpuvm_bo *vm_bo) 1085 + static void panthor_vm_bo_free(struct drm_gpuvm_bo *vm_bo) 1072 1086 { 1073 1087 struct panthor_gem_object *bo = to_panthor_bo(vm_bo->obj); 1074 - struct drm_gpuvm *vm = vm_bo->vm; 1075 - bool unpin; 1076 1088 1077 - /* We must retain the GEM before calling drm_gpuvm_bo_put(), 1078 - * otherwise the mutex might be destroyed while we hold it. 1079 - * Same goes for the VM, since we take the VM resv lock. 1080 - */ 1081 - drm_gem_object_get(&bo->base.base); 1082 - drm_gpuvm_get(vm); 1083 - 1084 - /* We take the resv lock to protect against concurrent accesses to the 1085 - * gpuvm evicted/extobj lists that are modified in 1086 - * drm_gpuvm_bo_destroy(), which is called if drm_gpuvm_bo_put() 1087 - * releases sthe last vm_bo reference. 1088 - * We take the BO GPUVA list lock to protect the vm_bo removal from the 1089 - * GEM vm_bo list. 1090 - */ 1091 - dma_resv_lock(drm_gpuvm_resv(vm), NULL); 1092 - mutex_lock(&bo->base.base.gpuva.lock); 1093 - unpin = drm_gpuvm_bo_put(vm_bo); 1094 - mutex_unlock(&bo->base.base.gpuva.lock); 1095 - dma_resv_unlock(drm_gpuvm_resv(vm)); 1096 - 1097 - /* If the vm_bo object was destroyed, release the pin reference that 1098 - * was hold by this object. 1099 - */ 1100 - if (unpin && !drm_gem_is_imported(&bo->base.base)) 1089 + if (!drm_gem_is_imported(&bo->base.base)) 1101 1090 drm_gem_shmem_unpin(&bo->base); 1102 - 1103 - drm_gpuvm_put(vm); 1104 - drm_gem_object_put(&bo->base.base); 1091 + kfree(vm_bo); 1105 1092 } 1106 1093 1107 1094 static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx, 1108 1095 struct panthor_vm *vm) 1109 1096 { 1110 - struct panthor_vma *vma, *tmp_vma; 1111 - 1112 1097 u32 remaining_pt_count = op_ctx->rsvd_page_tables.count - 1113 1098 op_ctx->rsvd_page_tables.ptr; 1114 1099 ··· 1092 1135 kfree(op_ctx->rsvd_page_tables.pages); 1093 1136 1094 1137 if (op_ctx->map.vm_bo) 1095 - panthor_vm_bo_put(op_ctx->map.vm_bo); 1138 + drm_gpuvm_bo_put_deferred(op_ctx->map.vm_bo); 1096 1139 1097 1140 for (u32 i = 0; i < ARRAY_SIZE(op_ctx->preallocated_vmas); i++) 1098 1141 kfree(op_ctx->preallocated_vmas[i]); 1099 1142 1100 - list_for_each_entry_safe(vma, tmp_vma, &op_ctx->returned_vmas, node) { 1101 - list_del(&vma->node); 1102 - panthor_vm_bo_put(vma->base.vm_bo); 1103 - kfree(vma); 1104 - } 1143 + drm_gpuvm_bo_deferred_cleanup(&vm->base); 1105 1144 } 1106 1145 1107 1146 static void ··· 1200 1247 return -EINVAL; 1201 1248 1202 1249 memset(op_ctx, 0, sizeof(*op_ctx)); 1203 - INIT_LIST_HEAD(&op_ctx->returned_vmas); 1204 1250 op_ctx->flags = flags; 1205 1251 op_ctx->va.range = size; 1206 1252 op_ctx->va.addr = va; ··· 1210 1258 1211 1259 if (!drm_gem_is_imported(&bo->base.base)) { 1212 1260 /* Pre-reserve the BO pages, so the map operation doesn't have to 1213 - * allocate. 1261 + * allocate. This pin is dropped in panthor_vm_bo_free(), so 1262 + * once we have successfully called drm_gpuvm_bo_create(), 1263 + * GPUVM will take care of dropping the pin for us. 1214 1264 */ 1215 1265 ret = drm_gem_shmem_pin(&bo->base); 1216 1266 if (ret) ··· 1250 1296 op_ctx->map.vm_bo = drm_gpuvm_bo_obtain_prealloc(preallocated_vm_bo); 1251 1297 mutex_unlock(&bo->base.base.gpuva.lock); 1252 1298 dma_resv_unlock(panthor_vm_resv(vm)); 1253 - 1254 - /* If the a vm_bo for this <VM,BO> combination exists, it already 1255 - * retains a pin ref, and we can release the one we took earlier. 1256 - * 1257 - * If our pre-allocated vm_bo is picked, it now retains the pin ref, 1258 - * which will be released in panthor_vm_bo_put(). 1259 - */ 1260 - if (preallocated_vm_bo != op_ctx->map.vm_bo && 1261 - !drm_gem_is_imported(&bo->base.base)) 1262 - drm_gem_shmem_unpin(&bo->base); 1263 1299 1264 1300 op_ctx->map.bo_offset = offset; 1265 1301 ··· 1298 1354 int ret; 1299 1355 1300 1356 memset(op_ctx, 0, sizeof(*op_ctx)); 1301 - INIT_LIST_HEAD(&op_ctx->returned_vmas); 1302 1357 op_ctx->va.range = size; 1303 1358 op_ctx->va.addr = va; 1304 1359 op_ctx->flags = DRM_PANTHOR_VM_BIND_OP_TYPE_UNMAP; ··· 1345 1402 struct panthor_vm *vm) 1346 1403 { 1347 1404 memset(op_ctx, 0, sizeof(*op_ctx)); 1348 - INIT_LIST_HEAD(&op_ctx->returned_vmas); 1349 1405 op_ctx->flags = DRM_PANTHOR_VM_BIND_OP_TYPE_SYNC_ONLY; 1350 1406 } 1351 1407 ··· 1990 2048 1991 2049 mutex_lock(&bo->base.base.gpuva.lock); 1992 2050 drm_gpuva_link(&vma->base, vm_bo); 1993 - drm_WARN_ON(&vm->ptdev->base, drm_gpuvm_bo_put(vm_bo)); 1994 2051 mutex_unlock(&bo->base.base.gpuva.lock); 1995 2052 } 1996 2053 1997 - static void panthor_vma_unlink(struct panthor_vm *vm, 1998 - struct panthor_vma *vma) 2054 + static void panthor_vma_unlink(struct panthor_vma *vma) 1999 2055 { 2000 - struct panthor_gem_object *bo = to_panthor_bo(vma->base.gem.obj); 2001 - struct drm_gpuvm_bo *vm_bo = drm_gpuvm_bo_get(vma->base.vm_bo); 2002 - 2003 - mutex_lock(&bo->base.base.gpuva.lock); 2004 - drm_gpuva_unlink(&vma->base); 2005 - mutex_unlock(&bo->base.base.gpuva.lock); 2006 - 2007 - /* drm_gpuva_unlink() release the vm_bo, but we manually retained it 2008 - * when entering this function, so we can implement deferred VMA 2009 - * destruction. Re-assign it here. 2010 - */ 2011 - vma->base.vm_bo = vm_bo; 2012 - list_add_tail(&vma->node, &vm->op_ctx->returned_vmas); 2056 + drm_gpuva_unlink_defer(&vma->base); 2057 + kfree(vma); 2013 2058 } 2014 2059 2015 2060 static void panthor_vma_init(struct panthor_vma *vma, u32 flags) ··· 2030 2101 return ret; 2031 2102 } 2032 2103 2033 - /* Ref owned by the mapping now, clear the obj field so we don't release the 2034 - * pinning/obj ref behind GPUVA's back. 2035 - */ 2036 2104 drm_gpuva_map(&vm->base, &vma->base, &op->map); 2037 2105 panthor_vma_link(vm, vma, op_ctx->map.vm_bo); 2106 + 2107 + drm_gpuvm_bo_put_deferred(op_ctx->map.vm_bo); 2038 2108 op_ctx->map.vm_bo = NULL; 2109 + 2039 2110 return 0; 2040 2111 } 2041 2112 ··· 2074 2145 * owned by the old mapping which will be released when this 2075 2146 * mapping is destroyed, we need to grab a ref here. 2076 2147 */ 2077 - panthor_vma_link(vm, prev_vma, 2078 - drm_gpuvm_bo_get(op->remap.unmap->va->vm_bo)); 2148 + panthor_vma_link(vm, prev_vma, op->remap.unmap->va->vm_bo); 2079 2149 } 2080 2150 2081 2151 if (next_vma) { 2082 - panthor_vma_link(vm, next_vma, 2083 - drm_gpuvm_bo_get(op->remap.unmap->va->vm_bo)); 2152 + panthor_vma_link(vm, next_vma, op->remap.unmap->va->vm_bo); 2084 2153 } 2085 2154 2086 - panthor_vma_unlink(vm, unmap_vma); 2155 + panthor_vma_unlink(unmap_vma); 2087 2156 return 0; 2088 2157 } 2089 2158 ··· 2098 2171 return ret; 2099 2172 2100 2173 drm_gpuva_unmap(&op->unmap); 2101 - panthor_vma_unlink(vm, unmap_vma); 2174 + panthor_vma_unlink(unmap_vma); 2102 2175 return 0; 2103 2176 } 2104 2177 2105 2178 static const struct drm_gpuvm_ops panthor_gpuvm_ops = { 2106 2179 .vm_free = panthor_vm_free, 2180 + .vm_bo_free = panthor_vm_bo_free, 2107 2181 .sm_step_map = panthor_gpuva_sm_step_map, 2108 2182 .sm_step_remap = panthor_gpuva_sm_step_remap, 2109 2183 .sm_step_unmap = panthor_gpuva_sm_step_unmap,
+330
drivers/gpu/nova-core/bitfield.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! Bitfield library for Rust structures 4 + //! 5 + //! Support for defining bitfields in Rust structures. Also used by the [`register!`] macro. 6 + 7 + /// Defines a struct with accessors to access bits within an inner unsigned integer. 8 + /// 9 + /// # Syntax 10 + /// 11 + /// ```rust 12 + /// use nova_core::bitfield; 13 + /// 14 + /// #[derive(Debug, Clone, Copy, Default)] 15 + /// enum Mode { 16 + /// #[default] 17 + /// Low = 0, 18 + /// High = 1, 19 + /// Auto = 2, 20 + /// } 21 + /// 22 + /// impl TryFrom<u8> for Mode { 23 + /// type Error = u8; 24 + /// fn try_from(value: u8) -> Result<Self, Self::Error> { 25 + /// match value { 26 + /// 0 => Ok(Mode::Low), 27 + /// 1 => Ok(Mode::High), 28 + /// 2 => Ok(Mode::Auto), 29 + /// _ => Err(value), 30 + /// } 31 + /// } 32 + /// } 33 + /// 34 + /// impl From<Mode> for u8 { 35 + /// fn from(mode: Mode) -> u8 { 36 + /// mode as u8 37 + /// } 38 + /// } 39 + /// 40 + /// #[derive(Debug, Clone, Copy, Default)] 41 + /// enum State { 42 + /// #[default] 43 + /// Inactive = 0, 44 + /// Active = 1, 45 + /// } 46 + /// 47 + /// impl From<bool> for State { 48 + /// fn from(value: bool) -> Self { 49 + /// if value { State::Active } else { State::Inactive } 50 + /// } 51 + /// } 52 + /// 53 + /// impl From<State> for bool { 54 + /// fn from(state: State) -> bool { 55 + /// match state { 56 + /// State::Inactive => false, 57 + /// State::Active => true, 58 + /// } 59 + /// } 60 + /// } 61 + /// 62 + /// bitfield! { 63 + /// pub struct ControlReg(u32) { 64 + /// 7:7 state as bool => State; 65 + /// 3:0 mode as u8 ?=> Mode; 66 + /// } 67 + /// } 68 + /// ``` 69 + /// 70 + /// This generates a struct with: 71 + /// - Field accessors: `mode()`, `state()`, etc. 72 + /// - Field setters: `set_mode()`, `set_state()`, etc. (supports chaining with builder pattern). 73 + /// Note that the compiler will error out if the size of the setter's arg exceeds the 74 + /// struct's storage size. 75 + /// - Debug and Default implementations. 76 + /// 77 + /// Note: Field accessors and setters inherit the same visibility as the struct itself. 78 + /// In the example above, both `mode()` and `set_mode()` methods will be `pub`. 79 + /// 80 + /// Fields are defined as follows: 81 + /// 82 + /// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or 83 + /// `bool`. Note that `bool` fields must have a range of 1 bit. 84 + /// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns 85 + /// the result. 86 + /// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation 87 + /// and returns the result. This is useful with fields for which not all values are valid. 88 + macro_rules! bitfield { 89 + // Main entry point - defines the bitfield struct with fields 90 + ($vis:vis struct $name:ident($storage:ty) $(, $comment:literal)? { $($fields:tt)* }) => { 91 + bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* }); 92 + }; 93 + 94 + // All rules below are helpers. 95 + 96 + // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, 97 + // `Default`, and conversion to the value type) and field accessor methods. 98 + (@core $vis:vis $name:ident $storage:ty $(, $comment:literal)? { $($fields:tt)* }) => { 99 + $( 100 + #[doc=$comment] 101 + )? 102 + #[repr(transparent)] 103 + #[derive(Clone, Copy)] 104 + $vis struct $name($storage); 105 + 106 + impl ::core::convert::From<$name> for $storage { 107 + fn from(val: $name) -> $storage { 108 + val.0 109 + } 110 + } 111 + 112 + bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* }); 113 + }; 114 + 115 + // Captures the fields and passes them to all the implementers that require field information. 116 + // 117 + // Used to simplify the matching rules for implementers, so they don't need to match the entire 118 + // complex fields rule even though they only make use of part of it. 119 + (@fields_dispatcher $vis:vis $name:ident $storage:ty { 120 + $($hi:tt:$lo:tt $field:ident as $type:tt 121 + $(?=> $try_into_type:ty)? 122 + $(=> $into_type:ty)? 123 + $(, $comment:literal)? 124 + ; 125 + )* 126 + } 127 + ) => { 128 + bitfield!(@field_accessors $vis $name $storage { 129 + $( 130 + $hi:$lo $field as $type 131 + $(?=> $try_into_type)? 132 + $(=> $into_type)? 133 + $(, $comment)? 134 + ; 135 + )* 136 + }); 137 + bitfield!(@debug $name { $($field;)* }); 138 + bitfield!(@default $name { $($field;)* }); 139 + }; 140 + 141 + // Defines all the field getter/setter methods for `$name`. 142 + ( 143 + @field_accessors $vis:vis $name:ident $storage:ty { 144 + $($hi:tt:$lo:tt $field:ident as $type:tt 145 + $(?=> $try_into_type:ty)? 146 + $(=> $into_type:ty)? 147 + $(, $comment:literal)? 148 + ; 149 + )* 150 + } 151 + ) => { 152 + $( 153 + bitfield!(@check_field_bounds $hi:$lo $field as $type); 154 + )* 155 + 156 + #[allow(dead_code)] 157 + impl $name { 158 + $( 159 + bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type 160 + $(?=> $try_into_type)? 161 + $(=> $into_type)? 162 + $(, $comment)? 163 + ; 164 + ); 165 + )* 166 + } 167 + }; 168 + 169 + // Boolean fields must have `$hi == $lo`. 170 + (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => { 171 + #[allow(clippy::eq_op)] 172 + const _: () = { 173 + ::kernel::build_assert!( 174 + $hi == $lo, 175 + concat!("boolean field `", stringify!($field), "` covers more than one bit") 176 + ); 177 + }; 178 + }; 179 + 180 + // Non-boolean fields must have `$hi >= $lo`. 181 + (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => { 182 + #[allow(clippy::eq_op)] 183 + const _: () = { 184 + ::kernel::build_assert!( 185 + $hi >= $lo, 186 + concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB") 187 + ); 188 + }; 189 + }; 190 + 191 + // Catches fields defined as `bool` and convert them into a boolean value. 192 + ( 193 + @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool 194 + => $into_type:ty $(, $comment:literal)?; 195 + ) => { 196 + bitfield!( 197 + @leaf_accessor $vis $name $storage, $hi:$lo $field 198 + { |f| <$into_type>::from(f != 0) } 199 + bool $into_type => $into_type $(, $comment)?; 200 + ); 201 + }; 202 + 203 + // Shortcut for fields defined as `bool` without the `=>` syntax. 204 + ( 205 + @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool 206 + $(, $comment:literal)?; 207 + ) => { 208 + bitfield!( 209 + @field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?; 210 + ); 211 + }; 212 + 213 + // Catches the `?=>` syntax for non-boolean fields. 214 + ( 215 + @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt 216 + ?=> $try_into_type:ty $(, $comment:literal)?; 217 + ) => { 218 + bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field 219 + { |f| <$try_into_type>::try_from(f as $type) } $type $try_into_type => 220 + ::core::result::Result< 221 + $try_into_type, 222 + <$try_into_type as ::core::convert::TryFrom<$type>>::Error 223 + > 224 + $(, $comment)?;); 225 + }; 226 + 227 + // Catches the `=>` syntax for non-boolean fields. 228 + ( 229 + @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt 230 + => $into_type:ty $(, $comment:literal)?; 231 + ) => { 232 + bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field 233 + { |f| <$into_type>::from(f as $type) } $type $into_type => $into_type $(, $comment)?;); 234 + }; 235 + 236 + // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax. 237 + ( 238 + @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt 239 + $(, $comment:literal)?; 240 + ) => { 241 + bitfield!( 242 + @field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?; 243 + ); 244 + }; 245 + 246 + // Generates the accessor methods for a single field. 247 + ( 248 + @leaf_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident 249 + { $process:expr } $prim_type:tt $to_type:ty => $res_type:ty $(, $comment:literal)?; 250 + ) => { 251 + ::kernel::macros::paste!( 252 + const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi; 253 + const [<$field:upper _MASK>]: $storage = { 254 + // Generate mask for shifting 255 + match ::core::mem::size_of::<$storage>() { 256 + 1 => ::kernel::bits::genmask_u8($lo..=$hi) as $storage, 257 + 2 => ::kernel::bits::genmask_u16($lo..=$hi) as $storage, 258 + 4 => ::kernel::bits::genmask_u32($lo..=$hi) as $storage, 259 + 8 => ::kernel::bits::genmask_u64($lo..=$hi) as $storage, 260 + _ => ::kernel::build_error!("Unsupported storage type size") 261 + } 262 + }; 263 + const [<$field:upper _SHIFT>]: u32 = $lo; 264 + ); 265 + 266 + $( 267 + #[doc="Returns the value of this field:"] 268 + #[doc=$comment] 269 + )? 270 + #[inline(always)] 271 + $vis fn $field(self) -> $res_type { 272 + ::kernel::macros::paste!( 273 + const MASK: $storage = $name::[<$field:upper _MASK>]; 274 + const SHIFT: u32 = $name::[<$field:upper _SHIFT>]; 275 + ); 276 + let field = ((self.0 & MASK) >> SHIFT); 277 + 278 + $process(field) 279 + } 280 + 281 + ::kernel::macros::paste!( 282 + $( 283 + #[doc="Sets the value of this field:"] 284 + #[doc=$comment] 285 + )? 286 + #[inline(always)] 287 + $vis fn [<set_ $field>](mut self, value: $to_type) -> Self { 288 + const MASK: $storage = $name::[<$field:upper _MASK>]; 289 + const SHIFT: u32 = $name::[<$field:upper _SHIFT>]; 290 + let value = ($storage::from($prim_type::from(value)) << SHIFT) & MASK; 291 + self.0 = (self.0 & !MASK) | value; 292 + 293 + self 294 + } 295 + ); 296 + }; 297 + 298 + // Generates the `Debug` implementation for `$name`. 299 + (@debug $name:ident { $($field:ident;)* }) => { 300 + impl ::kernel::fmt::Debug for $name { 301 + fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result { 302 + f.debug_struct(stringify!($name)) 303 + .field("<raw>", &::kernel::prelude::fmt!("{:#x}", &self.0)) 304 + $( 305 + .field(stringify!($field), &self.$field()) 306 + )* 307 + .finish() 308 + } 309 + } 310 + }; 311 + 312 + // Generates the `Default` implementation for `$name`. 313 + (@default $name:ident { $($field:ident;)* }) => { 314 + /// Returns a value for the bitfield where all fields are set to their default value. 315 + impl ::core::default::Default for $name { 316 + fn default() -> Self { 317 + #[allow(unused_mut)] 318 + let mut value = Self(Default::default()); 319 + 320 + ::kernel::macros::paste!( 321 + $( 322 + value.[<set_ $field>](Default::default()); 323 + )* 324 + ); 325 + 326 + value 327 + } 328 + } 329 + }; 330 + }
+15 -19
drivers/gpu/nova-core/dma.rs
··· 2 2 3 3 //! Simple DMA object wrapper. 4 4 5 - use core::ops::{Deref, DerefMut}; 5 + use core::ops::{ 6 + Deref, 7 + DerefMut, // 8 + }; 6 9 7 - use kernel::device; 8 - use kernel::dma::CoherentAllocation; 9 - use kernel::page::PAGE_SIZE; 10 - use kernel::prelude::*; 10 + use kernel::{ 11 + device, 12 + dma::CoherentAllocation, 13 + page::PAGE_SIZE, 14 + prelude::*, // 15 + }; 11 16 12 17 pub(crate) struct DmaObject { 13 18 dma: CoherentAllocation<u8>, ··· 30 25 } 31 26 32 27 pub(crate) fn from_data(dev: &device::Device<device::Bound>, data: &[u8]) -> Result<Self> { 33 - Self::new(dev, data.len()).map(|mut dma_obj| { 34 - // TODO[COHA]: replace with `CoherentAllocation::write()` once available. 35 - // SAFETY: 36 - // - `dma_obj`'s size is at least `data.len()`. 37 - // - We have just created this object and there is no other user at this stage. 38 - unsafe { 39 - core::ptr::copy_nonoverlapping( 40 - data.as_ptr(), 41 - dma_obj.dma.start_ptr_mut(), 42 - data.len(), 43 - ); 44 - } 45 - 46 - dma_obj 28 + Self::new(dev, data.len()).and_then(|mut dma_obj| { 29 + // SAFETY: We have just allocated the DMA memory, we are the only users and 30 + // we haven't made the device aware of the handle yet. 31 + unsafe { dma_obj.write(data, 0)? } 32 + Ok(dma_obj) 47 33 }) 48 34 } 49 35 }
+24 -3
drivers/gpu/nova-core/driver.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 3 use kernel::{ 4 - auxiliary, c_str, 4 + auxiliary, 5 + c_str, 5 6 device::Core, 7 + dma::Device, 8 + dma::DmaMask, 6 9 pci, 7 - pci::{Class, ClassMask, Vendor}, 10 + pci::{ 11 + Class, 12 + ClassMask, 13 + Vendor, // 14 + }, 8 15 prelude::*, 9 16 sizes::SZ_16M, 10 - sync::Arc, 17 + sync::Arc, // 11 18 }; 12 19 13 20 use crate::gpu::Gpu; ··· 27 20 } 28 21 29 22 const BAR0_SIZE: usize = SZ_16M; 23 + 24 + // For now we only support Ampere which can use up to 47-bit DMA addresses. 25 + // 26 + // TODO: Add an abstraction for this to support newer GPUs which may support 27 + // larger DMA addresses. Limiting these GPUs to smaller address widths won't 28 + // have any adverse affects, unless installed on systems which require larger 29 + // DMA addresses. These systems should be quite rare. 30 + const GPU_DMA_BITS: u32 = 47; 31 + 30 32 pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>; 31 33 32 34 kernel::pci_device_table!( ··· 72 56 73 57 pdev.enable_device_mem()?; 74 58 pdev.set_master(); 59 + 60 + // SAFETY: No concurrent DMA allocations or mappings can be made because 61 + // the device is still being probed and therefore isn't being used by 62 + // other threads of execution. 63 + unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? }; 75 64 76 65 let devres_bar = Arc::pin_init( 77 66 pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
+169 -118
drivers/gpu/nova-core/falcon.rs
··· 3 3 //! Falcon microprocessor base support 4 4 5 5 use core::ops::Deref; 6 - use hal::FalconHal; 7 - use kernel::device; 8 - use kernel::dma::DmaAddress; 9 - use kernel::prelude::*; 10 - use kernel::sync::aref::ARef; 11 - use kernel::time::Delta; 12 6 13 - use crate::dma::DmaObject; 14 - use crate::driver::Bar0; 15 - use crate::gpu::Chipset; 16 - use crate::regs; 17 - use crate::regs::macros::RegisterBase; 18 - use crate::util; 7 + use hal::FalconHal; 8 + 9 + use kernel::{ 10 + device, 11 + dma::DmaAddress, 12 + io::poll::read_poll_timeout, 13 + prelude::*, 14 + sync::aref::ARef, 15 + time::{ 16 + delay::fsleep, 17 + Delta, // 18 + }, 19 + }; 20 + 21 + use crate::{ 22 + dma::DmaObject, 23 + driver::Bar0, 24 + gpu::Chipset, 25 + num::{ 26 + FromSafeCast, 27 + IntoSafeCast, // 28 + }, 29 + regs, 30 + regs::macros::RegisterBase, // 31 + }; 19 32 20 33 pub(crate) mod gsp; 21 34 mod hal; 22 35 pub(crate) mod sec2; 23 36 24 37 // TODO[FPRI]: Replace with `ToPrimitive`. 25 - macro_rules! impl_from_enum_to_u32 { 38 + macro_rules! impl_from_enum_to_u8 { 26 39 ($enum_type:ty) => { 27 - impl From<$enum_type> for u32 { 40 + impl From<$enum_type> for u8 { 28 41 fn from(value: $enum_type) -> Self { 29 - value as u32 42 + value as u8 30 43 } 31 44 } 32 45 }; ··· 59 46 Rev6 = 6, 60 47 Rev7 = 7, 61 48 } 62 - impl_from_enum_to_u32!(FalconCoreRev); 49 + impl_from_enum_to_u8!(FalconCoreRev); 63 50 64 51 // TODO[FPRI]: replace with `FromPrimitive`. 65 52 impl TryFrom<u8> for FalconCoreRev { ··· 94 81 Subversion2 = 2, 95 82 Subversion3 = 3, 96 83 } 97 - impl_from_enum_to_u32!(FalconCoreRevSubversion); 84 + impl_from_enum_to_u8!(FalconCoreRevSubversion); 98 85 99 86 // TODO[FPRI]: replace with `FromPrimitive`. 100 87 impl TryFrom<u8> for FalconCoreRevSubversion { ··· 138 125 /// Also known as High-Secure, Privilege Level 3 or PL3. 139 126 Heavy = 3, 140 127 } 141 - impl_from_enum_to_u32!(FalconSecurityModel); 128 + impl_from_enum_to_u8!(FalconSecurityModel); 142 129 143 130 // TODO[FPRI]: replace with `FromPrimitive`. 144 131 impl TryFrom<u8> for FalconSecurityModel { ··· 170 157 #[default] 171 158 Rsa3k = 1, 172 159 } 173 - impl_from_enum_to_u32!(FalconModSelAlgo); 160 + impl_from_enum_to_u8!(FalconModSelAlgo); 174 161 175 162 // TODO[FPRI]: replace with `FromPrimitive`. 176 163 impl TryFrom<u8> for FalconModSelAlgo { ··· 192 179 #[default] 193 180 Size256B = 0x6, 194 181 } 195 - impl_from_enum_to_u32!(DmaTrfCmdSize); 182 + impl_from_enum_to_u8!(DmaTrfCmdSize); 196 183 197 184 // TODO[FPRI]: replace with `FromPrimitive`. 198 185 impl TryFrom<u8> for DmaTrfCmdSize { ··· 215 202 /// RISC-V core is active. 216 203 Riscv = 1, 217 204 } 218 - impl_from_enum_to_u32!(PeregrineCoreSelect); 219 205 220 206 impl From<bool> for PeregrineCoreSelect { 221 207 fn from(value: bool) -> Self { 222 208 match value { 223 209 false => PeregrineCoreSelect::Falcon, 224 210 true => PeregrineCoreSelect::Riscv, 211 + } 212 + } 213 + } 214 + 215 + impl From<PeregrineCoreSelect> for bool { 216 + fn from(value: PeregrineCoreSelect) -> Self { 217 + match value { 218 + PeregrineCoreSelect::Falcon => false, 219 + PeregrineCoreSelect::Riscv => true, 225 220 } 226 221 } 227 222 } ··· 257 236 /// Non-coherent system memory (System DRAM). 258 237 NoncoherentSysmem = 2, 259 238 } 260 - impl_from_enum_to_u32!(FalconFbifTarget); 239 + impl_from_enum_to_u8!(FalconFbifTarget); 261 240 262 241 // TODO[FPRI]: replace with `FromPrimitive`. 263 242 impl TryFrom<u8> for FalconFbifTarget { ··· 284 263 /// Physical memory addresses. 285 264 Physical = 1, 286 265 } 287 - impl_from_enum_to_u32!(FalconFbifMemType); 288 266 289 267 /// Conversion from a single-bit register field. 290 268 impl From<bool> for FalconFbifMemType { ··· 291 271 match value { 292 272 false => Self::Virtual, 293 273 true => Self::Physical, 274 + } 275 + } 276 + } 277 + 278 + impl From<FalconFbifMemType> for bool { 279 + fn from(value: FalconFbifMemType) -> Self { 280 + match value { 281 + FalconFbifMemType::Virtual => false, 282 + FalconFbifMemType::Physical => true, 294 283 } 295 284 } 296 285 } ··· 375 346 376 347 impl<E: FalconEngine + 'static> Falcon<E> { 377 348 /// Create a new falcon instance. 378 - /// 379 - /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv 380 - /// controller. 381 - pub(crate) fn new( 382 - dev: &device::Device, 383 - chipset: Chipset, 384 - bar: &Bar0, 385 - need_riscv: bool, 386 - ) -> Result<Self> { 387 - let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, &E::ID); 388 - // Check that the revision and security model contain valid values. 389 - let _ = hwcfg1.core_rev()?; 390 - let _ = hwcfg1.security_model()?; 391 - 392 - if need_riscv { 393 - let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); 394 - if !hwcfg2.riscv() { 395 - dev_err!( 396 - dev, 397 - "riscv support requested on a controller that does not support it\n" 398 - ); 399 - return Err(EINVAL); 400 - } 401 - } 402 - 349 + pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result<Self> { 403 350 Ok(Self { 404 351 hal: hal::falcon_hal(chipset)?, 405 352 dev: dev.into(), 406 353 }) 407 354 } 408 355 356 + /// Resets DMA-related registers. 357 + pub(crate) fn dma_reset(&self, bar: &Bar0) { 358 + regs::NV_PFALCON_FBIF_CTL::update(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true)); 359 + regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); 360 + } 361 + 409 362 /// Wait for memory scrubbing to complete. 410 363 fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { 411 364 // TIMEOUT: memory scrubbing should complete in less than 20ms. 412 - util::wait_on(Delta::from_millis(20), || { 413 - if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID).mem_scrubbing_done() { 414 - Some(()) 415 - } else { 416 - None 417 - } 418 - }) 365 + read_poll_timeout( 366 + || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), 367 + |r| r.mem_scrubbing_done(), 368 + Delta::ZERO, 369 + Delta::from_millis(20), 370 + ) 371 + .map(|_| ()) 419 372 } 420 373 421 374 /// Reset the falcon engine. ··· 406 395 407 396 // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set 408 397 // RESET_READY so a non-failing timeout is used. 409 - let _ = util::wait_on(Delta::from_micros(150), || { 410 - let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); 411 - if r.reset_ready() { 412 - Some(()) 413 - } else { 414 - None 415 - } 416 - }); 398 + let _ = read_poll_timeout( 399 + || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), 400 + |r| r.reset_ready(), 401 + Delta::ZERO, 402 + Delta::from_micros(150), 403 + ); 417 404 418 - regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(true)); 405 + regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true)); 419 406 420 - // TODO[DLAY]: replace with udelay() or equivalent once available. 421 407 // TIMEOUT: falcon engine should not take more than 10us to reset. 422 - let _: Result = util::wait_on(Delta::from_micros(10), || None); 408 + fsleep(Delta::from_micros(10)); 423 409 424 - regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(false)); 410 + regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false)); 425 411 426 412 self.reset_wait_mem_scrubbing(bar)?; 427 413 ··· 460 452 FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()), 461 453 FalconMem::Dmem => ( 462 454 0, 463 - fw.dma_handle_with_offset(load_offsets.src_start as usize)?, 455 + fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, 464 456 ), 465 457 }; 466 458 if dma_start % DmaAddress::from(DMA_LEN) > 0 { ··· 486 478 dev_err!(self.dev, "DMA transfer length overflow"); 487 479 return Err(EOVERFLOW); 488 480 } 489 - Some(upper_bound) if upper_bound as usize > fw.size() => { 481 + Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => { 490 482 dev_err!(self.dev, "DMA transfer goes beyond range of DMA object"); 491 483 return Err(EINVAL); 492 484 } ··· 496 488 // Set up the base source DMA address. 497 489 498 490 regs::NV_PFALCON_FALCON_DMATRFBASE::default() 491 + // CAST: `as u32` is used on purpose since we do want to strip the upper bits, which 492 + // will be written to `NV_PFALCON_FALCON_DMATRFBASE1`. 499 493 .set_base((dma_start >> 8) as u32) 500 494 .write(bar, &E::ID); 501 495 regs::NV_PFALCON_FALCON_DMATRFBASE1::default() 496 + // CAST: `as u16` is used on purpose since the remaining bits are guaranteed to fit 497 + // within a `u16`. 502 498 .set_base((dma_start >> 40) as u16) 503 499 .write(bar, &E::ID); 504 500 ··· 524 512 // Wait for the transfer to complete. 525 513 // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories 526 514 // should ever take that long. 527 - util::wait_on(Delta::from_secs(2), || { 528 - let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID); 529 - if r.idle() { 530 - Some(()) 531 - } else { 532 - None 533 - } 534 - })?; 515 + read_poll_timeout( 516 + || Ok(regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID)), 517 + |r| r.idle(), 518 + Delta::ZERO, 519 + Delta::from_secs(2), 520 + )?; 535 521 } 536 522 537 523 Ok(()) ··· 537 527 538 528 /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. 539 529 pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { 540 - regs::NV_PFALCON_FBIF_CTL::alter(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true)); 541 - regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); 542 - regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, &E::ID, 0, |v| { 530 + self.dma_reset(bar); 531 + regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| { 543 532 v.set_target(FalconFbifTarget::CoherentSysmem) 544 533 .set_mem_type(FalconFbifMemType::Physical) 545 534 }); ··· 556 547 Ok(()) 557 548 } 558 549 559 - /// Runs the loaded firmware and waits for its completion. 550 + /// Wait until the falcon CPU is halted. 551 + pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> { 552 + // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. 553 + read_poll_timeout( 554 + || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)), 555 + |r| r.halted(), 556 + Delta::ZERO, 557 + Delta::from_secs(2), 558 + )?; 559 + 560 + Ok(()) 561 + } 562 + 563 + /// Start the falcon CPU. 564 + pub(crate) fn start(&self, bar: &Bar0) -> Result<()> { 565 + match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() { 566 + true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default() 567 + .set_startcpu(true) 568 + .write(bar, &E::ID), 569 + false => regs::NV_PFALCON_FALCON_CPUCTL::default() 570 + .set_startcpu(true) 571 + .write(bar, &E::ID), 572 + } 573 + 574 + Ok(()) 575 + } 576 + 577 + /// Writes values to the mailbox registers if provided. 578 + pub(crate) fn write_mailboxes(&self, bar: &Bar0, mbox0: Option<u32>, mbox1: Option<u32>) { 579 + if let Some(mbox0) = mbox0 { 580 + regs::NV_PFALCON_FALCON_MAILBOX0::default() 581 + .set_value(mbox0) 582 + .write(bar, &E::ID); 583 + } 584 + 585 + if let Some(mbox1) = mbox1 { 586 + regs::NV_PFALCON_FALCON_MAILBOX1::default() 587 + .set_value(mbox1) 588 + .write(bar, &E::ID); 589 + } 590 + } 591 + 592 + /// Reads the value from `mbox0` register. 593 + pub(crate) fn read_mailbox0(&self, bar: &Bar0) -> u32 { 594 + regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value() 595 + } 596 + 597 + /// Reads the value from `mbox1` register. 598 + pub(crate) fn read_mailbox1(&self, bar: &Bar0) -> u32 { 599 + regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value() 600 + } 601 + 602 + /// Reads values from both mailbox registers. 603 + pub(crate) fn read_mailboxes(&self, bar: &Bar0) -> (u32, u32) { 604 + let mbox0 = self.read_mailbox0(bar); 605 + let mbox1 = self.read_mailbox1(bar); 606 + 607 + (mbox0, mbox1) 608 + } 609 + 610 + /// Start running the loaded firmware. 560 611 /// 561 612 /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers 562 613 /// prior to running. ··· 629 560 mbox0: Option<u32>, 630 561 mbox1: Option<u32>, 631 562 ) -> Result<(u32, u32)> { 632 - if let Some(mbox0) = mbox0 { 633 - regs::NV_PFALCON_FALCON_MAILBOX0::default() 634 - .set_value(mbox0) 635 - .write(bar, &E::ID); 636 - } 637 - 638 - if let Some(mbox1) = mbox1 { 639 - regs::NV_PFALCON_FALCON_MAILBOX1::default() 640 - .set_value(mbox1) 641 - .write(bar, &E::ID); 642 - } 643 - 644 - match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() { 645 - true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default() 646 - .set_startcpu(true) 647 - .write(bar, &E::ID), 648 - false => regs::NV_PFALCON_FALCON_CPUCTL::default() 649 - .set_startcpu(true) 650 - .write(bar, &E::ID), 651 - } 652 - 653 - // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds. 654 - util::wait_on(Delta::from_secs(2), || { 655 - let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID); 656 - if r.halted() { 657 - Some(()) 658 - } else { 659 - None 660 - } 661 - })?; 662 - 663 - let (mbox0, mbox1) = ( 664 - regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(), 665 - regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value(), 666 - ); 667 - 668 - Ok((mbox0, mbox1)) 563 + self.write_mailboxes(bar, mbox0, mbox1); 564 + self.start(bar)?; 565 + self.wait_till_halted(bar)?; 566 + Ok(self.read_mailboxes(bar)) 669 567 } 670 568 671 569 /// Returns the fused version of the signature to use in order to run a HS firmware on this ··· 645 609 ) -> Result<u32> { 646 610 self.hal 647 611 .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id) 612 + } 613 + 614 + /// Check if the RISC-V core is active. 615 + /// 616 + /// Returns `true` if the RISC-V core is active, `false` otherwise. 617 + pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { 618 + let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID); 619 + cpuctl.active_stat() 620 + } 621 + 622 + /// Write the application version to the OS register. 623 + pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) { 624 + regs::NV_PFALCON_FALCON_OS::default() 625 + .set_value(app_version) 626 + .write(bar, &E::ID); 648 627 } 649 628 }
+27 -2
drivers/gpu/nova-core/falcon/gsp.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 + use kernel::{ 4 + io::poll::read_poll_timeout, 5 + prelude::*, 6 + time::Delta, // 7 + }; 8 + 3 9 use crate::{ 4 10 driver::Bar0, 5 - falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase}, 6 - regs::{self, macros::RegisterBase}, 11 + falcon::{ 12 + Falcon, 13 + FalconEngine, 14 + PFalcon2Base, 15 + PFalconBase, // 16 + }, 17 + regs::{ 18 + self, 19 + macros::RegisterBase, // 20 + }, 7 21 }; 8 22 9 23 /// Type specifying the `Gsp` falcon engine. Cannot be instantiated. ··· 42 28 regs::NV_PFALCON_FALCON_IRQSCLR::default() 43 29 .set_swgen0(true) 44 30 .write(bar, &Gsp::ID); 31 + } 32 + 33 + /// Checks if GSP reload/resume has completed during the boot process. 34 + pub(crate) fn check_reload_completed(&self, bar: &Bar0, timeout: Delta) -> Result<bool> { 35 + read_poll_timeout( 36 + || Ok(regs::NV_PGC6_BSI_SECURE_SCRATCH_14::read(bar)), 37 + |val| val.boot_stage_3_handoff(), 38 + Delta::ZERO, 39 + timeout, 40 + ) 41 + .map(|_| true) 45 42 } 46 43 }
+10 -4
drivers/gpu/nova-core/falcon/hal.rs
··· 2 2 3 3 use kernel::prelude::*; 4 4 5 - use crate::driver::Bar0; 6 - use crate::falcon::{Falcon, FalconBromParams, FalconEngine}; 7 - use crate::gpu::Chipset; 5 + use crate::{ 6 + driver::Bar0, 7 + falcon::{ 8 + Falcon, 9 + FalconBromParams, 10 + FalconEngine, // 11 + }, 12 + gpu::Chipset, 13 + }; 8 14 9 15 mod ga102; 10 16 ··· 50 44 use Chipset::*; 51 45 52 46 let hal = match chipset { 53 - GA102 | GA103 | GA104 | GA106 | GA107 => { 47 + GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => { 54 48 KBox::new(ga102::Ga102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>> 55 49 } 56 50 _ => return Err(ENOTSUPP),
+25 -21
drivers/gpu/nova-core/falcon/hal/ga102.rs
··· 2 2 3 3 use core::marker::PhantomData; 4 4 5 - use kernel::device; 6 - use kernel::prelude::*; 7 - use kernel::time::Delta; 8 - 9 - use crate::driver::Bar0; 10 - use crate::falcon::{ 11 - Falcon, FalconBromParams, FalconEngine, FalconModSelAlgo, PeregrineCoreSelect, 5 + use kernel::{ 6 + device, 7 + io::poll::read_poll_timeout, 8 + prelude::*, 9 + time::Delta, // 12 10 }; 13 - use crate::regs; 14 - use crate::util; 11 + 12 + use crate::{ 13 + driver::Bar0, 14 + falcon::{ 15 + Falcon, 16 + FalconBromParams, 17 + FalconEngine, 18 + FalconModSelAlgo, 19 + PeregrineCoreSelect, // 20 + }, 21 + regs, 22 + }; 15 23 16 24 use super::FalconHal; 17 25 ··· 31 23 .write(bar, &E::ID); 32 24 33 25 // TIMEOUT: falcon core should take less than 10ms to report being enabled. 34 - util::wait_on(Delta::from_millis(10), || { 35 - let r = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID); 36 - if r.valid() { 37 - Some(()) 38 - } else { 39 - None 40 - } 41 - })?; 26 + read_poll_timeout( 27 + || Ok(regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID)), 28 + |r| r.valid(), 29 + Delta::ZERO, 30 + Delta::from_millis(10), 31 + )?; 42 32 } 43 33 44 34 Ok(()) ··· 48 42 engine_id_mask: u16, 49 43 ucode_id: u8, 50 44 ) -> Result<u32> { 51 - const NV_FUSE_OPT_FPF_SIZE: u8 = regs::NV_FUSE_OPT_FPF_SIZE as u8; 52 - 53 45 // Each engine has 16 ucode version registers numbered from 1 to 16. 54 - let ucode_idx = match ucode_id { 55 - 1..=NV_FUSE_OPT_FPF_SIZE => (ucode_id - 1) as usize, 46 + let ucode_idx = match usize::from(ucode_id) { 47 + ucode_id @ 1..=regs::NV_FUSE_OPT_FPF_SIZE => ucode_id - 1, 56 48 _ => { 57 49 dev_err!(dev, "invalid ucode id {:#x}", ucode_id); 58 50 return Err(EINVAL);
+8 -2
drivers/gpu/nova-core/falcon/sec2.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase}; 4 - use crate::regs::macros::RegisterBase; 3 + use crate::{ 4 + falcon::{ 5 + FalconEngine, 6 + PFalcon2Base, 7 + PFalconBase, // 8 + }, 9 + regs::macros::RegisterBase, 10 + }; 5 11 6 12 /// Type specifying the `Sec2` falcon engine. Cannot be instantiated. 7 13 pub(crate) struct Sec2(());
+85 -15
drivers/gpu/nova-core/fb.rs
··· 2 2 3 3 use core::ops::Range; 4 4 5 - use kernel::prelude::*; 6 - use kernel::ptr::{Alignable, Alignment}; 7 - use kernel::sizes::*; 8 - use kernel::sync::aref::ARef; 9 - use kernel::{dev_warn, device}; 5 + use kernel::{ 6 + device, 7 + prelude::*, 8 + ptr::{ 9 + Alignable, 10 + Alignment, // 11 + }, 12 + sizes::*, 13 + sync::aref::ARef, // 14 + }; 10 15 11 - use crate::dma::DmaObject; 12 - use crate::driver::Bar0; 13 - use crate::gpu::Chipset; 14 - use crate::regs; 16 + use crate::{ 17 + dma::DmaObject, 18 + driver::Bar0, 19 + firmware::gsp::GspFirmware, 20 + gpu::Chipset, 21 + gsp, 22 + num::{ 23 + usize_as_u64, 24 + FromSafeCast, // 25 + }, 26 + regs, 27 + }; 15 28 16 29 mod hal; 17 30 ··· 98 85 /// 99 86 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 100 87 #[derive(Debug)] 101 - #[expect(dead_code)] 102 88 pub(crate) struct FbLayout { 89 + /// Range of the framebuffer. Starts at `0`. 103 90 pub(crate) fb: Range<u64>, 91 + /// VGA workspace, small area of reserved memory at the end of the framebuffer. 104 92 pub(crate) vga_workspace: Range<u64>, 93 + /// FRTS range. 105 94 pub(crate) frts: Range<u64>, 95 + /// Memory area containing the GSP bootloader image. 96 + pub(crate) boot: Range<u64>, 97 + /// Memory area containing the GSP firmware image. 98 + pub(crate) elf: Range<u64>, 99 + /// WPR2 heap. 100 + pub(crate) wpr2_heap: Range<u64>, 101 + /// WPR2 region range, starting with an instance of `GspFwWprMeta`. 102 + pub(crate) wpr2: Range<u64>, 103 + pub(crate) heap: Range<u64>, 104 + pub(crate) vf_partition_count: u8, 106 105 } 107 106 108 107 impl FbLayout { 109 - /// Computes the FB layout. 110 - pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> { 108 + /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware. 109 + pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> { 111 110 let hal = hal::fb_hal(chipset); 112 111 113 112 let fb = { ··· 130 105 131 106 let vga_workspace = { 132 107 let vga_base = { 133 - const NV_PRAMIN_SIZE: u64 = SZ_1M as u64; 108 + const NV_PRAMIN_SIZE: u64 = usize_as_u64(SZ_1M); 134 109 let base = fb.end - NV_PRAMIN_SIZE; 135 110 136 111 if hal.supports_display(bar) { 137 112 match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { 138 113 Some(addr) => { 139 114 if addr < base { 140 - const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64; 115 + const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K); 141 116 142 117 // Point workspace address to end of framebuffer. 143 118 fb.end - VBIOS_WORKSPACE_SIZE ··· 157 132 158 133 let frts = { 159 134 const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>(); 160 - const FRTS_SIZE: u64 = SZ_1M as u64; 135 + const FRTS_SIZE: u64 = usize_as_u64(SZ_1M); 161 136 let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE; 162 137 163 138 frts_base..frts_base + FRTS_SIZE 139 + }; 140 + 141 + let boot = { 142 + const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>(); 143 + let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size()); 144 + let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN); 145 + 146 + bootloader_base..bootloader_base + bootloader_size 147 + }; 148 + 149 + let elf = { 150 + const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>(); 151 + let elf_size = u64::from_safe_cast(gsp_fw.size); 152 + let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN); 153 + 154 + elf_addr..elf_addr + elf_size 155 + }; 156 + 157 + let wpr2_heap = { 158 + const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 159 + let wpr2_heap_size = 160 + gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end); 161 + let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN); 162 + 163 + wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN) 164 + }; 165 + 166 + let wpr2 = { 167 + const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>(); 168 + let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>())) 169 + .align_down(WPR2_DOWN_ALIGN); 170 + 171 + wpr2_addr..frts.end 172 + }; 173 + 174 + let heap = { 175 + const HEAP_SIZE: u64 = usize_as_u64(SZ_1M); 176 + 177 + wpr2.start - HEAP_SIZE..wpr2.start 164 178 }; 165 179 166 180 Ok(Self { 167 181 fb, 168 182 vga_workspace, 169 183 frts, 184 + boot, 185 + elf, 186 + wpr2_heap, 187 + wpr2, 188 + heap, 189 + vf_partition_count: 0, 170 190 }) 171 191 } 172 192 }
+4 -2
drivers/gpu/nova-core/fb/hal.rs
··· 2 2 3 3 use kernel::prelude::*; 4 4 5 - use crate::driver::Bar0; 6 - use crate::gpu::Chipset; 5 + use crate::{ 6 + driver::Bar0, 7 + gpu::Chipset, // 8 + }; 7 9 8 10 mod ga100; 9 11 mod ga102;
+11 -5
drivers/gpu/nova-core/fb/hal/ga100.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - struct Ga100; 4 - 5 3 use kernel::prelude::*; 6 4 7 - use crate::driver::Bar0; 8 - use crate::fb::hal::FbHal; 9 - use crate::regs; 5 + use crate::{ 6 + driver::Bar0, 7 + fb::hal::FbHal, 8 + regs, // 9 + }; 10 10 11 11 use super::tu102::FLUSH_SYSMEM_ADDR_SHIFT; 12 + 13 + struct Ga100; 12 14 13 15 pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) -> u64 { 14 16 u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT ··· 20 18 21 19 pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) { 22 20 regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::default() 21 + // CAST: `as u32` is used on purpose since the remaining bits are guaranteed to fit within 22 + // a `u32`. 23 23 .set_adr_63_40((addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI) as u32) 24 24 .write(bar); 25 25 regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default() 26 + // CAST: `as u32` is used on purpose since we want to strip the upper bits that have been 27 + // written to `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`. 26 28 .set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32) 27 29 .write(bar); 28 30 }
+5 -3
drivers/gpu/nova-core/fb/hal/ga102.rs
··· 2 2 3 3 use kernel::prelude::*; 4 4 5 - use crate::driver::Bar0; 6 - use crate::fb::hal::FbHal; 7 - use crate::regs; 5 + use crate::{ 6 + driver::Bar0, 7 + fb::hal::FbHal, 8 + regs, // 9 + }; 8 10 9 11 fn vidmem_size_ga102(bar: &Bar0) -> u64 { 10 12 regs::NV_USABLE_FB_SIZE_IN_MB::read(bar).usable_fb_size()
+13 -12
drivers/gpu/nova-core/fb/hal/tu102.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - use crate::driver::Bar0; 4 - use crate::fb::hal::FbHal; 5 - use crate::regs; 6 3 use kernel::prelude::*; 4 + 5 + use crate::{ 6 + driver::Bar0, 7 + fb::hal::FbHal, 8 + regs, // 9 + }; 7 10 8 11 /// Shift applied to the sysmem address before it is written into `NV_PFB_NISO_FLUSH_SYSMEM_ADDR`, 9 12 /// to be used by HALs. ··· 18 15 19 16 pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result { 20 17 // Check that the address doesn't overflow the receiving 32-bit register. 21 - if addr >> (u32::BITS + FLUSH_SYSMEM_ADDR_SHIFT) == 0 { 22 - regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default() 23 - .set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32) 24 - .write(bar); 25 - 26 - Ok(()) 27 - } else { 28 - Err(EINVAL) 29 - } 18 + u32::try_from(addr >> FLUSH_SYSMEM_ADDR_SHIFT) 19 + .map_err(|_| EINVAL) 20 + .map(|addr| { 21 + regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default() 22 + .set_adr_39_08(addr) 23 + .write(bar) 24 + }) 30 25 } 31 26 32 27 pub(super) fn display_enabled_gm107(bar: &Bar0) -> bool {
+19 -12
drivers/gpu/nova-core/firmware.rs
··· 4 4 //! to be loaded into a given execution unit. 5 5 6 6 use core::marker::PhantomData; 7 - use core::mem::size_of; 8 7 9 - use kernel::device; 10 - use kernel::firmware; 11 - use kernel::prelude::*; 12 - use kernel::str::CString; 13 - use kernel::transmute::FromBytes; 8 + use kernel::{ 9 + device, 10 + firmware, 11 + prelude::*, 12 + str::CString, 13 + transmute::FromBytes, // 14 + }; 14 15 15 - use crate::dma::DmaObject; 16 - use crate::falcon::FalconFirmware; 17 - use crate::gpu; 16 + use crate::{ 17 + dma::DmaObject, 18 + falcon::FalconFirmware, 19 + gpu, 20 + num::{ 21 + FromSafeCast, 22 + IntoSafeCast, // 23 + }, 24 + }; 18 25 19 26 pub(crate) mod booter; 20 27 pub(crate) mod fwsec; ··· 82 75 const HDR_SIZE_SHIFT: u32 = 16; 83 76 const HDR_SIZE_MASK: u32 = 0xffff0000; 84 77 85 - ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT) as usize 78 + ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast() 86 79 } 87 80 } 88 81 ··· 197 190 /// Returns the data payload of the firmware, or `None` if the data range is out of bounds of 198 191 /// the firmware image. 199 192 fn data(&self) -> Option<&[u8]> { 200 - let fw_start = self.hdr.data_offset as usize; 201 - let fw_size = self.hdr.data_size as usize; 193 + let fw_start = usize::from_safe_cast(self.hdr.data_offset); 194 + let fw_size = usize::from_safe_cast(self.hdr.data_size); 202 195 203 196 self.fw.get(fw_start..fw_start + fw_size) 204 197 }
+52 -26
drivers/gpu/nova-core/firmware/booter.rs
··· 4 4 //! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon 5 5 //! (and optionally unload it through a separate firmware image). 6 6 7 - use core::marker::PhantomData; 8 - use core::mem::size_of; 9 - use core::ops::Deref; 7 + use core::{ 8 + marker::PhantomData, 9 + ops::Deref, // 10 + }; 10 11 11 - use kernel::device; 12 - use kernel::prelude::*; 13 - use kernel::transmute::FromBytes; 12 + use kernel::{ 13 + device, 14 + prelude::*, 15 + transmute::FromBytes, // 16 + }; 14 17 15 - use crate::dma::DmaObject; 16 - use crate::driver::Bar0; 17 - use crate::falcon::sec2::Sec2; 18 - use crate::falcon::{Falcon, FalconBromParams, FalconFirmware, FalconLoadParams, FalconLoadTarget}; 19 - use crate::firmware::{BinFirmware, FirmwareDmaObject, FirmwareSignature, Signed, Unsigned}; 20 - use crate::gpu::Chipset; 18 + use crate::{ 19 + dma::DmaObject, 20 + driver::Bar0, 21 + falcon::{ 22 + sec2::Sec2, 23 + Falcon, 24 + FalconBromParams, 25 + FalconFirmware, 26 + FalconLoadParams, 27 + FalconLoadTarget, // 28 + }, 29 + firmware::{ 30 + BinFirmware, 31 + FirmwareDmaObject, 32 + FirmwareSignature, 33 + Signed, 34 + Unsigned, // 35 + }, 36 + gpu::Chipset, 37 + num::{ 38 + FromSafeCast, 39 + IntoSafeCast, // 40 + }, 41 + }; 21 42 22 43 /// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at 23 44 /// `offset` in `slice`. ··· 95 74 /// 96 75 /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image. 97 76 fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> { 98 - frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset as usize) 77 + frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset.into_safe_cast()) 99 78 .map(|hdr| Self { hdr, fw: bin_fw.fw }) 100 79 } 101 80 ··· 104 83 /// Fails if the offset of the patch location is outside the bounds of the firmware 105 84 /// image. 106 85 fn patch_location(&self) -> Result<u32> { 107 - frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset as usize) 86 + frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset.into_safe_cast()) 108 87 } 109 88 110 89 /// Returns an iterator to the signatures of the firmware. The iterator can be empty if the ··· 112 91 /// 113 92 /// Fails if the pointed signatures are outside the bounds of the firmware image. 114 93 fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> { 115 - let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset as usize)?; 94 + let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset.into_safe_cast())?; 116 95 let iter = match self.hdr.sig_prod_size.checked_div(num_sig) { 117 96 // If there are no signatures, return an iterator that will yield zero elements. 118 97 None => (&[] as &[u8]).chunks_exact(1), 119 98 Some(sig_size) => { 120 - let patch_sig = frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset as usize)?; 121 - let signatures_start = (self.hdr.sig_prod_offset + patch_sig) as usize; 99 + let patch_sig = 100 + frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?; 101 + let signatures_start = usize::from_safe_cast(self.hdr.sig_prod_offset + patch_sig); 122 102 123 103 self.fw 124 104 // Get signatures range. 125 - .get(signatures_start..signatures_start + self.hdr.sig_prod_size as usize) 105 + .get( 106 + signatures_start 107 + ..signatures_start + usize::from_safe_cast(self.hdr.sig_prod_size), 108 + ) 126 109 .ok_or(EINVAL)? 127 - .chunks_exact(sig_size as usize) 110 + .chunks_exact(sig_size.into_safe_cast()) 128 111 } 129 112 }; 130 113 ··· 157 132 /// Fails if the meta data parameter of `hs_fw` is outside the bounds of the firmware image, or 158 133 /// if its size doesn't match that of [`HsSignatureParams`]. 159 134 fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> { 160 - let start = hs_fw.hdr.meta_data_offset as usize; 135 + let start = usize::from_safe_cast(hs_fw.hdr.meta_data_offset); 161 136 let end = start 162 - .checked_add(hs_fw.hdr.meta_data_size as usize) 137 + .checked_add(hs_fw.hdr.meta_data_size.into_safe_cast()) 163 138 .ok_or(EINVAL)?; 164 139 165 140 hs_fw ··· 194 169 /// 195 170 /// Fails if the header pointed at by `hs_fw` is not within the bounds of the firmware image. 196 171 fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> { 197 - frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset as usize) 172 + frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset.into_safe_cast()) 198 173 } 199 174 } 200 175 ··· 223 198 } else { 224 199 frombytes_at::<Self>( 225 200 hs_fw.fw, 226 - (hs_fw.hdr.header_offset as usize) 201 + usize::from_safe_cast(hs_fw.hdr.header_offset) 227 202 // Skip the load header... 228 203 .checked_add(size_of::<HsLoadHeaderV2>()) 229 204 // ... and jump to app header `idx`. 230 205 .and_then(|offset| { 231 - offset.checked_add((idx as usize).checked_mul(size_of::<Self>())?) 206 + offset 207 + .checked_add(usize::from_safe_cast(idx).checked_mul(size_of::<Self>())?) 232 208 }) 233 209 .ok_or(EINVAL)?, 234 210 ) ··· 344 318 dev_err!(dev, "invalid fuse version for Booter firmware\n"); 345 319 return Err(EINVAL); 346 320 }; 347 - signatures.nth(idx as usize) 321 + signatures.nth(idx.into_safe_cast()) 348 322 } 349 323 } 350 324 .ok_or(EINVAL)?; 351 325 352 - ucode.patch_signature(&signature, patch_loc as usize)? 326 + ucode.patch_signature(&signature, patch_loc.into_safe_cast())? 353 327 } 354 328 }; 355 329
+103 -81
drivers/gpu/nova-core/firmware/fwsec.rs
··· 10 10 //! - The command to be run, as this firmware can perform several tasks ; 11 11 //! - The ucode signature, so the GSP falcon can run FWSEC in HS mode. 12 12 13 - use core::marker::PhantomData; 14 - use core::mem::{align_of, size_of}; 15 - use core::ops::Deref; 13 + use core::{ 14 + marker::PhantomData, 15 + mem::size_of, 16 + ops::Deref, // 17 + }; 16 18 17 - use kernel::device::{self, Device}; 18 - use kernel::prelude::*; 19 - use kernel::transmute::FromBytes; 19 + use kernel::{ 20 + device::{ 21 + self, 22 + Device, // 23 + }, 24 + prelude::*, 25 + transmute::{ 26 + AsBytes, 27 + FromBytes, // 28 + }, 29 + }; 20 30 21 - use crate::dma::DmaObject; 22 - use crate::driver::Bar0; 23 - use crate::falcon::gsp::Gsp; 24 - use crate::falcon::{Falcon, FalconBromParams, FalconFirmware, FalconLoadParams, FalconLoadTarget}; 25 - use crate::firmware::{FalconUCodeDescV3, FirmwareDmaObject, FirmwareSignature, Signed, Unsigned}; 26 - use crate::vbios::Vbios; 31 + use crate::{ 32 + dma::DmaObject, 33 + driver::Bar0, 34 + falcon::{ 35 + gsp::Gsp, 36 + Falcon, 37 + FalconBromParams, 38 + FalconFirmware, 39 + FalconLoadParams, 40 + FalconLoadTarget, // 41 + }, 42 + firmware::{ 43 + FalconUCodeDescV3, 44 + FirmwareDmaObject, 45 + FirmwareSignature, 46 + Signed, 47 + Unsigned, // 48 + }, 49 + num::{ 50 + FromSafeCast, 51 + IntoSafeCast, // 52 + }, 53 + vbios::Vbios, 54 + }; 27 55 28 56 const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4; 29 57 ··· 63 35 entry_size: u8, 64 36 entry_count: u8, 65 37 } 66 - // SAFETY: any byte sequence is valid for this struct. 38 + // SAFETY: Any byte sequence is valid for this struct. 67 39 unsafe impl FromBytes for FalconAppifHdrV1 {} 68 40 69 41 #[repr(C, packed)] ··· 72 44 id: u32, 73 45 dmem_base: u32, 74 46 } 75 - // SAFETY: any byte sequence is valid for this struct. 47 + // SAFETY: Any byte sequence is valid for this struct. 76 48 unsafe impl FromBytes for FalconAppifV1 {} 77 49 78 50 #[derive(Debug)] ··· 96 68 ucode_cmd_mask1: u32, 97 69 multi_tgt_tbl: u32, 98 70 } 99 - // SAFETY: any byte sequence is valid for this struct. 71 + // SAFETY: Any byte sequence is valid for this struct. 100 72 unsafe impl FromBytes for FalconAppifDmemmapperV3 {} 73 + // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 74 + unsafe impl AsBytes for FalconAppifDmemmapperV3 {} 101 75 102 76 #[derive(Debug)] 103 77 #[repr(C, packed)] ··· 110 80 size: u32, 111 81 flags: u32, 112 82 } 113 - // SAFETY: any byte sequence is valid for this struct. 83 + // SAFETY: Any byte sequence is valid for this struct. 114 84 unsafe impl FromBytes for ReadVbios {} 85 + // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 86 + unsafe impl AsBytes for ReadVbios {} 115 87 116 88 #[derive(Debug)] 117 89 #[repr(C, packed)] ··· 124 92 size: u32, 125 93 ftype: u32, 126 94 } 127 - // SAFETY: any byte sequence is valid for this struct. 95 + // SAFETY: Any byte sequence is valid for this struct. 128 96 unsafe impl FromBytes for FrtsRegion {} 97 + // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 98 + unsafe impl AsBytes for FrtsRegion {} 129 99 130 100 const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2; 131 101 ··· 136 102 read_vbios: ReadVbios, 137 103 frts_region: FrtsRegion, 138 104 } 139 - // SAFETY: any byte sequence is valid for this struct. 105 + // SAFETY: Any byte sequence is valid for this struct. 140 106 unsafe impl FromBytes for FrtsCmd {} 107 + // SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability. 108 + unsafe impl AsBytes for FrtsCmd {} 141 109 142 110 const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15; 143 111 const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19; ··· 183 147 /// 184 148 /// # Safety 185 149 /// 186 - /// Callers must ensure that the region of memory returned is not written for as long as the 187 - /// returned reference is alive. 188 - /// 189 - /// TODO[TRSM][COHA]: Remove this and `transmute_mut` once `CoherentAllocation::as_slice` is 190 - /// available and we have a way to transmute objects implementing FromBytes, e.g.: 191 - /// https://lore.kernel.org/lkml/20250330234039.29814-1-christiansantoslima21@gmail.com/ 192 - unsafe fn transmute<'a, 'b, T: Sized + FromBytes>( 193 - fw: &'a DmaObject, 194 - offset: usize, 195 - ) -> Result<&'b T> { 196 - if offset + size_of::<T>() > fw.size() { 197 - return Err(EINVAL); 198 - } 199 - if (fw.start_ptr() as usize + offset) % align_of::<T>() != 0 { 200 - return Err(EINVAL); 201 - } 202 - 203 - // SAFETY: we have checked that the pointer is properly aligned that its pointed memory is 204 - // large enough the contains an instance of `T`, which implements `FromBytes`. 205 - Ok(unsafe { &*(fw.start_ptr().add(offset).cast::<T>()) }) 150 + /// * Callers must ensure that the device does not read/write to/from memory while the returned 151 + /// reference is live. 152 + /// * Callers must ensure that this call does not race with a write to the same region while 153 + /// the returned reference is live. 154 + unsafe fn transmute<T: Sized + FromBytes>(fw: &DmaObject, offset: usize) -> Result<&T> { 155 + // SAFETY: The safety requirements of the function guarantee the device won't read 156 + // or write to memory while the reference is alive and that this call won't race 157 + // with writes to the same memory region. 158 + T::from_bytes(unsafe { fw.as_slice(offset, size_of::<T>())? }).ok_or(EINVAL) 206 159 } 207 160 208 161 /// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must ··· 199 174 /// 200 175 /// # Safety 201 176 /// 202 - /// Callers must ensure that the region of memory returned is not read or written for as long as 203 - /// the returned reference is alive. 204 - unsafe fn transmute_mut<'a, 'b, T: Sized + FromBytes>( 205 - fw: &'a mut DmaObject, 177 + /// * Callers must ensure that the device does not read/write to/from memory while the returned 178 + /// slice is live. 179 + /// * Callers must ensure that this call does not race with a read or write to the same region 180 + /// while the returned slice is live. 181 + unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>( 182 + fw: &mut DmaObject, 206 183 offset: usize, 207 - ) -> Result<&'b mut T> { 208 - if offset + size_of::<T>() > fw.size() { 209 - return Err(EINVAL); 210 - } 211 - if (fw.start_ptr_mut() as usize + offset) % align_of::<T>() != 0 { 212 - return Err(EINVAL); 213 - } 214 - 215 - // SAFETY: we have checked that the pointer is properly aligned that its pointed memory is 216 - // large enough the contains an instance of `T`, which implements `FromBytes`. 217 - Ok(unsafe { &mut *(fw.start_ptr_mut().add(offset).cast::<T>()) }) 184 + ) -> Result<&mut T> { 185 + // SAFETY: The safety requirements of the function guarantee the device won't read 186 + // or write to memory while the reference is alive and that this call won't race 187 + // with writes or reads to the same memory region. 188 + T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::<T>())? }).ok_or(EINVAL) 218 189 } 219 190 220 191 /// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon. ··· 271 250 let ucode = bios.fwsec_image().ucode(desc)?; 272 251 let mut dma_object = DmaObject::from_data(dev, ucode)?; 273 252 274 - let hdr_offset = (desc.imem_load_size + desc.interface_offset) as usize; 253 + let hdr_offset = usize::from_safe_cast(desc.imem_load_size + desc.interface_offset); 275 254 // SAFETY: we have exclusive access to `dma_object`. 276 255 let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?; 277 256 ··· 280 259 } 281 260 282 261 // Find the DMEM mapper section in the firmware. 283 - for i in 0..hdr.entry_count as usize { 284 - let app: &FalconAppifV1 = 262 + for i in 0..usize::from(hdr.entry_count) { 285 263 // SAFETY: we have exclusive access to `dma_object`. 286 - unsafe { 264 + let app: &FalconAppifV1 = unsafe { 287 265 transmute( 288 266 &dma_object, 289 - hdr_offset + hdr.header_size as usize + i * hdr.entry_size as usize 267 + hdr_offset + usize::from(hdr.header_size) + i * usize::from(hdr.entry_size), 290 268 ) 291 269 }?; 292 270 293 271 if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER { 294 272 continue; 295 273 } 274 + let dmem_base = app.dmem_base; 296 275 297 276 // SAFETY: we have exclusive access to `dma_object`. 298 277 let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe { 299 278 transmute_mut( 300 279 &mut dma_object, 301 - (desc.imem_load_size + app.dmem_base) as usize, 280 + (desc.imem_load_size + dmem_base).into_safe_cast(), 302 281 ) 303 282 }?; 283 + 284 + dmem_mapper.init_cmd = match cmd { 285 + FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS, 286 + FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB, 287 + }; 288 + let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset; 304 289 305 290 // SAFETY: we have exclusive access to `dma_object`. 306 291 let frts_cmd: &mut FrtsCmd = unsafe { 307 292 transmute_mut( 308 293 &mut dma_object, 309 - (desc.imem_load_size + dmem_mapper.cmd_in_buffer_offset) as usize, 294 + (desc.imem_load_size + cmd_in_buffer_offset).into_safe_cast(), 310 295 ) 311 296 }?; 312 297 313 298 frts_cmd.read_vbios = ReadVbios { 314 299 ver: 1, 315 - hdr: size_of::<ReadVbios>() as u32, 300 + hdr: u32::try_from(size_of::<ReadVbios>())?, 316 301 addr: 0, 317 302 size: 0, 318 303 flags: 2, 319 304 }; 320 - 321 - dmem_mapper.init_cmd = match cmd { 322 - FwsecCommand::Frts { 323 - frts_addr, 324 - frts_size, 325 - } => { 326 - frts_cmd.frts_region = FrtsRegion { 327 - ver: 1, 328 - hdr: size_of::<FrtsRegion>() as u32, 329 - addr: (frts_addr >> 12) as u32, 330 - size: (frts_size >> 12) as u32, 331 - ftype: NVFW_FRTS_CMD_REGION_TYPE_FB, 332 - }; 333 - 334 - NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS 335 - } 336 - FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB, 337 - }; 305 + if let FwsecCommand::Frts { 306 + frts_addr, 307 + frts_size, 308 + } = cmd 309 + { 310 + frts_cmd.frts_region = FrtsRegion { 311 + ver: 1, 312 + hdr: u32::try_from(size_of::<FrtsRegion>())?, 313 + addr: u32::try_from(frts_addr >> 12)?, 314 + size: u32::try_from(frts_size >> 12)?, 315 + ftype: NVFW_FRTS_CMD_REGION_TYPE_FB, 316 + }; 317 + } 338 318 339 319 // Return early as we found and patched the DMEMMAPPER region. 340 320 return Ok(Self(dma_object, PhantomData)); ··· 360 338 // Patch signature if needed. 361 339 let desc = bios.fwsec_image().header()?; 362 340 let ucode_signed = if desc.signature_count != 0 { 363 - let sig_base_img = (desc.imem_load_size + desc.pkc_data_offset) as usize; 341 + let sig_base_img = usize::from_safe_cast(desc.imem_load_size + desc.pkc_data_offset); 364 342 let desc_sig_versions = u32::from(desc.signature_versions); 365 343 let reg_fuse_version = 366 344 falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?; ··· 391 369 // Mask of the bits of `desc_sig_versions` to preserve. 392 370 let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1); 393 371 394 - (desc_sig_versions & reg_fuse_version_mask).count_ones() as usize 372 + usize::from_safe_cast((desc_sig_versions & reg_fuse_version_mask).count_ones()) 395 373 }; 396 374 397 375 dev_dbg!(dev, "patching signature with index {}\n", signature_idx);
+34 -19
drivers/gpu/nova-core/firmware/gsp.rs
··· 2 2 3 3 use core::mem::size_of_val; 4 4 5 - use kernel::device; 6 - use kernel::dma::{DataDirection, DmaAddress}; 7 - use kernel::kvec; 8 - use kernel::prelude::*; 9 - use kernel::scatterlist::{Owned, SGTable}; 5 + use kernel::{ 6 + device, 7 + dma::{ 8 + DataDirection, 9 + DmaAddress, // 10 + }, 11 + kvec, 12 + prelude::*, 13 + scatterlist::{ 14 + Owned, 15 + SGTable, // 16 + }, 17 + }; 10 18 11 - use crate::dma::DmaObject; 12 - use crate::firmware::riscv::RiscvFirmware; 13 - use crate::gpu::{Architecture, Chipset}; 14 - use crate::gsp::GSP_PAGE_SIZE; 19 + use crate::{ 20 + dma::DmaObject, 21 + firmware::riscv::RiscvFirmware, 22 + gpu::{ 23 + Architecture, 24 + Chipset, // 25 + }, 26 + gsp::GSP_PAGE_SIZE, 27 + num::FromSafeCast, 28 + }; 15 29 16 30 /// Ad-hoc and temporary module to extract sections from ELF images. 17 31 /// ··· 143 129 /// Level 0 page table (single 4KB page) with one entry: DMA address of first level 1 page. 144 130 level0: DmaObject, 145 131 /// Size in bytes of the firmware contained in [`Self::fw`]. 146 - size: usize, 132 + pub(crate) size: usize, 147 133 /// Device-mapped GSP signatures matching the GPU's [`Chipset`]. 148 - signatures: DmaObject, 134 + pub(crate) signatures: DmaObject, 149 135 /// GSP bootloader, verifies the GSP firmware before loading and running it. 150 - bootloader: RiscvFirmware, 136 + pub(crate) bootloader: RiscvFirmware, 151 137 } 152 138 153 139 impl GspFirmware { ··· 164 150 165 151 let sigs_section = match chipset.arch() { 166 152 Architecture::Ampere => ".fwsignature_ga10x", 153 + Architecture::Ada => ".fwsignature_ad10x", 167 154 _ => return Err(ENOTSUPP), 168 155 }; 169 156 let signatures = elf::elf64_section(fw.data(), sigs_section) ··· 217 202 let mut level0_data = kvec![0u8; GSP_PAGE_SIZE]?; 218 203 219 204 // Fill level 1 page entry. 220 - #[allow(clippy::useless_conversion)] 221 - let level1_entry = u64::from(level1.iter().next().unwrap().dma_address()); 222 - let dst = &mut level0_data[..size_of_val(&level1_entry)]; 223 - dst.copy_from_slice(&level1_entry.to_le_bytes()); 205 + let level1_entry = level1.iter().next().ok_or(EINVAL)?; 206 + let level1_entry_addr = level1_entry.dma_address(); 207 + let dst = &mut level0_data[..size_of_val(&level1_entry_addr)]; 208 + dst.copy_from_slice(&level1_entry_addr.to_le_bytes()); 224 209 225 210 // Turn the level0 page table into a [`DmaObject`]. 226 211 DmaObject::from_data(dev, &level0_data)? ··· 231 216 })) 232 217 } 233 218 234 - #[expect(unused)] 235 219 /// Returns the DMA handle of the radix3 level 0 page table. 236 220 pub(crate) fn radix3_dma_handle(&self) -> DmaAddress { 237 221 self.level0.dma_handle() ··· 245 231 fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Result<VVec<u8>> { 246 232 for sg_entry in sg_table.iter() { 247 233 // Number of pages we need to map. 248 - let num_pages = (sg_entry.dma_len() as usize).div_ceil(GSP_PAGE_SIZE); 234 + let num_pages = usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE); 249 235 250 236 for i in 0..num_pages { 251 - let entry = sg_entry.dma_address() + (i as u64 * GSP_PAGE_SIZE as u64); 237 + let entry = sg_entry.dma_address() 238 + + (u64::from_safe_cast(i) * u64::from_safe_cast(GSP_PAGE_SIZE)); 252 239 dst.extend_from_slice(&entry.to_le_bytes(), GFP_KERNEL)?; 253 240 } 254 241 }
+19 -15
drivers/gpu/nova-core/firmware/riscv.rs
··· 5 5 6 6 use core::mem::size_of; 7 7 8 - use kernel::device; 9 - use kernel::firmware::Firmware; 10 - use kernel::prelude::*; 11 - use kernel::transmute::FromBytes; 8 + use kernel::{ 9 + device, 10 + firmware::Firmware, 11 + prelude::*, 12 + transmute::FromBytes, // 13 + }; 12 14 13 - use crate::dma::DmaObject; 14 - use crate::firmware::BinFirmware; 15 + use crate::{ 16 + dma::DmaObject, 17 + firmware::BinFirmware, 18 + num::FromSafeCast, // 19 + }; 15 20 16 21 /// Descriptor for microcode running on a RISC-V core. 17 22 #[repr(C)] ··· 46 41 /// 47 42 /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image. 48 43 fn new(bin_fw: &BinFirmware<'_>) -> Result<Self> { 49 - let offset = bin_fw.hdr.header_offset as usize; 44 + let offset = usize::from_safe_cast(bin_fw.hdr.header_offset); 50 45 51 46 bin_fw 52 47 .fw ··· 57 52 } 58 53 59 54 /// A parsed firmware for a RISC-V core, ready to be loaded and run. 60 - #[expect(unused)] 61 55 pub(crate) struct RiscvFirmware { 62 56 /// Offset at which the code starts in the firmware image. 63 - code_offset: u32, 57 + pub(crate) code_offset: u32, 64 58 /// Offset at which the data starts in the firmware image. 65 - data_offset: u32, 59 + pub(crate) data_offset: u32, 66 60 /// Offset at which the manifest starts in the firmware image. 67 - manifest_offset: u32, 61 + pub(crate) manifest_offset: u32, 68 62 /// Application version. 69 - app_version: u32, 63 + pub(crate) app_version: u32, 70 64 /// Device-mapped firmware image. 71 - ucode: DmaObject, 65 + pub(crate) ucode: DmaObject, 72 66 } 73 67 74 68 impl RiscvFirmware { ··· 78 74 let riscv_desc = RmRiscvUCodeDesc::new(&bin_fw)?; 79 75 80 76 let ucode = { 81 - let start = bin_fw.hdr.data_offset as usize; 82 - let len = bin_fw.hdr.data_size as usize; 77 + let start = usize::from_safe_cast(bin_fw.hdr.data_offset); 78 + let len = usize::from_safe_cast(bin_fw.hdr.data_size); 83 79 84 80 DmaObject::from_data(dev, fw.data().get(start..start + len).ok_or(EINVAL)?)? 85 81 };
+24 -24
drivers/gpu/nova-core/gfw.rs
··· 18 18 //! 19 19 //! Note that the devinit sequence also needs to run during suspend/resume. 20 20 21 - use kernel::bindings; 22 - use kernel::prelude::*; 23 - use kernel::time::Delta; 21 + use kernel::{ 22 + io::poll::read_poll_timeout, 23 + prelude::*, 24 + time::Delta, // 25 + }; 24 26 25 - use crate::driver::Bar0; 26 - use crate::regs; 27 - use crate::util; 27 + use crate::{ 28 + driver::Bar0, 29 + regs, // 30 + }; 28 31 29 32 /// Wait for the `GFW` (GPU firmware) boot completion signal (`GFW_BOOT`), or a 4 seconds timeout. 30 33 /// ··· 53 50 // 54 51 // TIMEOUT: arbitrarily large value. GFW starts running immediately after the GPU is put out of 55 52 // reset, and should complete in less time than that. 56 - util::wait_on(Delta::from_secs(4), || { 57 - // Check that FWSEC has lowered its protection level before reading the GFW_BOOT status. 58 - let gfw_booted = regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK::read(bar) 59 - .read_protection_level0() 60 - && regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT::read(bar).completed(); 61 - 62 - if gfw_booted { 63 - Some(()) 64 - } else { 65 - // TODO[DLAY]: replace with [1] once it merges. 66 - // [1] https://lore.kernel.org/rust-for-linux/20250423192857.199712-6-fujita.tomonori@gmail.com/ 67 - // 68 - // SAFETY: `msleep()` is safe to call with any parameter. 69 - unsafe { bindings::msleep(1) }; 70 - 71 - None 72 - } 73 - }) 53 + read_poll_timeout( 54 + || { 55 + Ok( 56 + // Check that FWSEC has lowered its protection level before reading the GFW_BOOT 57 + // status. 58 + regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK::read(bar) 59 + .read_protection_level0() 60 + && regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT::read(bar).completed(), 61 + ) 62 + }, 63 + |&gfw_booted| gfw_booted, 64 + Delta::from_millis(1), 65 + Delta::from_secs(4), 66 + ) 67 + .map(|_| ()) 74 68 }
+89 -28
drivers/gpu/nova-core/gpu.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - use kernel::{device, devres::Devres, error::code::*, fmt, pci, prelude::*, sync::Arc}; 3 + use kernel::{ 4 + device, 5 + devres::Devres, 6 + fmt, 7 + pci, 8 + prelude::*, 9 + sync::Arc, // 10 + }; 4 11 5 - use crate::driver::Bar0; 6 - use crate::falcon::{gsp::Gsp as GspFalcon, sec2::Sec2 as Sec2Falcon, Falcon}; 7 - use crate::fb::SysmemFlush; 8 - use crate::gfw; 9 - use crate::gsp::Gsp; 10 - use crate::regs; 12 + use crate::{ 13 + driver::Bar0, 14 + falcon::{ 15 + gsp::Gsp as GspFalcon, 16 + sec2::Sec2 as Sec2Falcon, 17 + Falcon, // 18 + }, 19 + fb::SysmemFlush, 20 + gfw, 21 + gsp::Gsp, 22 + regs, 23 + }; 11 24 12 25 macro_rules! define_chipset { 13 26 ({ $($variant:ident = $value:expr),* $(,)* }) => ··· 122 109 } 123 110 124 111 /// Enum representation of the GPU generation. 125 - #[derive(fmt::Debug)] 112 + /// 113 + /// TODO: remove the `Default` trait implementation, and the `#[default]` 114 + /// attribute, once the register!() macro (which creates Architecture items) no 115 + /// longer requires it for read-only fields. 116 + #[derive(fmt::Debug, Default, Copy, Clone)] 117 + #[repr(u8)] 126 118 pub(crate) enum Architecture { 119 + #[default] 127 120 Turing = 0x16, 128 121 Ampere = 0x17, 129 122 Ada = 0x19, ··· 148 129 } 149 130 } 150 131 132 + impl From<Architecture> for u8 { 133 + fn from(value: Architecture) -> Self { 134 + // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless. 135 + value as u8 136 + } 137 + } 138 + 151 139 pub(crate) struct Revision { 152 140 major: u8, 153 141 minor: u8, 154 142 } 155 143 156 - impl Revision { 157 - fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self { 144 + impl From<regs::NV_PMC_BOOT_42> for Revision { 145 + fn from(boot0: regs::NV_PMC_BOOT_42) -> Self { 158 146 Self { 159 147 major: boot0.major_revision(), 160 148 minor: boot0.minor_revision(), ··· 175 149 } 176 150 } 177 151 178 - /// Structure holding the metadata of the GPU. 152 + /// Structure holding a basic description of the GPU: `Chipset` and `Revision`. 179 153 pub(crate) struct Spec { 180 154 chipset: Chipset, 181 - /// The revision of the chipset. 182 155 revision: Revision, 183 156 } 184 157 185 158 impl Spec { 186 - fn new(bar: &Bar0) -> Result<Spec> { 159 + fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> { 160 + // Some brief notes about boot0 and boot42, in chronological order: 161 + // 162 + // NV04 through NV50: 163 + // 164 + // Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs. 165 + // boot42 may not even exist on some of these GPUs. 166 + // 167 + // Fermi through Volta: 168 + // 169 + // Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42 170 + // is also guaranteed to be both present and accurate. 171 + // 172 + // Turing and later: 173 + // 174 + // Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not 175 + // from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU. 176 + // Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs. 177 + 187 178 let boot0 = regs::NV_PMC_BOOT_0::read(bar); 188 179 189 - Ok(Self { 190 - chipset: boot0.chipset()?, 191 - revision: Revision::from_boot0(boot0), 180 + if boot0.is_older_than_fermi() { 181 + return Err(ENODEV); 182 + } 183 + 184 + let boot42 = regs::NV_PMC_BOOT_42::read(bar); 185 + Spec::try_from(boot42).inspect_err(|_| { 186 + dev_err!(dev, "Unsupported chipset: {}\n", boot42); 192 187 }) 188 + } 189 + } 190 + 191 + impl TryFrom<regs::NV_PMC_BOOT_42> for Spec { 192 + type Error = Error; 193 + 194 + fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> { 195 + Ok(Self { 196 + chipset: boot42.chipset()?, 197 + revision: boot42.into(), 198 + }) 199 + } 200 + } 201 + 202 + impl fmt::Display for Spec { 203 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 204 + f.write_fmt(fmt!( 205 + "Chipset: {}, Architecture: {:?}, Revision: {}", 206 + self.chipset, 207 + self.chipset.arch(), 208 + self.revision 209 + )) 193 210 } 194 211 } 195 212 ··· 261 192 bar: &'a Bar0, 262 193 ) -> impl PinInit<Self, Error> + 'a { 263 194 try_pin_init!(Self { 264 - spec: Spec::new(bar).inspect(|spec| { 265 - dev_info!( 266 - pdev.as_ref(), 267 - "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n", 268 - spec.chipset, 269 - spec.chipset.arch(), 270 - spec.revision 271 - ); 195 + spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| { 196 + dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec); 272 197 })?, 273 198 274 199 // We must wait for GFW_BOOT completion before doing any significant setup on the GPU. ··· 276 213 gsp_falcon: Falcon::new( 277 214 pdev.as_ref(), 278 215 spec.chipset, 279 - bar, 280 - spec.chipset > Chipset::GA100, 281 216 ) 282 217 .inspect(|falcon| falcon.clear_swgen0_intr(bar))?, 283 218 284 - sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset, bar, true)?, 219 + sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?, 285 220 286 - gsp <- Gsp::new(), 221 + gsp <- Gsp::new(pdev)?, 287 222 288 223 _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? }, 289 224
+145 -6
drivers/gpu/nova-core/gsp.rs
··· 2 2 3 3 mod boot; 4 4 5 - use kernel::prelude::*; 5 + use kernel::{ 6 + device, 7 + dma::{ 8 + CoherentAllocation, 9 + DmaAddress, // 10 + }, 11 + dma_write, 12 + pci, 13 + prelude::*, 14 + transmute::AsBytes, // 15 + }; 6 16 17 + pub(crate) mod cmdq; 18 + pub(crate) mod commands; 7 19 mod fw; 20 + mod sequencer; 21 + 22 + pub(crate) use fw::{ 23 + GspFwWprMeta, 24 + LibosParams, // 25 + }; 26 + 27 + use crate::{ 28 + gsp::cmdq::Cmdq, 29 + gsp::fw::{ 30 + GspArgumentsCached, 31 + LibosMemoryRegionInitArgument, // 32 + }, 33 + num, 34 + }; 8 35 9 36 pub(crate) const GSP_PAGE_SHIFT: usize = 12; 10 37 pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT; 11 38 12 - /// GSP runtime data. 39 + /// Number of GSP pages to use in a RM log buffer. 40 + const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10; 41 + 42 + /// Array of page table entries, as understood by the GSP bootloader. 43 + #[repr(C)] 44 + struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]); 45 + 46 + /// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one. 47 + unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {} 48 + 49 + impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> { 50 + /// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`. 51 + fn new(start: DmaAddress) -> Result<Self> { 52 + let mut ptes = [0u64; NUM_PAGES]; 53 + for (i, pte) in ptes.iter_mut().enumerate() { 54 + *pte = start 55 + .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT) 56 + .ok_or(EOVERFLOW)?; 57 + } 58 + 59 + Ok(Self(ptes)) 60 + } 61 + } 62 + 63 + /// The logging buffers are byte queues that contain encoded printf-like 64 + /// messages from GSP-RM. They need to be decoded by a special application 65 + /// that can parse the buffers. 13 66 /// 14 - /// This is an empty pinned placeholder for now. 67 + /// The 'loginit' buffer contains logs from early GSP-RM init and 68 + /// exception dumps. The 'logrm' buffer contains the subsequent logs. Both are 69 + /// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE. 70 + /// 71 + /// The physical address map for the log buffer is stored in the buffer 72 + /// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp). 73 + /// Initially, pp is equal to 0. If the buffer has valid logging data in it, 74 + /// then pp points to index into the buffer where the next logging entry will 75 + /// be written. Therefore, the logging data is valid if: 76 + /// 1 <= pp < sizeof(buffer)/sizeof(u64) 77 + struct LogBuffer(CoherentAllocation<u8>); 78 + 79 + impl LogBuffer { 80 + /// Creates a new `LogBuffer` mapped on `dev`. 81 + fn new(dev: &device::Device<device::Bound>) -> Result<Self> { 82 + const NUM_PAGES: usize = RM_LOG_BUFFER_NUM_PAGES; 83 + 84 + let mut obj = Self(CoherentAllocation::<u8>::alloc_coherent( 85 + dev, 86 + NUM_PAGES * GSP_PAGE_SIZE, 87 + GFP_KERNEL | __GFP_ZERO, 88 + )?); 89 + let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?; 90 + 91 + // SAFETY: `obj` has just been created and we are its sole user. 92 + unsafe { 93 + // Copy the self-mapping PTE at the expected location. 94 + obj.0 95 + .as_slice_mut(size_of::<u64>(), size_of_val(&ptes))? 96 + .copy_from_slice(ptes.as_bytes()) 97 + }; 98 + 99 + Ok(obj) 100 + } 101 + } 102 + 103 + /// GSP runtime data. 15 104 #[pin_data] 16 - pub(crate) struct Gsp {} 105 + pub(crate) struct Gsp { 106 + /// Libos arguments. 107 + pub(crate) libos: CoherentAllocation<LibosMemoryRegionInitArgument>, 108 + /// Init log buffer. 109 + loginit: LogBuffer, 110 + /// Interrupts log buffer. 111 + logintr: LogBuffer, 112 + /// RM log buffer. 113 + logrm: LogBuffer, 114 + /// Command queue. 115 + pub(crate) cmdq: Cmdq, 116 + /// RM arguments. 117 + rmargs: CoherentAllocation<GspArgumentsCached>, 118 + } 17 119 18 120 impl Gsp { 19 - pub(crate) fn new() -> impl PinInit<Self> { 20 - pin_init!(Self {}) 121 + // Creates an in-place initializer for a `Gsp` manager for `pdev`. 122 + pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self, Error>> { 123 + let dev = pdev.as_ref(); 124 + let libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent( 125 + dev, 126 + GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(), 127 + GFP_KERNEL | __GFP_ZERO, 128 + )?; 129 + 130 + // Initialise the logging structures. The OpenRM equivalents are in: 131 + // _kgspInitLibosLoggingStructures (allocates memory for buffers) 132 + // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) 133 + let loginit = LogBuffer::new(dev)?; 134 + dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; 135 + 136 + let logintr = LogBuffer::new(dev)?; 137 + dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; 138 + 139 + let logrm = LogBuffer::new(dev)?; 140 + dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; 141 + 142 + let cmdq = Cmdq::new(dev)?; 143 + 144 + let rmargs = CoherentAllocation::<GspArgumentsCached>::alloc_coherent( 145 + dev, 146 + 1, 147 + GFP_KERNEL | __GFP_ZERO, 148 + )?; 149 + dma_write!(rmargs[0] = fw::GspArgumentsCached::new(&cmdq))?; 150 + dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", &rmargs))?; 151 + 152 + Ok(try_pin_init!(Self { 153 + libos, 154 + loginit, 155 + logintr, 156 + logrm, 157 + rmargs, 158 + cmdq, 159 + })) 21 160 } 22 161 }
+134 -19
drivers/gpu/nova-core/gsp/boot.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - use kernel::device; 4 - use kernel::pci; 5 - use kernel::prelude::*; 6 - 7 - use crate::driver::Bar0; 8 - use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon}; 9 - use crate::fb::FbLayout; 10 - use crate::firmware::{ 11 - booter::{BooterFirmware, BooterKind}, 12 - fwsec::{FwsecCommand, FwsecFirmware}, 13 - gsp::GspFirmware, 14 - FIRMWARE_VERSION, 3 + use kernel::{ 4 + device, 5 + dma::CoherentAllocation, 6 + dma_write, 7 + io::poll::read_poll_timeout, 8 + pci, 9 + prelude::*, 10 + time::Delta, // 15 11 }; 16 - use crate::gpu::Chipset; 17 - use crate::regs; 18 - use crate::vbios::Vbios; 12 + 13 + use crate::{ 14 + driver::Bar0, 15 + falcon::{ 16 + gsp::Gsp, 17 + sec2::Sec2, 18 + Falcon, // 19 + }, 20 + fb::FbLayout, 21 + firmware::{ 22 + booter::{ 23 + BooterFirmware, 24 + BooterKind, // 25 + }, 26 + fwsec::{ 27 + FwsecCommand, 28 + FwsecFirmware, // 29 + }, 30 + gsp::GspFirmware, 31 + FIRMWARE_VERSION, // 32 + }, 33 + gpu::Chipset, 34 + gsp::{ 35 + commands, 36 + sequencer::{ 37 + GspSequencer, 38 + GspSequencerParams, // 39 + }, 40 + GspFwWprMeta, // 41 + }, 42 + regs, 43 + vbios::Vbios, 44 + }; 19 45 20 46 impl super::Gsp { 21 47 /// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly ··· 128 102 /// 129 103 /// Upon return, the GSP is up and running, and its runtime object given as return value. 130 104 pub(crate) fn boot( 131 - self: Pin<&mut Self>, 105 + mut self: Pin<&mut Self>, 132 106 pdev: &pci::Device<device::Bound>, 133 107 bar: &Bar0, 134 108 chipset: Chipset, ··· 139 113 140 114 let bios = Vbios::new(dev, bar)?; 141 115 142 - let _gsp_fw = KBox::pin_init( 116 + let gsp_fw = KBox::pin_init( 143 117 GspFirmware::new(dev, chipset, FIRMWARE_VERSION)?, 144 118 GFP_KERNEL, 145 119 )?; 146 120 147 - let fb_layout = FbLayout::new(chipset, bar)?; 121 + let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?; 148 122 dev_dbg!(dev, "{:#x?}\n", fb_layout); 149 123 150 124 Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?; 151 125 152 - let _booter_loader = BooterFirmware::new( 126 + let booter_loader = BooterFirmware::new( 153 127 dev, 154 128 BooterKind::Loader, 155 129 chipset, ··· 157 131 sec2_falcon, 158 132 bar, 159 133 )?; 134 + 135 + let wpr_meta = 136 + CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; 137 + dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?; 138 + 139 + self.cmdq 140 + .send_command(bar, commands::SetSystemInfo::new(pdev))?; 141 + self.cmdq.send_command(bar, commands::SetRegistry::new())?; 142 + 143 + gsp_falcon.reset(bar)?; 144 + let libos_handle = self.libos.dma_handle(); 145 + let (mbox0, mbox1) = gsp_falcon.boot( 146 + bar, 147 + Some(libos_handle as u32), 148 + Some((libos_handle >> 32) as u32), 149 + )?; 150 + dev_dbg!( 151 + pdev.as_ref(), 152 + "GSP MBOX0: {:#x}, MBOX1: {:#x}\n", 153 + mbox0, 154 + mbox1 155 + ); 156 + 157 + dev_dbg!( 158 + pdev.as_ref(), 159 + "Using SEC2 to load and run the booter_load firmware...\n" 160 + ); 161 + 162 + sec2_falcon.reset(bar)?; 163 + sec2_falcon.dma_load(bar, &booter_loader)?; 164 + let wpr_handle = wpr_meta.dma_handle(); 165 + let (mbox0, mbox1) = sec2_falcon.boot( 166 + bar, 167 + Some(wpr_handle as u32), 168 + Some((wpr_handle >> 32) as u32), 169 + )?; 170 + dev_dbg!( 171 + pdev.as_ref(), 172 + "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n", 173 + mbox0, 174 + mbox1 175 + ); 176 + 177 + if mbox0 != 0 { 178 + dev_err!( 179 + pdev.as_ref(), 180 + "Booter-load failed with error {:#x}\n", 181 + mbox0 182 + ); 183 + return Err(ENODEV); 184 + } 185 + 186 + gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version); 187 + 188 + // Poll for RISC-V to become active before running sequencer 189 + read_poll_timeout( 190 + || Ok(gsp_falcon.is_riscv_active(bar)), 191 + |val: &bool| *val, 192 + Delta::from_millis(10), 193 + Delta::from_secs(5), 194 + )?; 195 + 196 + dev_dbg!( 197 + pdev.as_ref(), 198 + "RISC-V active? {}\n", 199 + gsp_falcon.is_riscv_active(bar), 200 + ); 201 + 202 + // Create and run the GSP sequencer. 203 + let seq_params = GspSequencerParams { 204 + bootloader_app_version: gsp_fw.bootloader.app_version, 205 + libos_dma_handle: libos_handle, 206 + gsp_falcon, 207 + sec2_falcon, 208 + dev: pdev.as_ref().into(), 209 + bar, 210 + }; 211 + GspSequencer::run(&mut self.cmdq, seq_params)?; 212 + 213 + // Wait until GSP is fully initialized. 214 + commands::wait_gsp_init_done(&mut self.cmdq)?; 215 + 216 + // Obtain and display basic GPU information. 217 + let info = commands::get_gsp_info(&mut self.cmdq, bar)?; 218 + dev_info!( 219 + pdev.as_ref(), 220 + "GPU name: {}\n", 221 + info.gpu_name().unwrap_or("invalid GPU name") 222 + ); 160 223 161 224 Ok(()) 162 225 }
+679
drivers/gpu/nova-core/gsp/cmdq.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use core::{ 4 + cmp, 5 + mem, 6 + sync::atomic::{ 7 + fence, 8 + Ordering, // 9 + }, // 10 + }; 11 + 12 + use kernel::{ 13 + device, 14 + dma::{ 15 + CoherentAllocation, 16 + DmaAddress, // 17 + }, 18 + dma_write, 19 + io::poll::read_poll_timeout, 20 + prelude::*, 21 + sync::aref::ARef, 22 + time::Delta, 23 + transmute::{ 24 + AsBytes, 25 + FromBytes, // 26 + }, 27 + }; 28 + 29 + use crate::{ 30 + driver::Bar0, 31 + gsp::{ 32 + fw::{ 33 + GspMsgElement, 34 + MsgFunction, 35 + MsgqRxHeader, 36 + MsgqTxHeader, // 37 + }, 38 + PteArray, 39 + GSP_PAGE_SHIFT, 40 + GSP_PAGE_SIZE, // 41 + }, 42 + num, 43 + regs, 44 + sbuffer::SBufferIter, // 45 + }; 46 + 47 + /// Trait implemented by types representing a command to send to the GSP. 48 + /// 49 + /// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it 50 + /// needs to send a given command. 51 + /// 52 + /// [`CommandToGsp::init`] in particular is responsible for initializing the command directly 53 + /// into the space reserved for it in the command queue buffer. 54 + /// 55 + /// Some commands may be followed by a variable-length payload. For these, the 56 + /// [`CommandToGsp::variable_payload_len`] and [`CommandToGsp::init_variable_payload`] need to be 57 + /// defined as well. 58 + pub(crate) trait CommandToGsp { 59 + /// Function identifying this command to the GSP. 60 + const FUNCTION: MsgFunction; 61 + 62 + /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer. 63 + type Command: FromBytes + AsBytes; 64 + 65 + /// Error type returned by [`CommandToGsp::init`]. 66 + type InitError; 67 + 68 + /// In-place command initializer responsible for filling the command in the command queue 69 + /// buffer. 70 + fn init(&self) -> impl Init<Self::Command, Self::InitError>; 71 + 72 + /// Size of the variable-length payload following the command structure generated by 73 + /// [`CommandToGsp::init`]. 74 + /// 75 + /// Most commands don't have a variable-length payload, so this is zero by default. 76 + fn variable_payload_len(&self) -> usize { 77 + 0 78 + } 79 + 80 + /// Method initializing the variable-length payload. 81 + /// 82 + /// The command buffer is circular, which means that we may need to jump back to its beginning 83 + /// while in the middle of a command. For this reason, the variable-length payload is 84 + /// initialized using a [`SBufferIter`]. 85 + /// 86 + /// This method will receive a buffer of the length returned by 87 + /// [`CommandToGsp::variable_payload_len`], and must write every single byte of it. Leaving 88 + /// unwritten space will lead to an error. 89 + /// 90 + /// Most commands don't have a variable-length payload, so this does nothing by default. 91 + fn init_variable_payload( 92 + &self, 93 + _dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>, 94 + ) -> Result { 95 + Ok(()) 96 + } 97 + } 98 + 99 + /// Trait representing messages received from the GSP. 100 + /// 101 + /// This trait tells [`Cmdq::receive_msg`] how it can receive a given type of message. 102 + pub(crate) trait MessageFromGsp: Sized { 103 + /// Function identifying this message from the GSP. 104 + const FUNCTION: MsgFunction; 105 + 106 + /// Error type returned by [`MessageFromGsp::read`]. 107 + type InitError; 108 + 109 + /// Type containing the raw message to be read from the message queue. 110 + type Message: FromBytes; 111 + 112 + /// Method reading the message from the message queue and returning it. 113 + /// 114 + /// From a `Self::Message` and a [`SBufferIter`], constructs an instance of `Self` and returns 115 + /// it. 116 + fn read( 117 + msg: &Self::Message, 118 + sbuffer: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>, 119 + ) -> Result<Self, Self::InitError>; 120 + } 121 + 122 + /// Number of GSP pages making the [`Msgq`]. 123 + pub(crate) const MSGQ_NUM_PAGES: u32 = 0x3f; 124 + 125 + /// Circular buffer of a [`Msgq`]. 126 + /// 127 + /// This area of memory is to be shared between the driver and the GSP to exchange commands or 128 + /// messages. 129 + #[repr(C, align(0x1000))] 130 + #[derive(Debug)] 131 + struct MsgqData { 132 + data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)], 133 + } 134 + 135 + // Annoyingly we are forced to use a literal to specify the alignment of 136 + // `MsgqData`, so check that it corresponds to the actual GSP page size here. 137 + static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE); 138 + 139 + /// Unidirectional message queue. 140 + /// 141 + /// Contains the data for a message queue, that either the driver or GSP writes to. 142 + /// 143 + /// Note that while the write pointer of `tx` corresponds to the `msgq` of the same instance, the 144 + /// read pointer of `rx` actually refers to the `Msgq` owned by the other side. 145 + /// This design ensures that only the driver or GSP ever writes to a given instance of this struct. 146 + #[repr(C)] 147 + // There is no struct defined for this in the open-gpu-kernel-source headers. 148 + // Instead it is defined by code in `GspMsgQueuesInit()`. 149 + struct Msgq { 150 + /// Header for sending messages, including the write pointer. 151 + tx: MsgqTxHeader, 152 + /// Header for receiving messages, including the read pointer. 153 + rx: MsgqRxHeader, 154 + /// The message queue proper. 155 + msgq: MsgqData, 156 + } 157 + 158 + /// Structure shared between the driver and the GSP and containing the command and message queues. 159 + #[repr(C)] 160 + struct GspMem { 161 + /// Self-mapping page table entries. 162 + ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>, 163 + /// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the 164 + /// write and read pointers that the CPU updates. 165 + /// 166 + /// This member is read-only for the GSP. 167 + cpuq: Msgq, 168 + /// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the 169 + /// write and read pointers that the GSP updates. 170 + /// 171 + /// This member is read-only for the driver. 172 + gspq: Msgq, 173 + } 174 + 175 + // SAFETY: These structs don't meet the no-padding requirements of AsBytes but 176 + // that is not a problem because they are not used outside the kernel. 177 + unsafe impl AsBytes for GspMem {} 178 + 179 + // SAFETY: These structs don't meet the no-padding requirements of FromBytes but 180 + // that is not a problem because they are not used outside the kernel. 181 + unsafe impl FromBytes for GspMem {} 182 + 183 + /// Wrapper around [`GspMem`] to share it with the GPU using a [`CoherentAllocation`]. 184 + /// 185 + /// This provides the low-level functionality to communicate with the GSP, including allocation of 186 + /// queue space to write messages to and management of read/write pointers. 187 + /// 188 + /// This is shared with the GSP, with clear ownership rules regarding the command queues: 189 + /// 190 + /// * The driver owns (i.e. can write to) the part of the CPU message queue between the CPU write 191 + /// pointer and the GSP read pointer. This region is returned by [`Self::driver_write_area`]. 192 + /// * The driver owns (i.e. can read from) the part of the GSP message queue between the CPU read 193 + /// pointer and the GSP write pointer. This region is returned by [`Self::driver_read_area`]. 194 + struct DmaGspMem(CoherentAllocation<GspMem>); 195 + 196 + impl DmaGspMem { 197 + /// Allocate a new instance and map it for `dev`. 198 + fn new(dev: &device::Device<device::Bound>) -> Result<Self> { 199 + const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>(); 200 + const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>(); 201 + 202 + let gsp_mem = 203 + CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; 204 + dma_write!(gsp_mem[0].ptes = PteArray::new(gsp_mem.dma_handle())?)?; 205 + dma_write!(gsp_mem[0].cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES))?; 206 + dma_write!(gsp_mem[0].cpuq.rx = MsgqRxHeader::new())?; 207 + 208 + Ok(Self(gsp_mem)) 209 + } 210 + 211 + /// Returns the region of the CPU message queue that the driver is currently allowed to write 212 + /// to. 213 + /// 214 + /// As the message queue is a circular buffer, the region may be discontiguous in memory. In 215 + /// that case the second slice will have a non-zero length. 216 + fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) { 217 + let tx = self.cpu_write_ptr() as usize; 218 + let rx = self.gsp_read_ptr() as usize; 219 + 220 + // SAFETY: 221 + // - The `CoherentAllocation` contains exactly one object. 222 + // - We will only access the driver-owned part of the shared memory. 223 + // - Per the safety statement of the function, no concurrent access will be performed. 224 + let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0]; 225 + // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `<= MSGQ_NUM_PAGES`. 226 + let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx); 227 + 228 + if rx <= tx { 229 + // The area from `tx` up to the end of the ring, and from the beginning of the ring up 230 + // to `rx`, minus one unit, belongs to the driver. 231 + if rx == 0 { 232 + let last = after_tx.len() - 1; 233 + (&mut after_tx[..last], &mut before_tx[0..0]) 234 + } else { 235 + (after_tx, &mut before_tx[..rx]) 236 + } 237 + } else { 238 + // The area from `tx` to `rx`, minus one unit, belongs to the driver. 239 + // 240 + // PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are 241 + // `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`. 242 + (after_tx.split_at_mut(rx - tx).0, &mut before_tx[0..0]) 243 + } 244 + } 245 + 246 + /// Returns the region of the GSP message queue that the driver is currently allowed to read 247 + /// from. 248 + /// 249 + /// As the message queue is a circular buffer, the region may be discontiguous in memory. In 250 + /// that case the second slice will have a non-zero length. 251 + fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) { 252 + let tx = self.gsp_write_ptr() as usize; 253 + let rx = self.cpu_read_ptr() as usize; 254 + 255 + // SAFETY: 256 + // - The `CoherentAllocation` contains exactly one object. 257 + // - We will only access the driver-owned part of the shared memory. 258 + // - Per the safety statement of the function, no concurrent access will be performed. 259 + let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0]; 260 + // PANIC: per the invariant of `cpu_read_ptr`, `xx` is `<= MSGQ_NUM_PAGES`. 261 + let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx); 262 + 263 + match tx.cmp(&rx) { 264 + cmp::Ordering::Equal => (&after_rx[0..0], &after_rx[0..0]), 265 + cmp::Ordering::Greater => (&after_rx[..tx], &before_rx[0..0]), 266 + cmp::Ordering::Less => (after_rx, &before_rx[..tx]), 267 + } 268 + } 269 + 270 + /// Allocates a region on the command queue that is large enough to send a command of `size` 271 + /// bytes. 272 + /// 273 + /// This returns a [`GspCommand`] ready to be written to by the caller. 274 + /// 275 + /// # Errors 276 + /// 277 + /// - `EAGAIN` if the driver area is too small to hold the requested command. 278 + /// - `EIO` if the command header is not properly aligned. 279 + fn allocate_command(&mut self, size: usize) -> Result<GspCommand<'_>> { 280 + // Get the current writable area as an array of bytes. 281 + let (slice_1, slice_2) = { 282 + let (slice_1, slice_2) = self.driver_write_area(); 283 + 284 + #[allow(clippy::incompatible_msrv)] 285 + (slice_1.as_flattened_mut(), slice_2.as_flattened_mut()) 286 + }; 287 + 288 + // If the GSP is still processing previous messages the shared region 289 + // may be full in which case we will have to retry once the GSP has 290 + // processed the existing commands. 291 + if size_of::<GspMsgElement>() + size > slice_1.len() + slice_2.len() { 292 + return Err(EAGAIN); 293 + } 294 + 295 + // Extract area for the `GspMsgElement`. 296 + let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?; 297 + 298 + // Create the contents area. 299 + let (slice_1, slice_2) = if slice_1.len() > size { 300 + // Contents fits entirely in `slice_1`. 301 + (&mut slice_1[..size], &mut slice_2[0..0]) 302 + } else { 303 + // Need all of `slice_1` and some of `slice_2`. 304 + let slice_2_len = size - slice_1.len(); 305 + (slice_1, &mut slice_2[..slice_2_len]) 306 + }; 307 + 308 + Ok(GspCommand { 309 + header, 310 + contents: (slice_1, slice_2), 311 + }) 312 + } 313 + 314 + // Returns the index of the memory page the GSP will write the next message to. 315 + // 316 + // # Invariants 317 + // 318 + // - The returned value is between `0` and `MSGQ_NUM_PAGES`. 319 + fn gsp_write_ptr(&self) -> u32 { 320 + let gsp_mem = self.0.start_ptr(); 321 + 322 + // SAFETY: 323 + // - The 'CoherentAllocation' contains at least one object. 324 + // - By the invariants of `CoherentAllocation` the pointer is valid. 325 + (unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES) 326 + } 327 + 328 + // Returns the index of the memory page the GSP will read the next command from. 329 + // 330 + // # Invariants 331 + // 332 + // - The returned value is between `0` and `MSGQ_NUM_PAGES`. 333 + fn gsp_read_ptr(&self) -> u32 { 334 + let gsp_mem = self.0.start_ptr(); 335 + 336 + // SAFETY: 337 + // - The 'CoherentAllocation' contains at least one object. 338 + // - By the invariants of `CoherentAllocation` the pointer is valid. 339 + (unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES) 340 + } 341 + 342 + // Returns the index of the memory page the CPU can read the next message from. 343 + // 344 + // # Invariants 345 + // 346 + // - The returned value is between `0` and `MSGQ_NUM_PAGES`. 347 + fn cpu_read_ptr(&self) -> u32 { 348 + let gsp_mem = self.0.start_ptr(); 349 + 350 + // SAFETY: 351 + // - The ['CoherentAllocation'] contains at least one object. 352 + // - By the invariants of CoherentAllocation the pointer is valid. 353 + (unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES) 354 + } 355 + 356 + // Informs the GSP that it can send `elem_count` new pages into the message queue. 357 + fn advance_cpu_read_ptr(&mut self, elem_count: u32) { 358 + let rptr = self.cpu_read_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES; 359 + 360 + // Ensure read pointer is properly ordered. 361 + fence(Ordering::SeqCst); 362 + 363 + let gsp_mem = self.0.start_ptr_mut(); 364 + 365 + // SAFETY: 366 + // - The 'CoherentAllocation' contains at least one object. 367 + // - By the invariants of `CoherentAllocation` the pointer is valid. 368 + unsafe { (*gsp_mem).cpuq.rx.set_read_ptr(rptr) }; 369 + } 370 + 371 + // Returns the index of the memory page the CPU can write the next command to. 372 + // 373 + // # Invariants 374 + // 375 + // - The returned value is between `0` and `MSGQ_NUM_PAGES`. 376 + fn cpu_write_ptr(&self) -> u32 { 377 + let gsp_mem = self.0.start_ptr(); 378 + 379 + // SAFETY: 380 + // - The 'CoherentAllocation' contains at least one object. 381 + // - By the invariants of `CoherentAllocation` the pointer is valid. 382 + (unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES) 383 + } 384 + 385 + // Informs the GSP that it can process `elem_count` new pages from the command queue. 386 + fn advance_cpu_write_ptr(&mut self, elem_count: u32) { 387 + let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES; 388 + let gsp_mem = self.0.start_ptr_mut(); 389 + 390 + // SAFETY: 391 + // - The 'CoherentAllocation' contains at least one object. 392 + // - By the invariants of `CoherentAllocation` the pointer is valid. 393 + unsafe { (*gsp_mem).cpuq.tx.set_write_ptr(wptr) }; 394 + 395 + // Ensure all command data is visible before triggering the GSP read. 396 + fence(Ordering::SeqCst); 397 + } 398 + } 399 + 400 + /// A command ready to be sent on the command queue. 401 + /// 402 + /// This is the type returned by [`DmaGspMem::allocate_command`]. 403 + struct GspCommand<'a> { 404 + // Writable reference to the header of the command. 405 + header: &'a mut GspMsgElement, 406 + // Writable slices to the contents of the command. The second slice is zero unless the command 407 + // loops over the command queue. 408 + contents: (&'a mut [u8], &'a mut [u8]), 409 + } 410 + 411 + /// A message ready to be processed from the message queue. 412 + /// 413 + /// This is the type returned by [`Cmdq::wait_for_msg`]. 414 + struct GspMessage<'a> { 415 + // Reference to the header of the message. 416 + header: &'a GspMsgElement, 417 + // Slices to the contents of the message. The second slice is zero unless the message loops 418 + // over the message queue. 419 + contents: (&'a [u8], &'a [u8]), 420 + } 421 + 422 + /// GSP command queue. 423 + /// 424 + /// Provides the ability to send commands and receive messages from the GSP using a shared memory 425 + /// area. 426 + pub(crate) struct Cmdq { 427 + /// Device this command queue belongs to. 428 + dev: ARef<device::Device>, 429 + /// Current command sequence number. 430 + seq: u32, 431 + /// Memory area shared with the GSP for communicating commands and messages. 432 + gsp_mem: DmaGspMem, 433 + } 434 + 435 + impl Cmdq { 436 + /// Offset of the data after the PTEs. 437 + const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq); 438 + 439 + /// Offset of command queue ring buffer. 440 + pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq) 441 + + core::mem::offset_of!(Msgq, msgq) 442 + - Self::POST_PTE_OFFSET; 443 + 444 + /// Offset of message queue ring buffer. 445 + pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq) 446 + + core::mem::offset_of!(Msgq, msgq) 447 + - Self::POST_PTE_OFFSET; 448 + 449 + /// Number of page table entries for the GSP shared region. 450 + pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT; 451 + 452 + /// Creates a new command queue for `dev`. 453 + pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> { 454 + let gsp_mem = DmaGspMem::new(dev)?; 455 + 456 + Ok(Cmdq { 457 + dev: dev.into(), 458 + seq: 0, 459 + gsp_mem, 460 + }) 461 + } 462 + 463 + /// Computes the checksum for the message pointed to by `it`. 464 + /// 465 + /// A message is made of several parts, so `it` is an iterator over byte slices representing 466 + /// these parts. 467 + fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 { 468 + let sum64 = it 469 + .enumerate() 470 + .map(|(idx, byte)| (((idx % 8) * 8) as u32, byte)) 471 + .fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol)); 472 + 473 + ((sum64 >> 32) as u32) ^ (sum64 as u32) 474 + } 475 + 476 + /// Notifies the GSP that we have updated the command queue pointers. 477 + fn notify_gsp(bar: &Bar0) { 478 + regs::NV_PGSP_QUEUE_HEAD::default() 479 + .set_address(0) 480 + .write(bar); 481 + } 482 + 483 + /// Sends `command` to the GSP. 484 + /// 485 + /// # Errors 486 + /// 487 + /// - `EAGAIN` if there was not enough space in the command queue to send the command. 488 + /// - `EIO` if the variable payload requested by the command has not been entirely 489 + /// written to by its [`CommandToGsp::init_variable_payload`] method. 490 + /// 491 + /// Error codes returned by the command initializers are propagated as-is. 492 + pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result 493 + where 494 + M: CommandToGsp, 495 + // This allows all error types, including `Infallible`, to be used for `M::InitError`. 496 + Error: From<M::InitError>, 497 + { 498 + let command_size = size_of::<M::Command>() + command.variable_payload_len(); 499 + let dst = self.gsp_mem.allocate_command(command_size)?; 500 + 501 + // Extract area for the command itself. 502 + let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?; 503 + 504 + // Fill the header and command in-place. 505 + let msg_element = GspMsgElement::init(self.seq, command_size, M::FUNCTION); 506 + // SAFETY: `msg_header` and `cmd` are valid references, and not touched if the initializer 507 + // fails. 508 + unsafe { 509 + msg_element.__init(core::ptr::from_mut(dst.header))?; 510 + command.init().__init(core::ptr::from_mut(cmd))?; 511 + } 512 + 513 + // Fill the variable-length payload. 514 + if command_size > size_of::<M::Command>() { 515 + let mut sbuffer = 516 + SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]); 517 + command.init_variable_payload(&mut sbuffer)?; 518 + 519 + if !sbuffer.is_empty() { 520 + return Err(EIO); 521 + } 522 + } 523 + 524 + // Compute checksum now that the whole message is ready. 525 + dst.header 526 + .set_checksum(Cmdq::calculate_checksum(SBufferIter::new_reader([ 527 + dst.header.as_bytes(), 528 + dst.contents.0, 529 + dst.contents.1, 530 + ]))); 531 + 532 + dev_dbg!( 533 + &self.dev, 534 + "GSP RPC: send: seq# {}, function={}, length=0x{:x}\n", 535 + self.seq, 536 + M::FUNCTION, 537 + dst.header.length(), 538 + ); 539 + 540 + // All set - update the write pointer and inform the GSP of the new command. 541 + let elem_count = dst.header.element_count(); 542 + self.seq += 1; 543 + self.gsp_mem.advance_cpu_write_ptr(elem_count); 544 + Cmdq::notify_gsp(bar); 545 + 546 + Ok(()) 547 + } 548 + 549 + /// Wait for a message to become available on the message queue. 550 + /// 551 + /// This works purely at the transport layer and does not interpret or validate the message 552 + /// beyond the advertised length in its [`GspMsgElement`]. 553 + /// 554 + /// This method returns: 555 + /// 556 + /// - A reference to the [`GspMsgElement`] of the message, 557 + /// - Two byte slices with the contents of the message. The second slice is empty unless the 558 + /// message loops across the message queue. 559 + /// 560 + /// # Errors 561 + /// 562 + /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available. 563 + /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the 564 + /// message queue. 565 + /// 566 + /// Error codes returned by the message constructor are propagated as-is. 567 + fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> { 568 + // Wait for a message to arrive from the GSP. 569 + let (slice_1, slice_2) = read_poll_timeout( 570 + || Ok(self.gsp_mem.driver_read_area()), 571 + |driver_area| !driver_area.0.is_empty(), 572 + Delta::from_millis(1), 573 + timeout, 574 + ) 575 + .map(|(slice_1, slice_2)| { 576 + #[allow(clippy::incompatible_msrv)] 577 + (slice_1.as_flattened(), slice_2.as_flattened()) 578 + })?; 579 + 580 + // Extract the `GspMsgElement`. 581 + let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?; 582 + 583 + dev_dbg!( 584 + self.dev, 585 + "GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n", 586 + header.sequence(), 587 + header.function(), 588 + header.length(), 589 + ); 590 + 591 + // Check that the driver read area is large enough for the message. 592 + if slice_1.len() + slice_2.len() < header.length() { 593 + return Err(EIO); 594 + } 595 + 596 + // Cut the message slices down to the actual length of the message. 597 + let (slice_1, slice_2) = if slice_1.len() > header.length() { 598 + // PANIC: we checked above that `slice_1` is at least as long as `msg_header.length()`. 599 + (slice_1.split_at(header.length()).0, &slice_2[0..0]) 600 + } else { 601 + ( 602 + slice_1, 603 + // PANIC: we checked above that `slice_1.len() + slice_2.len()` is at least as 604 + // large as `msg_header.length()`. 605 + slice_2.split_at(header.length() - slice_1.len()).0, 606 + ) 607 + }; 608 + 609 + // Validate checksum. 610 + if Cmdq::calculate_checksum(SBufferIter::new_reader([ 611 + header.as_bytes(), 612 + slice_1, 613 + slice_2, 614 + ])) != 0 615 + { 616 + dev_err!( 617 + self.dev, 618 + "GSP RPC: receive: Call {} - bad checksum", 619 + header.sequence() 620 + ); 621 + return Err(EIO); 622 + } 623 + 624 + Ok(GspMessage { 625 + header, 626 + contents: (slice_1, slice_2), 627 + }) 628 + } 629 + 630 + /// Receive a message from the GSP. 631 + /// 632 + /// `init` is a closure tasked with processing the message. It receives a reference to the 633 + /// message in the message queue, and a [`SBufferIter`] pointing to its variable-length 634 + /// payload, if any. 635 + /// 636 + /// The expected message is specified using the `M` generic parameter. If the pending message 637 + /// is different, `EAGAIN` is returned and the unexpected message is dropped. 638 + /// 639 + /// This design is by no means final, but it is simple and will let us go through GSP 640 + /// initialization. 641 + /// 642 + /// # Errors 643 + /// 644 + /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available. 645 + /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the 646 + /// message queue. 647 + /// - `EINVAL` if the function of the message was unrecognized. 648 + pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M> 649 + where 650 + // This allows all error types, including `Infallible`, to be used for `M::InitError`. 651 + Error: From<M::InitError>, 652 + { 653 + let message = self.wait_for_msg(timeout)?; 654 + let function = message.header.function().map_err(|_| EINVAL)?; 655 + 656 + // Extract the message. Store the result as we want to advance the read pointer even in 657 + // case of failure. 658 + let result = if function == M::FUNCTION { 659 + let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?; 660 + let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]); 661 + 662 + M::read(cmd, &mut sbuffer).map_err(|e| e.into()) 663 + } else { 664 + Err(ERANGE) 665 + }; 666 + 667 + // Advance the read pointer past this message. 668 + self.gsp_mem.advance_cpu_read_ptr(u32::try_from( 669 + message.header.length().div_ceil(GSP_PAGE_SIZE), 670 + )?); 671 + 672 + result 673 + } 674 + 675 + /// Returns the DMA handle of the command queue's shared memory region. 676 + pub(crate) fn dma_handle(&self) -> DmaAddress { 677 + self.gsp_mem.0.dma_handle() 678 + } 679 + }
+227
drivers/gpu/nova-core/gsp/commands.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use core::{ 4 + array, 5 + convert::Infallible, // 6 + }; 7 + 8 + use kernel::{ 9 + device, 10 + pci, 11 + prelude::*, 12 + time::Delta, 13 + transmute::{ 14 + AsBytes, 15 + FromBytes, // 16 + }, // 17 + }; 18 + 19 + use crate::{ 20 + driver::Bar0, 21 + gsp::{ 22 + cmdq::{ 23 + Cmdq, 24 + CommandToGsp, 25 + MessageFromGsp, // 26 + }, 27 + fw::{ 28 + commands::*, 29 + MsgFunction, // 30 + }, 31 + }, 32 + sbuffer::SBufferIter, 33 + util, 34 + }; 35 + 36 + /// The `GspSetSystemInfo` command. 37 + pub(crate) struct SetSystemInfo<'a> { 38 + pdev: &'a pci::Device<device::Bound>, 39 + } 40 + 41 + impl<'a> SetSystemInfo<'a> { 42 + /// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`. 43 + pub(crate) fn new(pdev: &'a pci::Device<device::Bound>) -> Self { 44 + Self { pdev } 45 + } 46 + } 47 + 48 + impl<'a> CommandToGsp for SetSystemInfo<'a> { 49 + const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo; 50 + type Command = GspSetSystemInfo; 51 + type InitError = Error; 52 + 53 + fn init(&self) -> impl Init<Self::Command, Self::InitError> { 54 + GspSetSystemInfo::init(self.pdev) 55 + } 56 + } 57 + 58 + struct RegistryEntry { 59 + key: &'static str, 60 + value: u32, 61 + } 62 + 63 + /// The `SetRegistry` command. 64 + pub(crate) struct SetRegistry { 65 + entries: [RegistryEntry; Self::NUM_ENTRIES], 66 + } 67 + 68 + impl SetRegistry { 69 + // For now we hard-code the registry entries. Future work will allow others to 70 + // be added as module parameters. 71 + const NUM_ENTRIES: usize = 3; 72 + 73 + /// Creates a new `SetRegistry` command, using a set of hardcoded entries. 74 + pub(crate) fn new() -> Self { 75 + Self { 76 + entries: [ 77 + // RMSecBusResetEnable - enables PCI secondary bus reset 78 + RegistryEntry { 79 + key: "RMSecBusResetEnable", 80 + value: 1, 81 + }, 82 + // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on 83 + // any PCI reset. 84 + RegistryEntry { 85 + key: "RMForcePcieConfigSave", 86 + value: 1, 87 + }, 88 + // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found 89 + // in the internal product name database. 90 + RegistryEntry { 91 + key: "RMDevidCheckIgnore", 92 + value: 1, 93 + }, 94 + ], 95 + } 96 + } 97 + } 98 + 99 + impl CommandToGsp for SetRegistry { 100 + const FUNCTION: MsgFunction = MsgFunction::SetRegistry; 101 + type Command = PackedRegistryTable; 102 + type InitError = Infallible; 103 + 104 + fn init(&self) -> impl Init<Self::Command, Self::InitError> { 105 + PackedRegistryTable::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32) 106 + } 107 + 108 + fn variable_payload_len(&self) -> usize { 109 + let mut key_size = 0; 110 + for i in 0..Self::NUM_ENTRIES { 111 + key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator 112 + } 113 + Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>() + key_size 114 + } 115 + 116 + fn init_variable_payload( 117 + &self, 118 + dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>, 119 + ) -> Result { 120 + let string_data_start_offset = 121 + size_of::<PackedRegistryTable>() + Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>(); 122 + 123 + // Array for string data. 124 + let mut string_data = KVec::new(); 125 + 126 + for entry in self.entries.iter().take(Self::NUM_ENTRIES) { 127 + dst.write_all( 128 + PackedRegistryEntry::new( 129 + (string_data_start_offset + string_data.len()) as u32, 130 + entry.value, 131 + ) 132 + .as_bytes(), 133 + )?; 134 + 135 + let key_bytes = entry.key.as_bytes(); 136 + string_data.extend_from_slice(key_bytes, GFP_KERNEL)?; 137 + string_data.push(0, GFP_KERNEL)?; 138 + } 139 + 140 + dst.write_all(string_data.as_slice()) 141 + } 142 + } 143 + 144 + /// Message type for GSP initialization done notification. 145 + struct GspInitDone {} 146 + 147 + // SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it 148 + // trivially has no uninitialized bytes. 149 + unsafe impl FromBytes for GspInitDone {} 150 + 151 + impl MessageFromGsp for GspInitDone { 152 + const FUNCTION: MsgFunction = MsgFunction::GspInitDone; 153 + type InitError = Infallible; 154 + type Message = GspInitDone; 155 + 156 + fn read( 157 + _msg: &Self::Message, 158 + _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>, 159 + ) -> Result<Self, Self::InitError> { 160 + Ok(GspInitDone {}) 161 + } 162 + } 163 + 164 + /// Waits for GSP initialization to complete. 165 + pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result { 166 + loop { 167 + match cmdq.receive_msg::<GspInitDone>(Delta::from_secs(10)) { 168 + Ok(_) => break Ok(()), 169 + Err(ERANGE) => continue, 170 + Err(e) => break Err(e), 171 + } 172 + } 173 + } 174 + 175 + /// The `GetGspStaticInfo` command. 176 + struct GetGspStaticInfo; 177 + 178 + impl CommandToGsp for GetGspStaticInfo { 179 + const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo; 180 + type Command = GspStaticConfigInfo; 181 + type InitError = Infallible; 182 + 183 + fn init(&self) -> impl Init<Self::Command, Self::InitError> { 184 + GspStaticConfigInfo::init_zeroed() 185 + } 186 + } 187 + 188 + /// The reply from the GSP to the [`GetGspInfo`] command. 189 + pub(crate) struct GetGspStaticInfoReply { 190 + gpu_name: [u8; 64], 191 + } 192 + 193 + impl MessageFromGsp for GetGspStaticInfoReply { 194 + const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo; 195 + type Message = GspStaticConfigInfo; 196 + type InitError = Infallible; 197 + 198 + fn read( 199 + msg: &Self::Message, 200 + _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>, 201 + ) -> Result<Self, Self::InitError> { 202 + Ok(GetGspStaticInfoReply { 203 + gpu_name: msg.gpu_name_str(), 204 + }) 205 + } 206 + } 207 + 208 + impl GetGspStaticInfoReply { 209 + /// Returns the name of the GPU as a string, or `None` if the string given by the GSP was 210 + /// invalid. 211 + pub(crate) fn gpu_name(&self) -> Option<&str> { 212 + util::str_from_null_terminated(&self.gpu_name) 213 + } 214 + } 215 + 216 + /// Send the [`GetGspInfo`] command and awaits for its reply. 217 + pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> { 218 + cmdq.send_command(bar, GetGspStaticInfo)?; 219 + 220 + loop { 221 + match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) { 222 + Ok(info) => return Ok(info), 223 + Err(ERANGE) => continue, 224 + Err(e) => return Err(e), 225 + } 226 + } 227 + }
+922 -1
drivers/gpu/nova-core/gsp/fw.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 + pub(crate) mod commands; 3 4 mod r570_144; 4 5 5 6 // Alias to avoid repeating the version number with every use. 6 - #[expect(unused)] 7 7 use r570_144 as bindings; 8 + 9 + use core::ops::Range; 10 + 11 + use kernel::{ 12 + dma::CoherentAllocation, 13 + fmt, 14 + prelude::*, 15 + ptr::{ 16 + Alignable, 17 + Alignment, // 18 + }, 19 + sizes::{ 20 + SZ_128K, 21 + SZ_1M, // 22 + }, 23 + transmute::{ 24 + AsBytes, 25 + FromBytes, // 26 + }, 27 + }; 28 + 29 + use crate::{ 30 + fb::FbLayout, 31 + firmware::gsp::GspFirmware, 32 + gpu::Chipset, 33 + gsp::{ 34 + cmdq::Cmdq, // 35 + GSP_PAGE_SIZE, 36 + }, 37 + num::{ 38 + self, 39 + FromSafeCast, // 40 + }, 41 + }; 42 + 43 + /// Empty type to group methods related to heap parameters for running the GSP firmware. 44 + enum GspFwHeapParams {} 45 + 46 + /// Minimum required alignment for the GSP heap. 47 + const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>(); 48 + 49 + impl GspFwHeapParams { 50 + /// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to 51 + /// and including the first client subdevice allocation). 52 + fn base_rm_size(_chipset: Chipset) -> u64 { 53 + // TODO: this needs to be updated to return the correct value for Hopper+ once support for 54 + // them is added: 55 + // u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100) 56 + u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X) 57 + } 58 + 59 + /// Returns the amount of heap memory required to support a single channel allocation. 60 + fn client_alloc_size() -> u64 { 61 + u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE) 62 + .align_up(GSP_HEAP_ALIGNMENT) 63 + .unwrap_or(u64::MAX) 64 + } 65 + 66 + /// Returns the amount of memory to reserve for management purposes for a framebuffer of size 67 + /// `fb_size`. 68 + fn management_overhead(fb_size: u64) -> u64 { 69 + let fb_size_gb = fb_size.div_ceil(u64::from_safe_cast(kernel::sizes::SZ_1G)); 70 + 71 + u64::from(bindings::GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB) 72 + .saturating_mul(fb_size_gb) 73 + .align_up(GSP_HEAP_ALIGNMENT) 74 + .unwrap_or(u64::MAX) 75 + } 76 + } 77 + 78 + /// Heap memory requirements and constraints for a given version of the GSP LIBOS. 79 + pub(crate) struct LibosParams { 80 + /// The base amount of heap required by the GSP operating system, in bytes. 81 + carveout_size: u64, 82 + /// The minimum and maximum sizes allowed for the GSP FW heap, in bytes. 83 + allowed_heap_size: Range<u64>, 84 + } 85 + 86 + impl LibosParams { 87 + /// Version 2 of the GSP LIBOS (Turing and GA100) 88 + const LIBOS2: LibosParams = LibosParams { 89 + carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2), 90 + allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB) 91 + * num::usize_as_u64(SZ_1M) 92 + ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB) 93 + * num::usize_as_u64(SZ_1M), 94 + }; 95 + 96 + /// Version 3 of the GSP LIBOS (GA102+) 97 + const LIBOS3: LibosParams = LibosParams { 98 + carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL), 99 + allowed_heap_size: num::u32_as_u64( 100 + bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB, 101 + ) * num::usize_as_u64(SZ_1M) 102 + ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB) 103 + * num::usize_as_u64(SZ_1M), 104 + }; 105 + 106 + /// Returns the libos parameters corresponding to `chipset`. 107 + pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams { 108 + if chipset < Chipset::GA102 { 109 + &Self::LIBOS2 110 + } else { 111 + &Self::LIBOS3 112 + } 113 + } 114 + 115 + /// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size 116 + /// of `fb_size` (in bytes) for `chipset`. 117 + pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 { 118 + // The WPR heap will contain the following: 119 + // LIBOS carveout, 120 + self.carveout_size 121 + // RM boot working memory, 122 + .saturating_add(GspFwHeapParams::base_rm_size(chipset)) 123 + // One RM client, 124 + .saturating_add(GspFwHeapParams::client_alloc_size()) 125 + // Overhead for memory management. 126 + .saturating_add(GspFwHeapParams::management_overhead(fb_size)) 127 + // Clamp to the supported heap sizes. 128 + .clamp(self.allowed_heap_size.start, self.allowed_heap_size.end - 1) 129 + } 130 + } 131 + 132 + /// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA 133 + /// addresses of the GSP bootloader and firmware. 134 + #[repr(transparent)] 135 + pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta); 136 + 137 + // SAFETY: Padding is explicit and does not contain uninitialized data. 138 + unsafe impl AsBytes for GspFwWprMeta {} 139 + 140 + // SAFETY: This struct only contains integer types for which all bit patterns 141 + // are valid. 142 + unsafe impl FromBytes for GspFwWprMeta {} 143 + 144 + type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1; 145 + type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1; 146 + 147 + impl GspFwWprMeta { 148 + /// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the 149 + /// `fb_layout` layout. 150 + pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self { 151 + Self(bindings::GspFwWprMeta { 152 + // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified. 153 + magic: r570_144::GSP_FW_WPR_META_MAGIC as u64, 154 + revision: u64::from(r570_144::GSP_FW_WPR_META_REVISION), 155 + sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(), 156 + sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size), 157 + sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(), 158 + sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()), 159 + bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset), 160 + bootloaderDataOffset: u64::from(gsp_firmware.bootloader.data_offset), 161 + bootloaderManifestOffset: u64::from(gsp_firmware.bootloader.manifest_offset), 162 + __bindgen_anon_1: GspFwWprMetaBootResumeInfo { 163 + __bindgen_anon_1: GspFwWprMetaBootInfo { 164 + sysmemAddrOfSignature: gsp_firmware.signatures.dma_handle(), 165 + sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()), 166 + }, 167 + }, 168 + gspFwRsvdStart: fb_layout.heap.start, 169 + nonWprHeapOffset: fb_layout.heap.start, 170 + nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start, 171 + gspFwWprStart: fb_layout.wpr2.start, 172 + gspFwHeapOffset: fb_layout.wpr2_heap.start, 173 + gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start, 174 + gspFwOffset: fb_layout.elf.start, 175 + bootBinOffset: fb_layout.boot.start, 176 + frtsOffset: fb_layout.frts.start, 177 + frtsSize: fb_layout.frts.end - fb_layout.frts.start, 178 + gspFwWprEnd: fb_layout 179 + .vga_workspace 180 + .start 181 + .align_down(Alignment::new::<SZ_128K>()), 182 + gspFwHeapVfPartitionCount: fb_layout.vf_partition_count, 183 + fbSize: fb_layout.fb.end - fb_layout.fb.start, 184 + vgaWorkspaceOffset: fb_layout.vga_workspace.start, 185 + vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start, 186 + ..Default::default() 187 + }) 188 + } 189 + } 190 + 191 + #[derive(Copy, Clone, Debug, PartialEq)] 192 + #[repr(u32)] 193 + pub(crate) enum MsgFunction { 194 + // Common function codes 195 + Nop = bindings::NV_VGPU_MSG_FUNCTION_NOP, 196 + SetGuestSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO, 197 + AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT, 198 + AllocDevice = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE, 199 + AllocMemory = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY, 200 + AllocCtxDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA, 201 + AllocChannelDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA, 202 + MapMemory = bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY, 203 + BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA, 204 + AllocObject = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT, 205 + Free = bindings::NV_VGPU_MSG_FUNCTION_FREE, 206 + Log = bindings::NV_VGPU_MSG_FUNCTION_LOG, 207 + GetGspStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO, 208 + SetRegistry = bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY, 209 + GspSetSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO, 210 + GspInitPostObjGpu = bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU, 211 + GspRmControl = bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL, 212 + GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO, 213 + 214 + // Event codes 215 + GspInitDone = bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE, 216 + GspRunCpuSequencer = bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER, 217 + PostEvent = bindings::NV_VGPU_MSG_EVENT_POST_EVENT, 218 + RcTriggered = bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED, 219 + MmuFaultQueued = bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED, 220 + OsErrorLog = bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG, 221 + GspPostNoCat = bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD, 222 + GspLockdownNotice = bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE, 223 + UcodeLibOsPrint = bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT, 224 + } 225 + 226 + impl fmt::Display for MsgFunction { 227 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 228 + match self { 229 + // Common function codes 230 + MsgFunction::Nop => write!(f, "NOP"), 231 + MsgFunction::SetGuestSystemInfo => write!(f, "SET_GUEST_SYSTEM_INFO"), 232 + MsgFunction::AllocRoot => write!(f, "ALLOC_ROOT"), 233 + MsgFunction::AllocDevice => write!(f, "ALLOC_DEVICE"), 234 + MsgFunction::AllocMemory => write!(f, "ALLOC_MEMORY"), 235 + MsgFunction::AllocCtxDma => write!(f, "ALLOC_CTX_DMA"), 236 + MsgFunction::AllocChannelDma => write!(f, "ALLOC_CHANNEL_DMA"), 237 + MsgFunction::MapMemory => write!(f, "MAP_MEMORY"), 238 + MsgFunction::BindCtxDma => write!(f, "BIND_CTX_DMA"), 239 + MsgFunction::AllocObject => write!(f, "ALLOC_OBJECT"), 240 + MsgFunction::Free => write!(f, "FREE"), 241 + MsgFunction::Log => write!(f, "LOG"), 242 + MsgFunction::GetGspStaticInfo => write!(f, "GET_GSP_STATIC_INFO"), 243 + MsgFunction::SetRegistry => write!(f, "SET_REGISTRY"), 244 + MsgFunction::GspSetSystemInfo => write!(f, "GSP_SET_SYSTEM_INFO"), 245 + MsgFunction::GspInitPostObjGpu => write!(f, "GSP_INIT_POST_OBJGPU"), 246 + MsgFunction::GspRmControl => write!(f, "GSP_RM_CONTROL"), 247 + MsgFunction::GetStaticInfo => write!(f, "GET_STATIC_INFO"), 248 + 249 + // Event codes 250 + MsgFunction::GspInitDone => write!(f, "INIT_DONE"), 251 + MsgFunction::GspRunCpuSequencer => write!(f, "RUN_CPU_SEQUENCER"), 252 + MsgFunction::PostEvent => write!(f, "POST_EVENT"), 253 + MsgFunction::RcTriggered => write!(f, "RC_TRIGGERED"), 254 + MsgFunction::MmuFaultQueued => write!(f, "MMU_FAULT_QUEUED"), 255 + MsgFunction::OsErrorLog => write!(f, "OS_ERROR_LOG"), 256 + MsgFunction::GspPostNoCat => write!(f, "NOCAT"), 257 + MsgFunction::GspLockdownNotice => write!(f, "LOCKDOWN_NOTICE"), 258 + MsgFunction::UcodeLibOsPrint => write!(f, "LIBOS_PRINT"), 259 + } 260 + } 261 + } 262 + 263 + impl TryFrom<u32> for MsgFunction { 264 + type Error = kernel::error::Error; 265 + 266 + fn try_from(value: u32) -> Result<MsgFunction> { 267 + match value { 268 + bindings::NV_VGPU_MSG_FUNCTION_NOP => Ok(MsgFunction::Nop), 269 + bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => { 270 + Ok(MsgFunction::SetGuestSystemInfo) 271 + } 272 + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot), 273 + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE => Ok(MsgFunction::AllocDevice), 274 + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY => Ok(MsgFunction::AllocMemory), 275 + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => Ok(MsgFunction::AllocCtxDma), 276 + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => Ok(MsgFunction::AllocChannelDma), 277 + bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY => Ok(MsgFunction::MapMemory), 278 + bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma), 279 + bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => Ok(MsgFunction::AllocObject), 280 + bindings::NV_VGPU_MSG_FUNCTION_FREE => Ok(MsgFunction::Free), 281 + bindings::NV_VGPU_MSG_FUNCTION_LOG => Ok(MsgFunction::Log), 282 + bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => Ok(MsgFunction::GetGspStaticInfo), 283 + bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY => Ok(MsgFunction::SetRegistry), 284 + bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => Ok(MsgFunction::GspSetSystemInfo), 285 + bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU => { 286 + Ok(MsgFunction::GspInitPostObjGpu) 287 + } 288 + bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL => Ok(MsgFunction::GspRmControl), 289 + bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo), 290 + bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE => Ok(MsgFunction::GspInitDone), 291 + bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER => { 292 + Ok(MsgFunction::GspRunCpuSequencer) 293 + } 294 + bindings::NV_VGPU_MSG_EVENT_POST_EVENT => Ok(MsgFunction::PostEvent), 295 + bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED => Ok(MsgFunction::RcTriggered), 296 + bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED => Ok(MsgFunction::MmuFaultQueued), 297 + bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG => Ok(MsgFunction::OsErrorLog), 298 + bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => Ok(MsgFunction::GspPostNoCat), 299 + bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => Ok(MsgFunction::GspLockdownNotice), 300 + bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT => Ok(MsgFunction::UcodeLibOsPrint), 301 + _ => Err(EINVAL), 302 + } 303 + } 304 + } 305 + 306 + impl From<MsgFunction> for u32 { 307 + fn from(value: MsgFunction) -> Self { 308 + // CAST: `MsgFunction` is `repr(u32)` and can thus be cast losslessly. 309 + value as u32 310 + } 311 + } 312 + 313 + /// Sequencer buffer opcode for GSP sequencer commands. 314 + #[derive(Copy, Clone, Debug, PartialEq)] 315 + #[repr(u32)] 316 + pub(crate) enum SeqBufOpcode { 317 + // Core operation opcodes 318 + CoreReset = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET, 319 + CoreResume = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME, 320 + CoreStart = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START, 321 + CoreWaitForHalt = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT, 322 + 323 + // Delay opcode 324 + DelayUs = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US, 325 + 326 + // Register operation opcodes 327 + RegModify = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY, 328 + RegPoll = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL, 329 + RegStore = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE, 330 + RegWrite = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE, 331 + } 332 + 333 + impl fmt::Display for SeqBufOpcode { 334 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 335 + match self { 336 + SeqBufOpcode::CoreReset => write!(f, "CORE_RESET"), 337 + SeqBufOpcode::CoreResume => write!(f, "CORE_RESUME"), 338 + SeqBufOpcode::CoreStart => write!(f, "CORE_START"), 339 + SeqBufOpcode::CoreWaitForHalt => write!(f, "CORE_WAIT_FOR_HALT"), 340 + SeqBufOpcode::DelayUs => write!(f, "DELAY_US"), 341 + SeqBufOpcode::RegModify => write!(f, "REG_MODIFY"), 342 + SeqBufOpcode::RegPoll => write!(f, "REG_POLL"), 343 + SeqBufOpcode::RegStore => write!(f, "REG_STORE"), 344 + SeqBufOpcode::RegWrite => write!(f, "REG_WRITE"), 345 + } 346 + } 347 + } 348 + 349 + impl TryFrom<u32> for SeqBufOpcode { 350 + type Error = kernel::error::Error; 351 + 352 + fn try_from(value: u32) -> Result<SeqBufOpcode> { 353 + match value { 354 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET => { 355 + Ok(SeqBufOpcode::CoreReset) 356 + } 357 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME => { 358 + Ok(SeqBufOpcode::CoreResume) 359 + } 360 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START => { 361 + Ok(SeqBufOpcode::CoreStart) 362 + } 363 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT => { 364 + Ok(SeqBufOpcode::CoreWaitForHalt) 365 + } 366 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US => Ok(SeqBufOpcode::DelayUs), 367 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY => { 368 + Ok(SeqBufOpcode::RegModify) 369 + } 370 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL => Ok(SeqBufOpcode::RegPoll), 371 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE => Ok(SeqBufOpcode::RegStore), 372 + r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE => Ok(SeqBufOpcode::RegWrite), 373 + _ => Err(EINVAL), 374 + } 375 + } 376 + } 377 + 378 + impl From<SeqBufOpcode> for u32 { 379 + fn from(value: SeqBufOpcode) -> Self { 380 + // CAST: `SeqBufOpcode` is `repr(u32)` and can thus be cast losslessly. 381 + value as u32 382 + } 383 + } 384 + 385 + /// Wrapper for GSP sequencer register write payload. 386 + #[repr(transparent)] 387 + #[derive(Copy, Clone)] 388 + pub(crate) struct RegWritePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_WRITE); 389 + 390 + impl RegWritePayload { 391 + /// Returns the register address. 392 + pub(crate) fn addr(&self) -> u32 { 393 + self.0.addr 394 + } 395 + 396 + /// Returns the value to write. 397 + pub(crate) fn val(&self) -> u32 { 398 + self.0.val 399 + } 400 + } 401 + 402 + // SAFETY: This struct only contains integer types for which all bit patterns are valid. 403 + unsafe impl FromBytes for RegWritePayload {} 404 + 405 + // SAFETY: Padding is explicit and will not contain uninitialized data. 406 + unsafe impl AsBytes for RegWritePayload {} 407 + 408 + /// Wrapper for GSP sequencer register modify payload. 409 + #[repr(transparent)] 410 + #[derive(Copy, Clone)] 411 + pub(crate) struct RegModifyPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY); 412 + 413 + impl RegModifyPayload { 414 + /// Returns the register address. 415 + pub(crate) fn addr(&self) -> u32 { 416 + self.0.addr 417 + } 418 + 419 + /// Returns the mask to apply. 420 + pub(crate) fn mask(&self) -> u32 { 421 + self.0.mask 422 + } 423 + 424 + /// Returns the value to write. 425 + pub(crate) fn val(&self) -> u32 { 426 + self.0.val 427 + } 428 + } 429 + 430 + // SAFETY: This struct only contains integer types for which all bit patterns are valid. 431 + unsafe impl FromBytes for RegModifyPayload {} 432 + 433 + // SAFETY: Padding is explicit and will not contain uninitialized data. 434 + unsafe impl AsBytes for RegModifyPayload {} 435 + 436 + /// Wrapper for GSP sequencer register poll payload. 437 + #[repr(transparent)] 438 + #[derive(Copy, Clone)] 439 + pub(crate) struct RegPollPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_POLL); 440 + 441 + impl RegPollPayload { 442 + /// Returns the register address. 443 + pub(crate) fn addr(&self) -> u32 { 444 + self.0.addr 445 + } 446 + 447 + /// Returns the mask to apply. 448 + pub(crate) fn mask(&self) -> u32 { 449 + self.0.mask 450 + } 451 + 452 + /// Returns the expected value. 453 + pub(crate) fn val(&self) -> u32 { 454 + self.0.val 455 + } 456 + 457 + /// Returns the timeout in microseconds. 458 + pub(crate) fn timeout(&self) -> u32 { 459 + self.0.timeout 460 + } 461 + } 462 + 463 + // SAFETY: This struct only contains integer types for which all bit patterns are valid. 464 + unsafe impl FromBytes for RegPollPayload {} 465 + 466 + // SAFETY: Padding is explicit and will not contain uninitialized data. 467 + unsafe impl AsBytes for RegPollPayload {} 468 + 469 + /// Wrapper for GSP sequencer delay payload. 470 + #[repr(transparent)] 471 + #[derive(Copy, Clone)] 472 + pub(crate) struct DelayUsPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_DELAY_US); 473 + 474 + impl DelayUsPayload { 475 + /// Returns the delay value in microseconds. 476 + pub(crate) fn val(&self) -> u32 { 477 + self.0.val 478 + } 479 + } 480 + 481 + // SAFETY: This struct only contains integer types for which all bit patterns are valid. 482 + unsafe impl FromBytes for DelayUsPayload {} 483 + 484 + // SAFETY: Padding is explicit and will not contain uninitialized data. 485 + unsafe impl AsBytes for DelayUsPayload {} 486 + 487 + /// Wrapper for GSP sequencer register store payload. 488 + #[repr(transparent)] 489 + #[derive(Copy, Clone)] 490 + pub(crate) struct RegStorePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_STORE); 491 + 492 + impl RegStorePayload { 493 + /// Returns the register address. 494 + pub(crate) fn addr(&self) -> u32 { 495 + self.0.addr 496 + } 497 + 498 + /// Returns the storage index. 499 + #[allow(unused)] 500 + pub(crate) fn index(&self) -> u32 { 501 + self.0.index 502 + } 503 + } 504 + 505 + // SAFETY: This struct only contains integer types for which all bit patterns are valid. 506 + unsafe impl FromBytes for RegStorePayload {} 507 + 508 + // SAFETY: Padding is explicit and will not contain uninitialized data. 509 + unsafe impl AsBytes for RegStorePayload {} 510 + 511 + /// Wrapper for GSP sequencer buffer command. 512 + #[repr(transparent)] 513 + pub(crate) struct SequencerBufferCmd(r570_144::GSP_SEQUENCER_BUFFER_CMD); 514 + 515 + impl SequencerBufferCmd { 516 + /// Returns the opcode as a `SeqBufOpcode` enum, or error if invalid. 517 + pub(crate) fn opcode(&self) -> Result<SeqBufOpcode> { 518 + self.0.opCode.try_into() 519 + } 520 + 521 + /// Returns the register write payload by value. 522 + /// 523 + /// Returns an error if the opcode is not `SeqBufOpcode::RegWrite`. 524 + pub(crate) fn reg_write_payload(&self) -> Result<RegWritePayload> { 525 + if self.opcode()? != SeqBufOpcode::RegWrite { 526 + return Err(EINVAL); 527 + } 528 + // SAFETY: Opcode is verified to be `RegWrite`, so union contains valid `RegWritePayload`. 529 + let payload_bytes = unsafe { 530 + core::slice::from_raw_parts( 531 + core::ptr::addr_of!(self.0.payload.regWrite).cast::<u8>(), 532 + core::mem::size_of::<RegWritePayload>(), 533 + ) 534 + }; 535 + Ok(*RegWritePayload::from_bytes(payload_bytes).ok_or(EINVAL)?) 536 + } 537 + 538 + /// Returns the register modify payload by value. 539 + /// 540 + /// Returns an error if the opcode is not `SeqBufOpcode::RegModify`. 541 + pub(crate) fn reg_modify_payload(&self) -> Result<RegModifyPayload> { 542 + if self.opcode()? != SeqBufOpcode::RegModify { 543 + return Err(EINVAL); 544 + } 545 + // SAFETY: Opcode is verified to be `RegModify`, so union contains valid `RegModifyPayload`. 546 + let payload_bytes = unsafe { 547 + core::slice::from_raw_parts( 548 + core::ptr::addr_of!(self.0.payload.regModify).cast::<u8>(), 549 + core::mem::size_of::<RegModifyPayload>(), 550 + ) 551 + }; 552 + Ok(*RegModifyPayload::from_bytes(payload_bytes).ok_or(EINVAL)?) 553 + } 554 + 555 + /// Returns the register poll payload by value. 556 + /// 557 + /// Returns an error if the opcode is not `SeqBufOpcode::RegPoll`. 558 + pub(crate) fn reg_poll_payload(&self) -> Result<RegPollPayload> { 559 + if self.opcode()? != SeqBufOpcode::RegPoll { 560 + return Err(EINVAL); 561 + } 562 + // SAFETY: Opcode is verified to be `RegPoll`, so union contains valid `RegPollPayload`. 563 + let payload_bytes = unsafe { 564 + core::slice::from_raw_parts( 565 + core::ptr::addr_of!(self.0.payload.regPoll).cast::<u8>(), 566 + core::mem::size_of::<RegPollPayload>(), 567 + ) 568 + }; 569 + Ok(*RegPollPayload::from_bytes(payload_bytes).ok_or(EINVAL)?) 570 + } 571 + 572 + /// Returns the delay payload by value. 573 + /// 574 + /// Returns an error if the opcode is not `SeqBufOpcode::DelayUs`. 575 + pub(crate) fn delay_us_payload(&self) -> Result<DelayUsPayload> { 576 + if self.opcode()? != SeqBufOpcode::DelayUs { 577 + return Err(EINVAL); 578 + } 579 + // SAFETY: Opcode is verified to be `DelayUs`, so union contains valid `DelayUsPayload`. 580 + let payload_bytes = unsafe { 581 + core::slice::from_raw_parts( 582 + core::ptr::addr_of!(self.0.payload.delayUs).cast::<u8>(), 583 + core::mem::size_of::<DelayUsPayload>(), 584 + ) 585 + }; 586 + Ok(*DelayUsPayload::from_bytes(payload_bytes).ok_or(EINVAL)?) 587 + } 588 + 589 + /// Returns the register store payload by value. 590 + /// 591 + /// Returns an error if the opcode is not `SeqBufOpcode::RegStore`. 592 + pub(crate) fn reg_store_payload(&self) -> Result<RegStorePayload> { 593 + if self.opcode()? != SeqBufOpcode::RegStore { 594 + return Err(EINVAL); 595 + } 596 + // SAFETY: Opcode is verified to be `RegStore`, so union contains valid `RegStorePayload`. 597 + let payload_bytes = unsafe { 598 + core::slice::from_raw_parts( 599 + core::ptr::addr_of!(self.0.payload.regStore).cast::<u8>(), 600 + core::mem::size_of::<RegStorePayload>(), 601 + ) 602 + }; 603 + Ok(*RegStorePayload::from_bytes(payload_bytes).ok_or(EINVAL)?) 604 + } 605 + } 606 + 607 + // SAFETY: This struct only contains integer types for which all bit patterns are valid. 608 + unsafe impl FromBytes for SequencerBufferCmd {} 609 + 610 + // SAFETY: Padding is explicit and will not contain uninitialized data. 611 + unsafe impl AsBytes for SequencerBufferCmd {} 612 + 613 + /// Wrapper for GSP run CPU sequencer RPC. 614 + #[repr(transparent)] 615 + pub(crate) struct RunCpuSequencer(r570_144::rpc_run_cpu_sequencer_v17_00); 616 + 617 + impl RunCpuSequencer { 618 + /// Returns the command index. 619 + pub(crate) fn cmd_index(&self) -> u32 { 620 + self.0.cmdIndex 621 + } 622 + } 623 + 624 + // SAFETY: This struct only contains integer types for which all bit patterns are valid. 625 + unsafe impl FromBytes for RunCpuSequencer {} 626 + 627 + // SAFETY: Padding is explicit and will not contain uninitialized data. 628 + unsafe impl AsBytes for RunCpuSequencer {} 629 + 630 + /// Struct containing the arguments required to pass a memory buffer to the GSP 631 + /// for use during initialisation. 632 + /// 633 + /// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is 634 + /// configured for a larger page size (e.g. 64K pages), we need to give 635 + /// the GSP an array of 4K pages. Since we only create physically contiguous 636 + /// buffers the math to calculate the addresses is simple. 637 + /// 638 + /// The buffers must be a multiple of GSP_PAGE_SIZE. GSP-RM also currently 639 + /// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the 640 + /// buffers to be physically contiguous anyway. 641 + /// 642 + /// The memory allocated for the arguments must remain until the GSP sends the 643 + /// init_done RPC. 644 + #[repr(transparent)] 645 + pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument); 646 + 647 + // SAFETY: Padding is explicit and does not contain uninitialized data. 648 + unsafe impl AsBytes for LibosMemoryRegionInitArgument {} 649 + 650 + // SAFETY: This struct only contains integer types for which all bit patterns 651 + // are valid. 652 + unsafe impl FromBytes for LibosMemoryRegionInitArgument {} 653 + 654 + impl LibosMemoryRegionInitArgument { 655 + pub(crate) fn new<A: AsBytes + FromBytes>( 656 + name: &'static str, 657 + obj: &CoherentAllocation<A>, 658 + ) -> Self { 659 + /// Generates the `ID8` identifier required for some GSP objects. 660 + fn id8(name: &str) -> u64 { 661 + let mut bytes = [0u8; core::mem::size_of::<u64>()]; 662 + 663 + for (c, b) in name.bytes().rev().zip(&mut bytes) { 664 + *b = c; 665 + } 666 + 667 + u64::from_ne_bytes(bytes) 668 + } 669 + 670 + Self(bindings::LibosMemoryRegionInitArgument { 671 + id8: id8(name), 672 + pa: obj.dma_handle(), 673 + size: num::usize_as_u64(obj.size()), 674 + kind: num::u32_into_u8::< 675 + { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS }, 676 + >(), 677 + loc: num::u32_into_u8::< 678 + { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM }, 679 + >(), 680 + ..Default::default() 681 + }) 682 + } 683 + } 684 + 685 + /// TX header for setting up a message queue with the GSP. 686 + #[repr(transparent)] 687 + pub(crate) struct MsgqTxHeader(bindings::msgqTxHeader); 688 + 689 + impl MsgqTxHeader { 690 + /// Create a new TX queue header. 691 + /// 692 + /// # Arguments 693 + /// 694 + /// * `msgq_size` - Total size of the message queue structure, in bytes. 695 + /// * `rx_hdr_offset` - Offset, in bytes, of the start of the RX header in the message queue 696 + /// structure. 697 + /// * `msg_count` - Number of messages that can be sent, i.e. the number of memory pages 698 + /// allocated for the message queue in the message queue structure. 699 + pub(crate) fn new(msgq_size: u32, rx_hdr_offset: u32, msg_count: u32) -> Self { 700 + Self(bindings::msgqTxHeader { 701 + version: 0, 702 + size: msgq_size, 703 + msgSize: num::usize_into_u32::<GSP_PAGE_SIZE>(), 704 + msgCount: msg_count, 705 + writePtr: 0, 706 + flags: 1, 707 + rxHdrOff: rx_hdr_offset, 708 + entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(), 709 + }) 710 + } 711 + 712 + /// Returns the value of the write pointer for this queue. 713 + pub(crate) fn write_ptr(&self) -> u32 { 714 + let ptr = core::ptr::from_ref(&self.0.writePtr); 715 + 716 + // SAFETY: `ptr` is a valid pointer to a `u32`. 717 + unsafe { ptr.read_volatile() } 718 + } 719 + 720 + /// Sets the value of the write pointer for this queue. 721 + pub(crate) fn set_write_ptr(&mut self, val: u32) { 722 + let ptr = core::ptr::from_mut(&mut self.0.writePtr); 723 + 724 + // SAFETY: `ptr` is a valid pointer to a `u32`. 725 + unsafe { ptr.write_volatile(val) } 726 + } 727 + } 728 + 729 + // SAFETY: Padding is explicit and does not contain uninitialized data. 730 + unsafe impl AsBytes for MsgqTxHeader {} 731 + 732 + /// RX header for setting up a message queue with the GSP. 733 + #[repr(transparent)] 734 + pub(crate) struct MsgqRxHeader(bindings::msgqRxHeader); 735 + 736 + /// Header for the message RX queue. 737 + impl MsgqRxHeader { 738 + /// Creates a new RX queue header. 739 + pub(crate) fn new() -> Self { 740 + Self(Default::default()) 741 + } 742 + 743 + /// Returns the value of the read pointer for this queue. 744 + pub(crate) fn read_ptr(&self) -> u32 { 745 + let ptr = core::ptr::from_ref(&self.0.readPtr); 746 + 747 + // SAFETY: `ptr` is a valid pointer to a `u32`. 748 + unsafe { ptr.read_volatile() } 749 + } 750 + 751 + /// Sets the value of the read pointer for this queue. 752 + pub(crate) fn set_read_ptr(&mut self, val: u32) { 753 + let ptr = core::ptr::from_mut(&mut self.0.readPtr); 754 + 755 + // SAFETY: `ptr` is a valid pointer to a `u32`. 756 + unsafe { ptr.write_volatile(val) } 757 + } 758 + } 759 + 760 + // SAFETY: Padding is explicit and does not contain uninitialized data. 761 + unsafe impl AsBytes for MsgqRxHeader {} 762 + 763 + bitfield! { 764 + struct MsgHeaderVersion(u32) { 765 + 31:24 major as u8; 766 + 23:16 minor as u8; 767 + } 768 + } 769 + 770 + impl MsgHeaderVersion { 771 + const MAJOR_TOT: u8 = 3; 772 + const MINOR_TOT: u8 = 0; 773 + 774 + fn new() -> Self { 775 + Self::default() 776 + .set_major(Self::MAJOR_TOT) 777 + .set_minor(Self::MINOR_TOT) 778 + } 779 + } 780 + 781 + impl bindings::rpc_message_header_v { 782 + fn init(cmd_size: usize, function: MsgFunction) -> impl Init<Self, Error> { 783 + type RpcMessageHeader = bindings::rpc_message_header_v; 784 + 785 + try_init!(RpcMessageHeader { 786 + header_version: MsgHeaderVersion::new().into(), 787 + signature: bindings::NV_VGPU_MSG_SIGNATURE_VALID, 788 + function: function.into(), 789 + length: size_of::<Self>() 790 + .checked_add(cmd_size) 791 + .ok_or(EOVERFLOW) 792 + .and_then(|v| v.try_into().map_err(|_| EINVAL))?, 793 + rpc_result: 0xffffffff, 794 + rpc_result_private: 0xffffffff, 795 + ..Zeroable::init_zeroed() 796 + }) 797 + } 798 + } 799 + 800 + // SAFETY: We can't derive the Zeroable trait for this binding because the 801 + // procedural macro doesn't support the syntax used by bindgen to create the 802 + // __IncompleteArrayField types. So instead we implement it here, which is safe 803 + // because these are explicitly padded structures only containing types for 804 + // which any bit pattern, including all zeros, is valid. 805 + unsafe impl Zeroable for bindings::rpc_message_header_v {} 806 + 807 + /// GSP Message Element. 808 + /// 809 + /// This is essentially a message header expected to be followed by the message data. 810 + #[repr(transparent)] 811 + pub(crate) struct GspMsgElement { 812 + inner: bindings::GSP_MSG_QUEUE_ELEMENT, 813 + } 814 + 815 + impl GspMsgElement { 816 + /// Creates a new message element. 817 + /// 818 + /// # Arguments 819 + /// 820 + /// * `sequence` - Sequence number of the message. 821 + /// * `cmd_size` - Size of the command (not including the message element), in bytes. 822 + /// * `function` - Function of the message. 823 + #[allow(non_snake_case)] 824 + pub(crate) fn init( 825 + sequence: u32, 826 + cmd_size: usize, 827 + function: MsgFunction, 828 + ) -> impl Init<Self, Error> { 829 + type RpcMessageHeader = bindings::rpc_message_header_v; 830 + type InnerGspMsgElement = bindings::GSP_MSG_QUEUE_ELEMENT; 831 + let init_inner = try_init!(InnerGspMsgElement { 832 + seqNum: sequence, 833 + elemCount: size_of::<Self>() 834 + .checked_add(cmd_size) 835 + .ok_or(EOVERFLOW)? 836 + .div_ceil(GSP_PAGE_SIZE) 837 + .try_into() 838 + .map_err(|_| EOVERFLOW)?, 839 + rpc <- RpcMessageHeader::init(cmd_size, function), 840 + ..Zeroable::init_zeroed() 841 + }); 842 + 843 + try_init!(GspMsgElement { 844 + inner <- init_inner, 845 + }) 846 + } 847 + 848 + /// Sets the checksum of this message. 849 + /// 850 + /// Since the header is also part of the checksum, this is usually called after the whole 851 + /// message has been written to the shared memory area. 852 + pub(crate) fn set_checksum(&mut self, checksum: u32) { 853 + self.inner.checkSum = checksum; 854 + } 855 + 856 + /// Returns the total length of the message. 857 + pub(crate) fn length(&self) -> usize { 858 + // `rpc.length` includes the length of the GspRpcHeader but not the message header. 859 + size_of::<Self>() - size_of::<bindings::rpc_message_header_v>() 860 + + num::u32_as_usize(self.inner.rpc.length) 861 + } 862 + 863 + // Returns the sequence number of the message. 864 + pub(crate) fn sequence(&self) -> u32 { 865 + self.inner.rpc.sequence 866 + } 867 + 868 + // Returns the function of the message, if it is valid, or the invalid function number as an 869 + // error. 870 + pub(crate) fn function(&self) -> Result<MsgFunction, u32> { 871 + self.inner 872 + .rpc 873 + .function 874 + .try_into() 875 + .map_err(|_| self.inner.rpc.function) 876 + } 877 + 878 + // Returns the number of elements (i.e. memory pages) used by this message. 879 + pub(crate) fn element_count(&self) -> u32 { 880 + self.inner.elemCount 881 + } 882 + } 883 + 884 + // SAFETY: Padding is explicit and does not contain uninitialized data. 885 + unsafe impl AsBytes for GspMsgElement {} 886 + 887 + // SAFETY: This struct only contains integer types for which all bit patterns 888 + // are valid. 889 + unsafe impl FromBytes for GspMsgElement {} 890 + 891 + /// Arguments for GSP startup. 892 + #[repr(transparent)] 893 + pub(crate) struct GspArgumentsCached(bindings::GSP_ARGUMENTS_CACHED); 894 + 895 + impl GspArgumentsCached { 896 + /// Creates the arguments for starting the GSP up using `cmdq` as its command queue. 897 + pub(crate) fn new(cmdq: &Cmdq) -> Self { 898 + Self(bindings::GSP_ARGUMENTS_CACHED { 899 + messageQueueInitArguments: MessageQueueInitArguments::new(cmdq).0, 900 + bDmemStack: 1, 901 + ..Default::default() 902 + }) 903 + } 904 + } 905 + 906 + // SAFETY: Padding is explicit and will not contain uninitialized data. 907 + unsafe impl AsBytes for GspArgumentsCached {} 908 + 909 + // SAFETY: This struct only contains integer types for which all bit patterns 910 + // are valid. 911 + unsafe impl FromBytes for GspArgumentsCached {} 912 + 913 + /// Init arguments for the message queue. 914 + #[repr(transparent)] 915 + struct MessageQueueInitArguments(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS); 916 + 917 + impl MessageQueueInitArguments { 918 + /// Creates a new init arguments structure for `cmdq`. 919 + fn new(cmdq: &Cmdq) -> Self { 920 + Self(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS { 921 + sharedMemPhysAddr: cmdq.dma_handle(), 922 + pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(), 923 + cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET), 924 + statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET), 925 + ..Default::default() 926 + }) 927 + } 928 + }
+128
drivers/gpu/nova-core/gsp/fw/commands.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use kernel::prelude::*; 4 + use kernel::transmute::{AsBytes, FromBytes}; 5 + use kernel::{device, pci}; 6 + 7 + use crate::gsp::GSP_PAGE_SIZE; 8 + 9 + use super::bindings; 10 + 11 + /// Payload of the `GspSetSystemInfo` command. 12 + #[repr(transparent)] 13 + pub(crate) struct GspSetSystemInfo { 14 + inner: bindings::GspSystemInfo, 15 + } 16 + static_assert!(size_of::<GspSetSystemInfo>() < GSP_PAGE_SIZE); 17 + 18 + impl GspSetSystemInfo { 19 + /// Returns an in-place initializer for the `GspSetSystemInfo` command. 20 + #[allow(non_snake_case)] 21 + pub(crate) fn init<'a>(dev: &'a pci::Device<device::Bound>) -> impl Init<Self, Error> + 'a { 22 + type InnerGspSystemInfo = bindings::GspSystemInfo; 23 + let init_inner = try_init!(InnerGspSystemInfo { 24 + gpuPhysAddr: dev.resource_start(0)?, 25 + gpuPhysFbAddr: dev.resource_start(1)?, 26 + gpuPhysInstAddr: dev.resource_start(3)?, 27 + nvDomainBusDeviceFunc: u64::from(dev.dev_id()), 28 + 29 + // Using TASK_SIZE in r535_gsp_rpc_set_system_info() seems wrong because 30 + // TASK_SIZE is per-task. That's probably a design issue in GSP-RM though. 31 + maxUserVa: (1 << 47) - 4096, 32 + pciConfigMirrorBase: 0x088000, 33 + pciConfigMirrorSize: 0x001000, 34 + 35 + PCIDeviceID: (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id().as_raw()), 36 + PCISubDeviceID: (u32::from(dev.subsystem_device_id()) << 16) 37 + | u32::from(dev.subsystem_vendor_id()), 38 + PCIRevisionID: u32::from(dev.revision_id()), 39 + bIsPrimary: 0, 40 + bPreserveVideoMemoryAllocations: 0, 41 + ..Zeroable::init_zeroed() 42 + }); 43 + 44 + try_init!(GspSetSystemInfo { 45 + inner <- init_inner, 46 + }) 47 + } 48 + } 49 + 50 + // SAFETY: These structs don't meet the no-padding requirements of AsBytes but 51 + // that is not a problem because they are not used outside the kernel. 52 + unsafe impl AsBytes for GspSetSystemInfo {} 53 + 54 + // SAFETY: These structs don't meet the no-padding requirements of FromBytes but 55 + // that is not a problem because they are not used outside the kernel. 56 + unsafe impl FromBytes for GspSetSystemInfo {} 57 + 58 + #[repr(transparent)] 59 + pub(crate) struct PackedRegistryEntry(bindings::PACKED_REGISTRY_ENTRY); 60 + 61 + impl PackedRegistryEntry { 62 + pub(crate) fn new(offset: u32, value: u32) -> Self { 63 + Self({ 64 + bindings::PACKED_REGISTRY_ENTRY { 65 + nameOffset: offset, 66 + 67 + // We only support DWORD types for now. Support for other types 68 + // will come later if required. 69 + type_: bindings::REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8, 70 + __bindgen_padding_0: Default::default(), 71 + data: value, 72 + length: 0, 73 + } 74 + }) 75 + } 76 + } 77 + 78 + // SAFETY: Padding is explicit and will not contain uninitialized data. 79 + unsafe impl AsBytes for PackedRegistryEntry {} 80 + 81 + /// Payload of the `SetRegistry` command. 82 + #[repr(transparent)] 83 + pub(crate) struct PackedRegistryTable { 84 + inner: bindings::PACKED_REGISTRY_TABLE, 85 + } 86 + 87 + impl PackedRegistryTable { 88 + #[allow(non_snake_case)] 89 + pub(crate) fn init(num_entries: u32, size: u32) -> impl Init<Self> { 90 + type InnerPackedRegistryTable = bindings::PACKED_REGISTRY_TABLE; 91 + let init_inner = init!(InnerPackedRegistryTable { 92 + numEntries: num_entries, 93 + size, 94 + entries: Default::default() 95 + }); 96 + 97 + init!(PackedRegistryTable { inner <- init_inner }) 98 + } 99 + } 100 + 101 + // SAFETY: Padding is explicit and will not contain uninitialized data. 102 + unsafe impl AsBytes for PackedRegistryTable {} 103 + 104 + // SAFETY: This struct only contains integer types for which all bit patterns 105 + // are valid. 106 + unsafe impl FromBytes for PackedRegistryTable {} 107 + 108 + /// Payload of the `GetGspStaticInfo` command and message. 109 + #[repr(transparent)] 110 + pub(crate) struct GspStaticConfigInfo(bindings::GspStaticConfigInfo_t); 111 + 112 + impl GspStaticConfigInfo { 113 + /// Returns a bytes array containing the (hopefully) zero-terminated name of this GPU. 114 + pub(crate) fn gpu_name_str(&self) -> [u8; 64] { 115 + self.0.gpuNameString 116 + } 117 + } 118 + 119 + // SAFETY: Padding is explicit and will not contain uninitialized data. 120 + unsafe impl AsBytes for GspStaticConfigInfo {} 121 + 122 + // SAFETY: This struct only contains integer types for which all bit patterns 123 + // are valid. 124 + unsafe impl FromBytes for GspStaticConfigInfo {} 125 + 126 + // SAFETY: This struct only contains integer types and fixed-size arrays for which 127 + // all bit patterns are valid. 128 + unsafe impl Zeroable for GspStaticConfigInfo {}
+4 -2
drivers/gpu/nova-core/gsp/fw/r570_144.rs
··· 12 12 #![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))] 13 13 #![allow( 14 14 dead_code, 15 - unused_imports, 16 15 clippy::all, 17 16 clippy::undocumented_unsafe_blocks, 18 17 clippy::ptr_as_ptr, ··· 24 25 unreachable_pub, 25 26 unsafe_op_in_unsafe_fn 26 27 )] 27 - use kernel::ffi; 28 + use kernel::{ 29 + ffi, 30 + prelude::Zeroable, // 31 + }; 28 32 include!("r570_144/bindings.rs");
+950
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 + 3 + #[repr(C)] 4 + #[derive(Default)] 5 + pub struct __IncompleteArrayField<T>(::core::marker::PhantomData<T>, [T; 0]); 6 + impl<T> __IncompleteArrayField<T> { 7 + #[inline] 8 + pub const fn new() -> Self { 9 + __IncompleteArrayField(::core::marker::PhantomData, []) 10 + } 11 + #[inline] 12 + pub fn as_ptr(&self) -> *const T { 13 + self as *const _ as *const T 14 + } 15 + #[inline] 16 + pub fn as_mut_ptr(&mut self) -> *mut T { 17 + self as *mut _ as *mut T 18 + } 19 + #[inline] 20 + pub unsafe fn as_slice(&self, len: usize) -> &[T] { 21 + ::core::slice::from_raw_parts(self.as_ptr(), len) 22 + } 23 + #[inline] 24 + pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { 25 + ::core::slice::from_raw_parts_mut(self.as_mut_ptr(), len) 26 + } 27 + } 28 + impl<T> ::core::fmt::Debug for __IncompleteArrayField<T> { 29 + fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 30 + fmt.write_str("__IncompleteArrayField") 31 + } 32 + } 33 + pub const NV_VGPU_MSG_SIGNATURE_VALID: u32 = 1129337430; 34 + pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2: u32 = 0; 35 + pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL: u32 = 23068672; 36 + pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X: u32 = 8388608; 37 + pub const GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB: u32 = 98304; 38 + pub const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE: u32 = 100663296; 39 + pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB: u32 = 64; 40 + pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB: u32 = 256; 41 + pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB: u32 = 88; 42 + pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB: u32 = 280; 43 + pub const GSP_FW_WPR_META_REVISION: u32 = 1; 44 + pub const GSP_FW_WPR_META_MAGIC: i64 = -2577556379034558285; 45 + pub const REGISTRY_TABLE_ENTRY_TYPE_DWORD: u32 = 1; 46 + pub type __u8 = ffi::c_uchar; 47 + pub type __u16 = ffi::c_ushort; 48 + pub type __u32 = ffi::c_uint; 49 + pub type __u64 = ffi::c_ulonglong; 50 + pub type u8_ = __u8; 51 + pub type u16_ = __u16; 52 + pub type u32_ = __u32; 53 + pub type u64_ = __u64; 54 + pub const NV_VGPU_MSG_FUNCTION_NOP: _bindgen_ty_2 = 0; 55 + pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO: _bindgen_ty_2 = 1; 56 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_ROOT: _bindgen_ty_2 = 2; 57 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE: _bindgen_ty_2 = 3; 58 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY: _bindgen_ty_2 = 4; 59 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA: _bindgen_ty_2 = 5; 60 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA: _bindgen_ty_2 = 6; 61 + pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY: _bindgen_ty_2 = 7; 62 + pub const NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA: _bindgen_ty_2 = 8; 63 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT: _bindgen_ty_2 = 9; 64 + pub const NV_VGPU_MSG_FUNCTION_FREE: _bindgen_ty_2 = 10; 65 + pub const NV_VGPU_MSG_FUNCTION_LOG: _bindgen_ty_2 = 11; 66 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIDMEM: _bindgen_ty_2 = 12; 67 + pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY: _bindgen_ty_2 = 13; 68 + pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY_DMA: _bindgen_ty_2 = 14; 69 + pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY_DMA: _bindgen_ty_2 = 15; 70 + pub const NV_VGPU_MSG_FUNCTION_GET_EDID: _bindgen_ty_2 = 16; 71 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_CHANNEL: _bindgen_ty_2 = 17; 72 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_OBJECT: _bindgen_ty_2 = 18; 73 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_SUBDEVICE: _bindgen_ty_2 = 19; 74 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_DYNAMIC_MEMORY: _bindgen_ty_2 = 20; 75 + pub const NV_VGPU_MSG_FUNCTION_DUP_OBJECT: _bindgen_ty_2 = 21; 76 + pub const NV_VGPU_MSG_FUNCTION_IDLE_CHANNELS: _bindgen_ty_2 = 22; 77 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_EVENT: _bindgen_ty_2 = 23; 78 + pub const NV_VGPU_MSG_FUNCTION_SEND_EVENT: _bindgen_ty_2 = 24; 79 + pub const NV_VGPU_MSG_FUNCTION_REMAPPER_CONTROL: _bindgen_ty_2 = 25; 80 + pub const NV_VGPU_MSG_FUNCTION_DMA_CONTROL: _bindgen_ty_2 = 26; 81 + pub const NV_VGPU_MSG_FUNCTION_DMA_FILL_PTE_MEM: _bindgen_ty_2 = 27; 82 + pub const NV_VGPU_MSG_FUNCTION_MANAGE_HW_RESOURCE: _bindgen_ty_2 = 28; 83 + pub const NV_VGPU_MSG_FUNCTION_BIND_ARBITRARY_CTX_DMA: _bindgen_ty_2 = 29; 84 + pub const NV_VGPU_MSG_FUNCTION_CREATE_FB_SEGMENT: _bindgen_ty_2 = 30; 85 + pub const NV_VGPU_MSG_FUNCTION_DESTROY_FB_SEGMENT: _bindgen_ty_2 = 31; 86 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_SHARE_DEVICE: _bindgen_ty_2 = 32; 87 + pub const NV_VGPU_MSG_FUNCTION_DEFERRED_API_CONTROL: _bindgen_ty_2 = 33; 88 + pub const NV_VGPU_MSG_FUNCTION_REMOVE_DEFERRED_API: _bindgen_ty_2 = 34; 89 + pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_READ: _bindgen_ty_2 = 35; 90 + pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_WRITE: _bindgen_ty_2 = 36; 91 + pub const NV_VGPU_MSG_FUNCTION_SIM_MANAGE_DISPLAY_CONTEXT_DMA: _bindgen_ty_2 = 37; 92 + pub const NV_VGPU_MSG_FUNCTION_FREE_VIDMEM_VIRT: _bindgen_ty_2 = 38; 93 + pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PSTATE_INFO: _bindgen_ty_2 = 39; 94 + pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PERFMON_SAMPLE: _bindgen_ty_2 = 40; 95 + pub const NV_VGPU_MSG_FUNCTION_PERF_GET_VIRTUAL_PSTATE_INFO: _bindgen_ty_2 = 41; 96 + pub const NV_VGPU_MSG_FUNCTION_PERF_GET_LEVEL_INFO: _bindgen_ty_2 = 42; 97 + pub const NV_VGPU_MSG_FUNCTION_MAP_SEMA_MEMORY: _bindgen_ty_2 = 43; 98 + pub const NV_VGPU_MSG_FUNCTION_UNMAP_SEMA_MEMORY: _bindgen_ty_2 = 44; 99 + pub const NV_VGPU_MSG_FUNCTION_SET_SURFACE_PROPERTIES: _bindgen_ty_2 = 45; 100 + pub const NV_VGPU_MSG_FUNCTION_CLEANUP_SURFACE: _bindgen_ty_2 = 46; 101 + pub const NV_VGPU_MSG_FUNCTION_UNLOADING_GUEST_DRIVER: _bindgen_ty_2 = 47; 102 + pub const NV_VGPU_MSG_FUNCTION_TDR_SET_TIMEOUT_STATE: _bindgen_ty_2 = 48; 103 + pub const NV_VGPU_MSG_FUNCTION_SWITCH_TO_VGA: _bindgen_ty_2 = 49; 104 + pub const NV_VGPU_MSG_FUNCTION_GPU_EXEC_REG_OPS: _bindgen_ty_2 = 50; 105 + pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO: _bindgen_ty_2 = 51; 106 + pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIRTMEM: _bindgen_ty_2 = 52; 107 + pub const NV_VGPU_MSG_FUNCTION_UPDATE_PDE_2: _bindgen_ty_2 = 53; 108 + pub const NV_VGPU_MSG_FUNCTION_SET_PAGE_DIRECTORY: _bindgen_ty_2 = 54; 109 + pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_PSTATE_INFO: _bindgen_ty_2 = 55; 110 + pub const NV_VGPU_MSG_FUNCTION_TRANSLATE_GUEST_GPU_PTES: _bindgen_ty_2 = 56; 111 + pub const NV_VGPU_MSG_FUNCTION_RESERVED_57: _bindgen_ty_2 = 57; 112 + pub const NV_VGPU_MSG_FUNCTION_RESET_CURRENT_GR_CONTEXT: _bindgen_ty_2 = 58; 113 + pub const NV_VGPU_MSG_FUNCTION_SET_SEMA_MEM_VALIDATION_STATE: _bindgen_ty_2 = 59; 114 + pub const NV_VGPU_MSG_FUNCTION_GET_ENGINE_UTILIZATION: _bindgen_ty_2 = 60; 115 + pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPU_PDES: _bindgen_ty_2 = 61; 116 + pub const NV_VGPU_MSG_FUNCTION_GET_ENCODER_CAPACITY: _bindgen_ty_2 = 62; 117 + pub const NV_VGPU_MSG_FUNCTION_VGPU_PF_REG_READ32: _bindgen_ty_2 = 63; 118 + pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO_EXT: _bindgen_ty_2 = 64; 119 + pub const NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO: _bindgen_ty_2 = 65; 120 + pub const NV_VGPU_MSG_FUNCTION_RMFS_INIT: _bindgen_ty_2 = 66; 121 + pub const NV_VGPU_MSG_FUNCTION_RMFS_CLOSE_QUEUE: _bindgen_ty_2 = 67; 122 + pub const NV_VGPU_MSG_FUNCTION_RMFS_CLEANUP: _bindgen_ty_2 = 68; 123 + pub const NV_VGPU_MSG_FUNCTION_RMFS_TEST: _bindgen_ty_2 = 69; 124 + pub const NV_VGPU_MSG_FUNCTION_UPDATE_BAR_PDE: _bindgen_ty_2 = 70; 125 + pub const NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD: _bindgen_ty_2 = 71; 126 + pub const NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO: _bindgen_ty_2 = 72; 127 + pub const NV_VGPU_MSG_FUNCTION_SET_REGISTRY: _bindgen_ty_2 = 73; 128 + pub const NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU: _bindgen_ty_2 = 74; 129 + pub const NV_VGPU_MSG_FUNCTION_SUBDEV_EVENT_SET_NOTIFICATION: _bindgen_ty_2 = 75; 130 + pub const NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL: _bindgen_ty_2 = 76; 131 + pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO2: _bindgen_ty_2 = 77; 132 + pub const NV_VGPU_MSG_FUNCTION_DUMP_PROTOBUF_COMPONENT: _bindgen_ty_2 = 78; 133 + pub const NV_VGPU_MSG_FUNCTION_UNSET_PAGE_DIRECTORY: _bindgen_ty_2 = 79; 134 + pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_STATIC_INFO: _bindgen_ty_2 = 80; 135 + pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_FAULT_BUFFER: _bindgen_ty_2 = 81; 136 + pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_FAULT_BUFFER: _bindgen_ty_2 = 82; 137 + pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 83; 138 + pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 84; 139 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_VGPU_FB_USAGE: _bindgen_ty_2 = 85; 140 + pub const NV_VGPU_MSG_FUNCTION_CTRL_NVFBC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 86; 141 + pub const NV_VGPU_MSG_FUNCTION_CTRL_NVENC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 87; 142 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_CHANNEL: _bindgen_ty_2 = 88; 143 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_ISOLATED_CHANNEL: _bindgen_ty_2 = 89; 144 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_HANDLE_VF_PRI_FAULT: _bindgen_ty_2 = 90; 145 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CLK_GET_EXTENDED_INFO: _bindgen_ty_2 = 91; 146 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_BOOST: _bindgen_ty_2 = 92; 147 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_VPSTATES_GET_CONTROL: _bindgen_ty_2 = 93; 148 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE: _bindgen_ty_2 = 94; 149 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_COLOR_CLEAR: _bindgen_ty_2 = 95; 150 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_DEPTH_CLEAR: _bindgen_ty_2 = 96; 151 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SCHEDULE: _bindgen_ty_2 = 97; 152 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TIMESLICE: _bindgen_ty_2 = 98; 153 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PREEMPT: _bindgen_ty_2 = 99; 154 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_DISABLE_CHANNELS: _bindgen_ty_2 = 100; 155 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TSG_INTERLEAVE_LEVEL: _bindgen_ty_2 = 101; 156 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_CHANNEL_INTERLEAVE_LEVEL: _bindgen_ty_2 = 102; 157 + pub const NV_VGPU_MSG_FUNCTION_GSP_RM_ALLOC: _bindgen_ty_2 = 103; 158 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_V2: _bindgen_ty_2 = 104; 159 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_AES_ENCRYPT: _bindgen_ty_2 = 105; 160 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY: _bindgen_ty_2 = 106; 161 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY_STATUS: _bindgen_ty_2 = 107; 162 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 108; 163 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 109; 164 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_EXCEPTION_MASK: _bindgen_ty_2 = 110; 165 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_PROMOTE_CTX: _bindgen_ty_2 = 111; 166 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_PREEMPTION_BIND: _bindgen_ty_2 = 112; 167 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_CTXSW_PREEMPTION_MODE: _bindgen_ty_2 = 113; 168 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_ZCULL_BIND: _bindgen_ty_2 = 114; 169 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_INITIALIZE_CTX: _bindgen_ty_2 = 115; 170 + pub const NV_VGPU_MSG_FUNCTION_CTRL_VASPACE_COPY_SERVER_RESERVED_PDES: _bindgen_ty_2 = 116; 171 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_CLEAR_FAULTED_BIT: _bindgen_ty_2 = 117; 172 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_LATEST_ECC_ADDRESSES: _bindgen_ty_2 = 118; 173 + pub const NV_VGPU_MSG_FUNCTION_CTRL_MC_SERVICE_INTERRUPTS: _bindgen_ty_2 = 119; 174 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DMA_SET_DEFAULT_VASPACE: _bindgen_ty_2 = 120; 175 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_CE_PCE_MASK: _bindgen_ty_2 = 121; 176 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE_ENTRY: _bindgen_ty_2 = 122; 177 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_PEER_ID_MASK: _bindgen_ty_2 = 123; 178 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_STATUS: _bindgen_ty_2 = 124; 179 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS: _bindgen_ty_2 = 125; 180 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_MATRIX: _bindgen_ty_2 = 126; 181 + pub const NV_VGPU_MSG_FUNCTION_RESERVED_0: _bindgen_ty_2 = 127; 182 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_PM_AREA_SMPC: _bindgen_ty_2 = 128; 183 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HWPM_LEGACY: _bindgen_ty_2 = 129; 184 + pub const NV_VGPU_MSG_FUNCTION_CTRL_B0CC_EXEC_REG_OPS: _bindgen_ty_2 = 130; 185 + pub const NV_VGPU_MSG_FUNCTION_CTRL_BIND_PM_RESOURCES: _bindgen_ty_2 = 131; 186 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SUSPEND_CONTEXT: _bindgen_ty_2 = 132; 187 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_RESUME_CONTEXT: _bindgen_ty_2 = 133; 188 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_EXEC_REG_OPS: _bindgen_ty_2 = 134; 189 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_DEBUG: _bindgen_ty_2 = 135; 190 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 136; 191 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 137; 192 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_ERRBAR_DEBUG: _bindgen_ty_2 = 138; 193 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_NEXT_STOP_TRIGGER_TYPE: _bindgen_ty_2 = 139; 194 + pub const NV_VGPU_MSG_FUNCTION_CTRL_ALLOC_PMA_STREAM: _bindgen_ty_2 = 140; 195 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PMA_STREAM_UPDATE_GET_PUT: _bindgen_ty_2 = 141; 196 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_INFO_V2: _bindgen_ty_2 = 142; 197 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SET_CHANNEL_PROPERTIES: _bindgen_ty_2 = 143; 198 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 144; 199 + pub const NV_VGPU_MSG_FUNCTION_CTRL_KGR_GET_CTX_BUFFER_PTES: _bindgen_ty_2 = 145; 200 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_EVICT_CTX: _bindgen_ty_2 = 146; 201 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_FS_INFO: _bindgen_ty_2 = 147; 202 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GRMGR_GET_GR_FS_INFO: _bindgen_ty_2 = 148; 203 + pub const NV_VGPU_MSG_FUNCTION_CTRL_STOP_CHANNEL: _bindgen_ty_2 = 149; 204 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_PC_SAMPLING_MODE: _bindgen_ty_2 = 150; 205 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_GET_STATUS: _bindgen_ty_2 = 151; 206 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_SET_CONTROL: _bindgen_ty_2 = 152; 207 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FREE_PMA_STREAM: _bindgen_ty_2 = 153; 208 + pub const NV_VGPU_MSG_FUNCTION_CTRL_TIMER_SET_GR_TICK_FREQ: _bindgen_ty_2 = 154; 209 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SETUP_VF_ZOMBIE_SUBCTX_PDB: _bindgen_ty_2 = 155; 210 + pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_GR_STATIC_INFO: _bindgen_ty_2 = 156; 211 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_SINGLE_SM_SINGLE_STEP: _bindgen_ty_2 = 157; 212 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_TPC_PARTITION_MODE: _bindgen_ty_2 = 158; 213 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_TPC_PARTITION_MODE: _bindgen_ty_2 = 159; 214 + pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_ALLOCATE: _bindgen_ty_2 = 160; 215 + pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_DESTROY: _bindgen_ty_2 = 161; 216 + pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_MAP: _bindgen_ty_2 = 162; 217 + pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_UNMAP: _bindgen_ty_2 = 163; 218 + pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_PUSH_STREAM: _bindgen_ty_2 = 164; 219 + pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_SET_HANDLES: _bindgen_ty_2 = 165; 220 + pub const NV_VGPU_MSG_FUNCTION_UVM_METHOD_STREAM_GUEST_PAGES_OPERATION: _bindgen_ty_2 = 166; 221 + pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_QUIESCE_PMA_CHANNEL: _bindgen_ty_2 = 167; 222 + pub const NV_VGPU_MSG_FUNCTION_DCE_RM_INIT: _bindgen_ty_2 = 168; 223 + pub const NV_VGPU_MSG_FUNCTION_REGISTER_VIRTUAL_EVENT_BUFFER: _bindgen_ty_2 = 169; 224 + pub const NV_VGPU_MSG_FUNCTION_CTRL_EVENT_BUFFER_UPDATE_GET: _bindgen_ty_2 = 170; 225 + pub const NV_VGPU_MSG_FUNCTION_GET_PLCABLE_ADDRESS_KIND: _bindgen_ty_2 = 171; 226 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_LIMITS_SET_STATUS_V2: _bindgen_ty_2 = 172; 227 + pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_SRIOV_PROMOTE_PMA_STREAM: _bindgen_ty_2 = 173; 228 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_MMU_DEBUG_MODE: _bindgen_ty_2 = 174; 229 + pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_PROMOTE_FAULT_METHOD_BUFFERS: _bindgen_ty_2 = 175; 230 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_SIZE: _bindgen_ty_2 = 176; 231 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 177; 232 + pub const NV_VGPU_MSG_FUNCTION_DISABLE_CHANNELS: _bindgen_ty_2 = 178; 233 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEMORY_DESCRIBE: _bindgen_ty_2 = 179; 234 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEM_STATS: _bindgen_ty_2 = 180; 235 + pub const NV_VGPU_MSG_FUNCTION_SAVE_HIBERNATION_DATA: _bindgen_ty_2 = 181; 236 + pub const NV_VGPU_MSG_FUNCTION_RESTORE_HIBERNATION_DATA: _bindgen_ty_2 = 182; 237 + pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_MEMSYS_SET_ZBC_REFERENCED: _bindgen_ty_2 = 183; 238 + pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_CREATE: _bindgen_ty_2 = 184; 239 + pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_DELETE: _bindgen_ty_2 = 185; 240 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_GET_WORK_SUBMIT_TOKEN: _bindgen_ty_2 = 186; 241 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX: _bindgen_ty_2 = 187; 242 + pub const NV_VGPU_MSG_FUNCTION_PMA_SCRUBBER_SHARED_BUFFER_GUEST_PAGES_OPERATION: _bindgen_ty_2 = 243 + 188; 244 + pub const NV_VGPU_MSG_FUNCTION_CTRL_MASTER_GET_VIRTUAL_FUNCTION_ERROR_CONT_INTR_MASK: 245 + _bindgen_ty_2 = 189; 246 + pub const NV_VGPU_MSG_FUNCTION_SET_SYSMEM_DIRTY_PAGE_TRACKING_BUFFER: _bindgen_ty_2 = 190; 247 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_P2P_CAPS: _bindgen_ty_2 = 191; 248 + pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_SET_P2P_MAPPING: _bindgen_ty_2 = 192; 249 + pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_UNSET_P2P_MAPPING: _bindgen_ty_2 = 193; 250 + pub const NV_VGPU_MSG_FUNCTION_CTRL_FLA_SETUP_INSTANCE_MEM_BLOCK: _bindgen_ty_2 = 194; 251 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_MIGRATABLE_OPS: _bindgen_ty_2 = 195; 252 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_TOTAL_HS_CREDITS: _bindgen_ty_2 = 196; 253 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_HS_CREDITS: _bindgen_ty_2 = 197; 254 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_HS_CREDITS: _bindgen_ty_2 = 198; 255 + pub const NV_VGPU_MSG_FUNCTION_CTRL_PM_AREA_PC_SAMPLER: _bindgen_ty_2 = 199; 256 + pub const NV_VGPU_MSG_FUNCTION_INVALIDATE_TLB: _bindgen_ty_2 = 200; 257 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_QUERY_ECC_STATUS: _bindgen_ty_2 = 201; 258 + pub const NV_VGPU_MSG_FUNCTION_ECC_NOTIFIER_WRITE_ACK: _bindgen_ty_2 = 202; 259 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_DEBUG: _bindgen_ty_2 = 203; 260 + pub const NV_VGPU_MSG_FUNCTION_RM_API_CONTROL: _bindgen_ty_2 = 204; 261 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_GPU_START_FABRIC_PROBE: _bindgen_ty_2 = 205; 262 + pub const NV_VGPU_MSG_FUNCTION_CTRL_NVLINK_GET_INBAND_RECEIVED_DATA: _bindgen_ty_2 = 206; 263 + pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_DATA: _bindgen_ty_2 = 207; 264 + pub const NV_VGPU_MSG_FUNCTION_RESERVED_208: _bindgen_ty_2 = 208; 265 + pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_GET_INFO_V2: _bindgen_ty_2 = 209; 266 + pub const NV_VGPU_MSG_FUNCTION_GET_BRAND_CAPS: _bindgen_ty_2 = 210; 267 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_NVLINK_INBAND_SEND_DATA: _bindgen_ty_2 = 211; 268 + pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPM_GUEST_BUFFER_INFO: _bindgen_ty_2 = 212; 269 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_CONTROL_GSP_TRACE: _bindgen_ty_2 = 213; 270 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_STENCIL_CLEAR: _bindgen_ty_2 = 214; 271 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_VGPU_HEAP_STATS: _bindgen_ty_2 = 215; 272 + pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_LIBOS_HEAP_STATS: _bindgen_ty_2 = 216; 273 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 217; 274 + pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 218; 275 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HES: _bindgen_ty_2 = 219; 276 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_HES: _bindgen_ty_2 = 220; 277 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_CCU_PROF: _bindgen_ty_2 = 221; 278 + pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_CCU_PROF: _bindgen_ty_2 = 222; 279 + pub const NV_VGPU_MSG_FUNCTION_RESERVED: _bindgen_ty_2 = 223; 280 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_CHIPLET_HS_CREDIT_POOL: _bindgen_ty_2 = 224; 281 + pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_HS_CREDITS_MAPPING: _bindgen_ty_2 = 225; 282 + pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_EXPORT: _bindgen_ty_2 = 226; 283 + pub const NV_VGPU_MSG_FUNCTION_NUM_FUNCTIONS: _bindgen_ty_2 = 227; 284 + pub type _bindgen_ty_2 = ffi::c_uint; 285 + pub const NV_VGPU_MSG_EVENT_FIRST_EVENT: _bindgen_ty_3 = 4096; 286 + pub const NV_VGPU_MSG_EVENT_GSP_INIT_DONE: _bindgen_ty_3 = 4097; 287 + pub const NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER: _bindgen_ty_3 = 4098; 288 + pub const NV_VGPU_MSG_EVENT_POST_EVENT: _bindgen_ty_3 = 4099; 289 + pub const NV_VGPU_MSG_EVENT_RC_TRIGGERED: _bindgen_ty_3 = 4100; 290 + pub const NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED: _bindgen_ty_3 = 4101; 291 + pub const NV_VGPU_MSG_EVENT_OS_ERROR_LOG: _bindgen_ty_3 = 4102; 292 + pub const NV_VGPU_MSG_EVENT_RG_LINE_INTR: _bindgen_ty_3 = 4103; 293 + pub const NV_VGPU_MSG_EVENT_GPUACCT_PERFMON_UTIL_SAMPLES: _bindgen_ty_3 = 4104; 294 + pub const NV_VGPU_MSG_EVENT_SIM_READ: _bindgen_ty_3 = 4105; 295 + pub const NV_VGPU_MSG_EVENT_SIM_WRITE: _bindgen_ty_3 = 4106; 296 + pub const NV_VGPU_MSG_EVENT_SEMAPHORE_SCHEDULE_CALLBACK: _bindgen_ty_3 = 4107; 297 + pub const NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT: _bindgen_ty_3 = 4108; 298 + pub const NV_VGPU_MSG_EVENT_VGPU_GSP_PLUGIN_TRIGGERED: _bindgen_ty_3 = 4109; 299 + pub const NV_VGPU_MSG_EVENT_PERF_GPU_BOOST_SYNC_LIMITS_CALLBACK: _bindgen_ty_3 = 4110; 300 + pub const NV_VGPU_MSG_EVENT_PERF_BRIDGELESS_INFO_UPDATE: _bindgen_ty_3 = 4111; 301 + pub const NV_VGPU_MSG_EVENT_VGPU_CONFIG: _bindgen_ty_3 = 4112; 302 + pub const NV_VGPU_MSG_EVENT_DISPLAY_MODESET: _bindgen_ty_3 = 4113; 303 + pub const NV_VGPU_MSG_EVENT_EXTDEV_INTR_SERVICE: _bindgen_ty_3 = 4114; 304 + pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_256: _bindgen_ty_3 = 4115; 305 + pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_512: _bindgen_ty_3 = 4116; 306 + pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_1024: _bindgen_ty_3 = 4117; 307 + pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_2048: _bindgen_ty_3 = 4118; 308 + pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_4096: _bindgen_ty_3 = 4119; 309 + pub const NV_VGPU_MSG_EVENT_TIMED_SEMAPHORE_RELEASE: _bindgen_ty_3 = 4120; 310 + pub const NV_VGPU_MSG_EVENT_NVLINK_IS_GPU_DEGRADED: _bindgen_ty_3 = 4121; 311 + pub const NV_VGPU_MSG_EVENT_PFM_REQ_HNDLR_STATE_SYNC_CALLBACK: _bindgen_ty_3 = 4122; 312 + pub const NV_VGPU_MSG_EVENT_NVLINK_FAULT_UP: _bindgen_ty_3 = 4123; 313 + pub const NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE: _bindgen_ty_3 = 4124; 314 + pub const NV_VGPU_MSG_EVENT_MIG_CI_CONFIG_UPDATE: _bindgen_ty_3 = 4125; 315 + pub const NV_VGPU_MSG_EVENT_UPDATE_GSP_TRACE: _bindgen_ty_3 = 4126; 316 + pub const NV_VGPU_MSG_EVENT_NVLINK_FATAL_ERROR_RECOVERY: _bindgen_ty_3 = 4127; 317 + pub const NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD: _bindgen_ty_3 = 4128; 318 + pub const NV_VGPU_MSG_EVENT_FECS_ERROR: _bindgen_ty_3 = 4129; 319 + pub const NV_VGPU_MSG_EVENT_RECOVERY_ACTION: _bindgen_ty_3 = 4130; 320 + pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4131; 321 + pub type _bindgen_ty_3 = ffi::c_uint; 322 + #[repr(C)] 323 + #[derive(Debug, Default, Copy, Clone)] 324 + pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS { 325 + pub totalVFs: u32_, 326 + pub firstVfOffset: u32_, 327 + pub vfFeatureMask: u32_, 328 + pub FirstVFBar0Address: u64_, 329 + pub FirstVFBar1Address: u64_, 330 + pub FirstVFBar2Address: u64_, 331 + pub bar0Size: u64_, 332 + pub bar1Size: u64_, 333 + pub bar2Size: u64_, 334 + pub b64bitBar0: u8_, 335 + pub b64bitBar1: u8_, 336 + pub b64bitBar2: u8_, 337 + pub bSriovEnabled: u8_, 338 + pub bSriovHeavyEnabled: u8_, 339 + pub bEmulateVFBar0TlbInvalidationRegister: u8_, 340 + pub bClientRmAllocatedCtxBuffer: u8_, 341 + pub bNonPowerOf2ChannelCountSupported: u8_, 342 + pub bVfResizableBAR1Supported: u8_, 343 + } 344 + #[repr(C)] 345 + #[derive(Debug, Default, Copy, Clone)] 346 + pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS { 347 + pub BoardID: u32_, 348 + pub chipSKU: [ffi::c_char; 9usize], 349 + pub chipSKUMod: [ffi::c_char; 5usize], 350 + pub skuConfigVersion: u32_, 351 + pub project: [ffi::c_char; 5usize], 352 + pub projectSKU: [ffi::c_char; 5usize], 353 + pub CDP: [ffi::c_char; 6usize], 354 + pub projectSKUMod: [ffi::c_char; 2usize], 355 + pub businessCycle: u32_, 356 + } 357 + pub type NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG = [u8_; 17usize]; 358 + #[repr(C)] 359 + #[derive(Debug, Default, Copy, Clone)] 360 + pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO { 361 + pub base: u64_, 362 + pub limit: u64_, 363 + pub reserved: u64_, 364 + pub performance: u32_, 365 + pub supportCompressed: u8_, 366 + pub supportISO: u8_, 367 + pub bProtected: u8_, 368 + pub blackList: NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG, 369 + } 370 + #[repr(C)] 371 + #[derive(Debug, Default, Copy, Clone)] 372 + pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS { 373 + pub numFBRegions: u32_, 374 + pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize], 375 + } 376 + #[repr(C)] 377 + #[derive(Debug, Copy, Clone)] 378 + pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS { 379 + pub index: u32_, 380 + pub flags: u32_, 381 + pub length: u32_, 382 + pub data: [u8_; 256usize], 383 + } 384 + impl Default for NV2080_CTRL_GPU_GET_GID_INFO_PARAMS { 385 + fn default() -> Self { 386 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 387 + unsafe { 388 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 389 + s.assume_init() 390 + } 391 + } 392 + } 393 + #[repr(C)] 394 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 395 + pub struct DOD_METHOD_DATA { 396 + pub status: u32_, 397 + pub acpiIdListLen: u32_, 398 + pub acpiIdList: [u32_; 16usize], 399 + } 400 + #[repr(C)] 401 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 402 + pub struct JT_METHOD_DATA { 403 + pub status: u32_, 404 + pub jtCaps: u32_, 405 + pub jtRevId: u16_, 406 + pub bSBIOSCaps: u8_, 407 + pub __bindgen_padding_0: u8, 408 + } 409 + #[repr(C)] 410 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 411 + pub struct MUX_METHOD_DATA_ELEMENT { 412 + pub acpiId: u32_, 413 + pub mode: u32_, 414 + pub status: u32_, 415 + } 416 + #[repr(C)] 417 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 418 + pub struct MUX_METHOD_DATA { 419 + pub tableLen: u32_, 420 + pub acpiIdMuxModeTable: [MUX_METHOD_DATA_ELEMENT; 16usize], 421 + pub acpiIdMuxPartTable: [MUX_METHOD_DATA_ELEMENT; 16usize], 422 + pub acpiIdMuxStateTable: [MUX_METHOD_DATA_ELEMENT; 16usize], 423 + } 424 + #[repr(C)] 425 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 426 + pub struct CAPS_METHOD_DATA { 427 + pub status: u32_, 428 + pub optimusCaps: u32_, 429 + } 430 + #[repr(C)] 431 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 432 + pub struct ACPI_METHOD_DATA { 433 + pub bValid: u8_, 434 + pub __bindgen_padding_0: [u8; 3usize], 435 + pub dodMethodData: DOD_METHOD_DATA, 436 + pub jtMethodData: JT_METHOD_DATA, 437 + pub muxMethodData: MUX_METHOD_DATA, 438 + pub capsMethodData: CAPS_METHOD_DATA, 439 + } 440 + #[repr(C)] 441 + #[derive(Debug, Default, Copy, Clone)] 442 + pub struct VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS { 443 + pub headIndex: u32_, 444 + pub maxHResolution: u32_, 445 + pub maxVResolution: u32_, 446 + } 447 + #[repr(C)] 448 + #[derive(Debug, Default, Copy, Clone)] 449 + pub struct VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS { 450 + pub numHeads: u32_, 451 + pub maxNumHeads: u32_, 452 + } 453 + #[repr(C)] 454 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 455 + pub struct BUSINFO { 456 + pub deviceID: u16_, 457 + pub vendorID: u16_, 458 + pub subdeviceID: u16_, 459 + pub subvendorID: u16_, 460 + pub revisionID: u8_, 461 + pub __bindgen_padding_0: u8, 462 + } 463 + #[repr(C)] 464 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 465 + pub struct GSP_VF_INFO { 466 + pub totalVFs: u32_, 467 + pub firstVFOffset: u32_, 468 + pub FirstVFBar0Address: u64_, 469 + pub FirstVFBar1Address: u64_, 470 + pub FirstVFBar2Address: u64_, 471 + pub b64bitBar0: u8_, 472 + pub b64bitBar1: u8_, 473 + pub b64bitBar2: u8_, 474 + pub __bindgen_padding_0: [u8; 5usize], 475 + } 476 + #[repr(C)] 477 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 478 + pub struct GSP_PCIE_CONFIG_REG { 479 + pub linkCap: u32_, 480 + } 481 + #[repr(C)] 482 + #[derive(Debug, Default, Copy, Clone)] 483 + pub struct EcidManufacturingInfo { 484 + pub ecidLow: u32_, 485 + pub ecidHigh: u32_, 486 + pub ecidExtended: u32_, 487 + } 488 + #[repr(C)] 489 + #[derive(Debug, Default, Copy, Clone)] 490 + pub struct FW_WPR_LAYOUT_OFFSET { 491 + pub nonWprHeapOffset: u64_, 492 + pub frtsOffset: u64_, 493 + } 494 + #[repr(C)] 495 + #[derive(Debug, Copy, Clone)] 496 + pub struct GspStaticConfigInfo_t { 497 + pub grCapsBits: [u8_; 23usize], 498 + pub gidInfo: NV2080_CTRL_GPU_GET_GID_INFO_PARAMS, 499 + pub SKUInfo: NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS, 500 + pub fbRegionInfoParams: NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS, 501 + pub sriovCaps: NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS, 502 + pub sriovMaxGfid: u32_, 503 + pub engineCaps: [u32_; 3usize], 504 + pub poisonFuseEnabled: u8_, 505 + pub fb_length: u64_, 506 + pub fbio_mask: u64_, 507 + pub fb_bus_width: u32_, 508 + pub fb_ram_type: u32_, 509 + pub fbp_mask: u64_, 510 + pub l2_cache_size: u32_, 511 + pub gpuNameString: [u8_; 64usize], 512 + pub gpuShortNameString: [u8_; 64usize], 513 + pub gpuNameString_Unicode: [u16_; 64usize], 514 + pub bGpuInternalSku: u8_, 515 + pub bIsQuadroGeneric: u8_, 516 + pub bIsQuadroAd: u8_, 517 + pub bIsNvidiaNvs: u8_, 518 + pub bIsVgx: u8_, 519 + pub bGeforceSmb: u8_, 520 + pub bIsTitan: u8_, 521 + pub bIsTesla: u8_, 522 + pub bIsMobile: u8_, 523 + pub bIsGc6Rtd3Allowed: u8_, 524 + pub bIsGc8Rtd3Allowed: u8_, 525 + pub bIsGcOffRtd3Allowed: u8_, 526 + pub bIsGcoffLegacyAllowed: u8_, 527 + pub bIsMigSupported: u8_, 528 + pub RTD3GC6TotalBoardPower: u16_, 529 + pub RTD3GC6PerstDelay: u16_, 530 + pub bar1PdeBase: u64_, 531 + pub bar2PdeBase: u64_, 532 + pub bVbiosValid: u8_, 533 + pub vbiosSubVendor: u32_, 534 + pub vbiosSubDevice: u32_, 535 + pub bPageRetirementSupported: u8_, 536 + pub bSplitVasBetweenServerClientRm: u8_, 537 + pub bClRootportNeedsNosnoopWAR: u8_, 538 + pub displaylessMaxHeads: VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS, 539 + pub displaylessMaxResolution: VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS, 540 + pub displaylessMaxPixels: u64_, 541 + pub hInternalClient: u32_, 542 + pub hInternalDevice: u32_, 543 + pub hInternalSubdevice: u32_, 544 + pub bSelfHostedMode: u8_, 545 + pub bAtsSupported: u8_, 546 + pub bIsGpuUefi: u8_, 547 + pub bIsEfiInit: u8_, 548 + pub ecidInfo: [EcidManufacturingInfo; 2usize], 549 + pub fwWprLayoutOffset: FW_WPR_LAYOUT_OFFSET, 550 + } 551 + impl Default for GspStaticConfigInfo_t { 552 + fn default() -> Self { 553 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 554 + unsafe { 555 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 556 + s.assume_init() 557 + } 558 + } 559 + } 560 + #[repr(C)] 561 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 562 + pub struct GspSystemInfo { 563 + pub gpuPhysAddr: u64_, 564 + pub gpuPhysFbAddr: u64_, 565 + pub gpuPhysInstAddr: u64_, 566 + pub gpuPhysIoAddr: u64_, 567 + pub nvDomainBusDeviceFunc: u64_, 568 + pub simAccessBufPhysAddr: u64_, 569 + pub notifyOpSharedSurfacePhysAddr: u64_, 570 + pub pcieAtomicsOpMask: u64_, 571 + pub consoleMemSize: u64_, 572 + pub maxUserVa: u64_, 573 + pub pciConfigMirrorBase: u32_, 574 + pub pciConfigMirrorSize: u32_, 575 + pub PCIDeviceID: u32_, 576 + pub PCISubDeviceID: u32_, 577 + pub PCIRevisionID: u32_, 578 + pub pcieAtomicsCplDeviceCapMask: u32_, 579 + pub oorArch: u8_, 580 + pub __bindgen_padding_0: [u8; 7usize], 581 + pub clPdbProperties: u64_, 582 + pub Chipset: u32_, 583 + pub bGpuBehindBridge: u8_, 584 + pub bFlrSupported: u8_, 585 + pub b64bBar0Supported: u8_, 586 + pub bMnocAvailable: u8_, 587 + pub chipsetL1ssEnable: u32_, 588 + pub bUpstreamL0sUnsupported: u8_, 589 + pub bUpstreamL1Unsupported: u8_, 590 + pub bUpstreamL1PorSupported: u8_, 591 + pub bUpstreamL1PorMobileOnly: u8_, 592 + pub bSystemHasMux: u8_, 593 + pub upstreamAddressValid: u8_, 594 + pub FHBBusInfo: BUSINFO, 595 + pub chipsetIDInfo: BUSINFO, 596 + pub __bindgen_padding_1: [u8; 2usize], 597 + pub acpiMethodData: ACPI_METHOD_DATA, 598 + pub hypervisorType: u32_, 599 + pub bIsPassthru: u8_, 600 + pub __bindgen_padding_2: [u8; 7usize], 601 + pub sysTimerOffsetNs: u64_, 602 + pub gspVFInfo: GSP_VF_INFO, 603 + pub bIsPrimary: u8_, 604 + pub isGridBuild: u8_, 605 + pub __bindgen_padding_3: [u8; 2usize], 606 + pub pcieConfigReg: GSP_PCIE_CONFIG_REG, 607 + pub gridBuildCsp: u32_, 608 + pub bPreserveVideoMemoryAllocations: u8_, 609 + pub bTdrEventSupported: u8_, 610 + pub bFeatureStretchVblankCapable: u8_, 611 + pub bEnableDynamicGranularityPageArrays: u8_, 612 + pub bClockBoostSupported: u8_, 613 + pub bRouteDispIntrsToCPU: u8_, 614 + pub __bindgen_padding_4: [u8; 6usize], 615 + pub hostPageSize: u64_, 616 + } 617 + #[repr(C)] 618 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 619 + pub struct MESSAGE_QUEUE_INIT_ARGUMENTS { 620 + pub sharedMemPhysAddr: u64_, 621 + pub pageTableEntryCount: u32_, 622 + pub __bindgen_padding_0: [u8; 4usize], 623 + pub cmdQueueOffset: u64_, 624 + pub statQueueOffset: u64_, 625 + } 626 + #[repr(C)] 627 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 628 + pub struct GSP_SR_INIT_ARGUMENTS { 629 + pub oldLevel: u32_, 630 + pub flags: u32_, 631 + pub bInPMTransition: u8_, 632 + pub __bindgen_padding_0: [u8; 3usize], 633 + } 634 + #[repr(C)] 635 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 636 + pub struct GSP_ARGUMENTS_CACHED { 637 + pub messageQueueInitArguments: MESSAGE_QUEUE_INIT_ARGUMENTS, 638 + pub srInitArguments: GSP_SR_INIT_ARGUMENTS, 639 + pub gpuInstance: u32_, 640 + pub bDmemStack: u8_, 641 + pub __bindgen_padding_0: [u8; 7usize], 642 + pub profilerArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_1, 643 + } 644 + #[repr(C)] 645 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 646 + pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 { 647 + pub pa: u64_, 648 + pub size: u64_, 649 + } 650 + #[repr(C)] 651 + #[derive(Copy, Clone, Zeroable)] 652 + pub union rpc_message_rpc_union_field_v03_00 { 653 + pub spare: u32_, 654 + pub cpuRmGfid: u32_, 655 + } 656 + impl Default for rpc_message_rpc_union_field_v03_00 { 657 + fn default() -> Self { 658 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 659 + unsafe { 660 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 661 + s.assume_init() 662 + } 663 + } 664 + } 665 + pub type rpc_message_rpc_union_field_v = rpc_message_rpc_union_field_v03_00; 666 + #[repr(C)] 667 + pub struct rpc_message_header_v03_00 { 668 + pub header_version: u32_, 669 + pub signature: u32_, 670 + pub length: u32_, 671 + pub function: u32_, 672 + pub rpc_result: u32_, 673 + pub rpc_result_private: u32_, 674 + pub sequence: u32_, 675 + pub u: rpc_message_rpc_union_field_v, 676 + pub rpc_message_data: __IncompleteArrayField<u8_>, 677 + } 678 + impl Default for rpc_message_header_v03_00 { 679 + fn default() -> Self { 680 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 681 + unsafe { 682 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 683 + s.assume_init() 684 + } 685 + } 686 + } 687 + pub type rpc_message_header_v = rpc_message_header_v03_00; 688 + #[repr(C)] 689 + #[derive(Copy, Clone, Zeroable)] 690 + pub struct GspFwWprMeta { 691 + pub magic: u64_, 692 + pub revision: u64_, 693 + pub sysmemAddrOfRadix3Elf: u64_, 694 + pub sizeOfRadix3Elf: u64_, 695 + pub sysmemAddrOfBootloader: u64_, 696 + pub sizeOfBootloader: u64_, 697 + pub bootloaderCodeOffset: u64_, 698 + pub bootloaderDataOffset: u64_, 699 + pub bootloaderManifestOffset: u64_, 700 + pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1, 701 + pub gspFwRsvdStart: u64_, 702 + pub nonWprHeapOffset: u64_, 703 + pub nonWprHeapSize: u64_, 704 + pub gspFwWprStart: u64_, 705 + pub gspFwHeapOffset: u64_, 706 + pub gspFwHeapSize: u64_, 707 + pub gspFwOffset: u64_, 708 + pub bootBinOffset: u64_, 709 + pub frtsOffset: u64_, 710 + pub frtsSize: u64_, 711 + pub gspFwWprEnd: u64_, 712 + pub fbSize: u64_, 713 + pub vgaWorkspaceOffset: u64_, 714 + pub vgaWorkspaceSize: u64_, 715 + pub bootCount: u64_, 716 + pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2, 717 + pub gspFwHeapVfPartitionCount: u8_, 718 + pub flags: u8_, 719 + pub padding: [u8_; 2usize], 720 + pub pmuReservedSize: u32_, 721 + pub verified: u64_, 722 + } 723 + #[repr(C)] 724 + #[derive(Copy, Clone, Zeroable)] 725 + pub union GspFwWprMeta__bindgen_ty_1 { 726 + pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1, 727 + pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2, 728 + } 729 + #[repr(C)] 730 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 731 + pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 { 732 + pub sysmemAddrOfSignature: u64_, 733 + pub sizeOfSignature: u64_, 734 + } 735 + #[repr(C)] 736 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 737 + pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 { 738 + pub gspFwHeapFreeListWprOffset: u32_, 739 + pub unused0: u32_, 740 + pub unused1: u64_, 741 + } 742 + impl Default for GspFwWprMeta__bindgen_ty_1 { 743 + fn default() -> Self { 744 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 745 + unsafe { 746 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 747 + s.assume_init() 748 + } 749 + } 750 + } 751 + #[repr(C)] 752 + #[derive(Copy, Clone, Zeroable)] 753 + pub union GspFwWprMeta__bindgen_ty_2 { 754 + pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1, 755 + pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2, 756 + } 757 + #[repr(C)] 758 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 759 + pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 { 760 + pub partitionRpcAddr: u64_, 761 + pub partitionRpcRequestOffset: u16_, 762 + pub partitionRpcReplyOffset: u16_, 763 + pub elfCodeOffset: u32_, 764 + pub elfDataOffset: u32_, 765 + pub elfCodeSize: u32_, 766 + pub elfDataSize: u32_, 767 + pub lsUcodeVersion: u32_, 768 + } 769 + #[repr(C)] 770 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 771 + pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 { 772 + pub partitionRpcPadding: [u32_; 4usize], 773 + pub sysmemAddrOfCrashReportQueue: u64_, 774 + pub sizeOfCrashReportQueue: u32_, 775 + pub lsUcodeVersionPadding: [u32_; 1usize], 776 + } 777 + impl Default for GspFwWprMeta__bindgen_ty_2 { 778 + fn default() -> Self { 779 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 780 + unsafe { 781 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 782 + s.assume_init() 783 + } 784 + } 785 + } 786 + impl Default for GspFwWprMeta { 787 + fn default() -> Self { 788 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 789 + unsafe { 790 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 791 + s.assume_init() 792 + } 793 + } 794 + } 795 + pub type LibosAddress = u64_; 796 + pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_NONE: LibosMemoryRegionKind = 0; 797 + pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS: LibosMemoryRegionKind = 1; 798 + pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_RADIX3: LibosMemoryRegionKind = 2; 799 + pub type LibosMemoryRegionKind = ffi::c_uint; 800 + pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_NONE: LibosMemoryRegionLoc = 0; 801 + pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM: LibosMemoryRegionLoc = 1; 802 + pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2; 803 + pub type LibosMemoryRegionLoc = ffi::c_uint; 804 + #[repr(C)] 805 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 806 + pub struct LibosMemoryRegionInitArgument { 807 + pub id8: LibosAddress, 808 + pub pa: LibosAddress, 809 + pub size: LibosAddress, 810 + pub kind: u8_, 811 + pub loc: u8_, 812 + pub __bindgen_padding_0: [u8; 6usize], 813 + } 814 + #[repr(C)] 815 + #[derive(Debug, Default, Copy, Clone)] 816 + pub struct PACKED_REGISTRY_ENTRY { 817 + pub nameOffset: u32_, 818 + pub type_: u8_, 819 + pub __bindgen_padding_0: [u8; 3usize], 820 + pub data: u32_, 821 + pub length: u32_, 822 + } 823 + #[repr(C)] 824 + #[derive(Debug, Default)] 825 + pub struct PACKED_REGISTRY_TABLE { 826 + pub size: u32_, 827 + pub numEntries: u32_, 828 + pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>, 829 + } 830 + #[repr(C)] 831 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 832 + pub struct msgqTxHeader { 833 + pub version: u32_, 834 + pub size: u32_, 835 + pub msgSize: u32_, 836 + pub msgCount: u32_, 837 + pub writePtr: u32_, 838 + pub flags: u32_, 839 + pub rxHdrOff: u32_, 840 + pub entryOff: u32_, 841 + } 842 + #[repr(C)] 843 + #[derive(Debug, Default, Copy, Clone, Zeroable)] 844 + pub struct msgqRxHeader { 845 + pub readPtr: u32_, 846 + } 847 + #[repr(C)] 848 + #[repr(align(8))] 849 + #[derive(Zeroable)] 850 + pub struct GSP_MSG_QUEUE_ELEMENT { 851 + pub authTagBuffer: [u8_; 16usize], 852 + pub aadBuffer: [u8_; 16usize], 853 + pub checkSum: u32_, 854 + pub seqNum: u32_, 855 + pub elemCount: u32_, 856 + pub __bindgen_padding_0: [u8; 4usize], 857 + pub rpc: rpc_message_header_v, 858 + } 859 + impl Default for GSP_MSG_QUEUE_ELEMENT { 860 + fn default() -> Self { 861 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 862 + unsafe { 863 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 864 + s.assume_init() 865 + } 866 + } 867 + } 868 + #[repr(C)] 869 + #[derive(Debug, Default)] 870 + pub struct rpc_run_cpu_sequencer_v17_00 { 871 + pub bufferSizeDWord: u32_, 872 + pub cmdIndex: u32_, 873 + pub regSaveArea: [u32_; 8usize], 874 + pub commandBuffer: __IncompleteArrayField<u32_>, 875 + } 876 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE: GSP_SEQ_BUF_OPCODE = 0; 877 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY: GSP_SEQ_BUF_OPCODE = 1; 878 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL: GSP_SEQ_BUF_OPCODE = 2; 879 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US: GSP_SEQ_BUF_OPCODE = 3; 880 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE: GSP_SEQ_BUF_OPCODE = 4; 881 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET: GSP_SEQ_BUF_OPCODE = 5; 882 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START: GSP_SEQ_BUF_OPCODE = 6; 883 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT: GSP_SEQ_BUF_OPCODE = 7; 884 + pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME: GSP_SEQ_BUF_OPCODE = 8; 885 + pub type GSP_SEQ_BUF_OPCODE = ffi::c_uint; 886 + #[repr(C)] 887 + #[derive(Debug, Default, Copy, Clone)] 888 + pub struct GSP_SEQ_BUF_PAYLOAD_REG_WRITE { 889 + pub addr: u32_, 890 + pub val: u32_, 891 + } 892 + #[repr(C)] 893 + #[derive(Debug, Default, Copy, Clone)] 894 + pub struct GSP_SEQ_BUF_PAYLOAD_REG_MODIFY { 895 + pub addr: u32_, 896 + pub mask: u32_, 897 + pub val: u32_, 898 + } 899 + #[repr(C)] 900 + #[derive(Debug, Default, Copy, Clone)] 901 + pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL { 902 + pub addr: u32_, 903 + pub mask: u32_, 904 + pub val: u32_, 905 + pub timeout: u32_, 906 + pub error: u32_, 907 + } 908 + #[repr(C)] 909 + #[derive(Debug, Default, Copy, Clone)] 910 + pub struct GSP_SEQ_BUF_PAYLOAD_DELAY_US { 911 + pub val: u32_, 912 + } 913 + #[repr(C)] 914 + #[derive(Debug, Default, Copy, Clone)] 915 + pub struct GSP_SEQ_BUF_PAYLOAD_REG_STORE { 916 + pub addr: u32_, 917 + pub index: u32_, 918 + } 919 + #[repr(C)] 920 + #[derive(Copy, Clone)] 921 + pub struct GSP_SEQUENCER_BUFFER_CMD { 922 + pub opCode: GSP_SEQ_BUF_OPCODE, 923 + pub payload: GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1, 924 + } 925 + #[repr(C)] 926 + #[derive(Copy, Clone)] 927 + pub union GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 { 928 + pub regWrite: GSP_SEQ_BUF_PAYLOAD_REG_WRITE, 929 + pub regModify: GSP_SEQ_BUF_PAYLOAD_REG_MODIFY, 930 + pub regPoll: GSP_SEQ_BUF_PAYLOAD_REG_POLL, 931 + pub delayUs: GSP_SEQ_BUF_PAYLOAD_DELAY_US, 932 + pub regStore: GSP_SEQ_BUF_PAYLOAD_REG_STORE, 933 + } 934 + impl Default for GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 { 935 + fn default() -> Self { 936 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 937 + unsafe { 938 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 939 + s.assume_init() 940 + } 941 + } 942 + } 943 + impl Default for GSP_SEQUENCER_BUFFER_CMD { 944 + fn default() -> Self { 945 + let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); 946 + unsafe { 947 + ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); 948 + s.assume_init() 949 + } 950 + } 951 + }
+407
drivers/gpu/nova-core/gsp/sequencer.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! GSP Sequencer implementation for Pre-hopper GSP boot sequence. 4 + 5 + use core::{ 6 + array, 7 + mem::{ 8 + size_of, 9 + size_of_val, // 10 + }, 11 + }; 12 + 13 + use kernel::{ 14 + device, 15 + io::poll::read_poll_timeout, 16 + prelude::*, 17 + time::{ 18 + delay::fsleep, 19 + Delta, // 20 + }, 21 + transmute::FromBytes, 22 + types::ARef, // 23 + }; 24 + 25 + use crate::{ 26 + driver::Bar0, 27 + falcon::{ 28 + gsp::Gsp, 29 + sec2::Sec2, 30 + Falcon, // 31 + }, 32 + gsp::{ 33 + cmdq::{ 34 + Cmdq, 35 + MessageFromGsp, // 36 + }, 37 + fw, 38 + }, 39 + num::FromSafeCast, 40 + sbuffer::SBufferIter, 41 + }; 42 + 43 + /// GSP Sequencer information containing the command sequence and data. 44 + struct GspSequence { 45 + /// Current command index for error reporting. 46 + cmd_index: u32, 47 + /// Command data buffer containing the sequence of commands. 48 + cmd_data: KVec<u8>, 49 + } 50 + 51 + impl MessageFromGsp for GspSequence { 52 + const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer; 53 + type InitError = Error; 54 + type Message = fw::RunCpuSequencer; 55 + 56 + fn read( 57 + msg: &Self::Message, 58 + sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>, 59 + ) -> Result<Self, Self::InitError> { 60 + let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?; 61 + Ok(GspSequence { 62 + cmd_index: msg.cmd_index(), 63 + cmd_data, 64 + }) 65 + } 66 + } 67 + 68 + const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>(); 69 + 70 + /// GSP Sequencer Command types with payload data. 71 + /// Commands have an opcode and an opcode-dependent struct. 72 + #[allow(clippy::enum_variant_names)] 73 + pub(crate) enum GspSeqCmd { 74 + RegWrite(fw::RegWritePayload), 75 + RegModify(fw::RegModifyPayload), 76 + RegPoll(fw::RegPollPayload), 77 + DelayUs(fw::DelayUsPayload), 78 + RegStore(fw::RegStorePayload), 79 + CoreReset, 80 + CoreStart, 81 + CoreWaitForHalt, 82 + CoreResume, 83 + } 84 + 85 + impl GspSeqCmd { 86 + /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes. 87 + pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> { 88 + let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?; 89 + let opcode_size = core::mem::size_of::<u32>(); 90 + 91 + let (cmd, size) = match fw_cmd.opcode()? { 92 + fw::SeqBufOpcode::RegWrite => { 93 + let payload = fw_cmd.reg_write_payload()?; 94 + let size = opcode_size + size_of_val(&payload); 95 + (GspSeqCmd::RegWrite(payload), size) 96 + } 97 + fw::SeqBufOpcode::RegModify => { 98 + let payload = fw_cmd.reg_modify_payload()?; 99 + let size = opcode_size + size_of_val(&payload); 100 + (GspSeqCmd::RegModify(payload), size) 101 + } 102 + fw::SeqBufOpcode::RegPoll => { 103 + let payload = fw_cmd.reg_poll_payload()?; 104 + let size = opcode_size + size_of_val(&payload); 105 + (GspSeqCmd::RegPoll(payload), size) 106 + } 107 + fw::SeqBufOpcode::DelayUs => { 108 + let payload = fw_cmd.delay_us_payload()?; 109 + let size = opcode_size + size_of_val(&payload); 110 + (GspSeqCmd::DelayUs(payload), size) 111 + } 112 + fw::SeqBufOpcode::RegStore => { 113 + let payload = fw_cmd.reg_store_payload()?; 114 + let size = opcode_size + size_of_val(&payload); 115 + (GspSeqCmd::RegStore(payload), size) 116 + } 117 + fw::SeqBufOpcode::CoreReset => (GspSeqCmd::CoreReset, opcode_size), 118 + fw::SeqBufOpcode::CoreStart => (GspSeqCmd::CoreStart, opcode_size), 119 + fw::SeqBufOpcode::CoreWaitForHalt => (GspSeqCmd::CoreWaitForHalt, opcode_size), 120 + fw::SeqBufOpcode::CoreResume => (GspSeqCmd::CoreResume, opcode_size), 121 + }; 122 + 123 + if data.len() < size { 124 + dev_err!(dev, "Data is not enough for command"); 125 + return Err(EINVAL); 126 + } 127 + 128 + Ok((cmd, size)) 129 + } 130 + } 131 + 132 + /// GSP Sequencer for executing firmware commands during boot. 133 + pub(crate) struct GspSequencer<'a> { 134 + /// Sequencer information with command data. 135 + seq_info: GspSequence, 136 + /// `Bar0` for register access. 137 + bar: &'a Bar0, 138 + /// SEC2 falcon for core operations. 139 + sec2_falcon: &'a Falcon<Sec2>, 140 + /// GSP falcon for core operations. 141 + gsp_falcon: &'a Falcon<Gsp>, 142 + /// LibOS DMA handle address. 143 + libos_dma_handle: u64, 144 + /// Bootloader application version. 145 + bootloader_app_version: u32, 146 + /// Device for logging. 147 + dev: ARef<device::Device>, 148 + } 149 + 150 + /// Trait for running sequencer commands. 151 + pub(crate) trait GspSeqCmdRunner { 152 + fn run(&self, sequencer: &GspSequencer<'_>) -> Result; 153 + } 154 + 155 + impl GspSeqCmdRunner for fw::RegWritePayload { 156 + fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 157 + let addr = usize::from_safe_cast(self.addr()); 158 + 159 + sequencer.bar.try_write32(self.val(), addr) 160 + } 161 + } 162 + 163 + impl GspSeqCmdRunner for fw::RegModifyPayload { 164 + fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 165 + let addr = usize::from_safe_cast(self.addr()); 166 + 167 + sequencer.bar.try_read32(addr).and_then(|val| { 168 + sequencer 169 + .bar 170 + .try_write32((val & !self.mask()) | self.val(), addr) 171 + }) 172 + } 173 + } 174 + 175 + impl GspSeqCmdRunner for fw::RegPollPayload { 176 + fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 177 + let addr = usize::from_safe_cast(self.addr()); 178 + 179 + // Default timeout to 4 seconds. 180 + let timeout_us = if self.timeout() == 0 { 181 + 4_000_000 182 + } else { 183 + i64::from(self.timeout()) 184 + }; 185 + 186 + // First read. 187 + sequencer.bar.try_read32(addr)?; 188 + 189 + // Poll the requested register with requested timeout. 190 + read_poll_timeout( 191 + || sequencer.bar.try_read32(addr), 192 + |current| (current & self.mask()) == self.val(), 193 + Delta::ZERO, 194 + Delta::from_micros(timeout_us), 195 + ) 196 + .map(|_| ()) 197 + } 198 + } 199 + 200 + impl GspSeqCmdRunner for fw::DelayUsPayload { 201 + fn run(&self, _sequencer: &GspSequencer<'_>) -> Result { 202 + fsleep(Delta::from_micros(i64::from(self.val()))); 203 + Ok(()) 204 + } 205 + } 206 + 207 + impl GspSeqCmdRunner for fw::RegStorePayload { 208 + fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 209 + let addr = usize::from_safe_cast(self.addr()); 210 + 211 + sequencer.bar.try_read32(addr).map(|_| ()) 212 + } 213 + } 214 + 215 + impl GspSeqCmdRunner for GspSeqCmd { 216 + fn run(&self, seq: &GspSequencer<'_>) -> Result { 217 + match self { 218 + GspSeqCmd::RegWrite(cmd) => cmd.run(seq), 219 + GspSeqCmd::RegModify(cmd) => cmd.run(seq), 220 + GspSeqCmd::RegPoll(cmd) => cmd.run(seq), 221 + GspSeqCmd::DelayUs(cmd) => cmd.run(seq), 222 + GspSeqCmd::RegStore(cmd) => cmd.run(seq), 223 + GspSeqCmd::CoreReset => { 224 + seq.gsp_falcon.reset(seq.bar)?; 225 + seq.gsp_falcon.dma_reset(seq.bar); 226 + Ok(()) 227 + } 228 + GspSeqCmd::CoreStart => { 229 + seq.gsp_falcon.start(seq.bar)?; 230 + Ok(()) 231 + } 232 + GspSeqCmd::CoreWaitForHalt => { 233 + seq.gsp_falcon.wait_till_halted(seq.bar)?; 234 + Ok(()) 235 + } 236 + GspSeqCmd::CoreResume => { 237 + // At this point, 'SEC2-RTOS' has been loaded into SEC2 by the sequencer 238 + // but neither SEC2-RTOS nor GSP-RM is running yet. This part of the 239 + // sequencer will start both. 240 + 241 + // Reset the GSP to prepare it for resuming. 242 + seq.gsp_falcon.reset(seq.bar)?; 243 + 244 + // Write the libOS DMA handle to GSP mailboxes. 245 + seq.gsp_falcon.write_mailboxes( 246 + seq.bar, 247 + Some(seq.libos_dma_handle as u32), 248 + Some((seq.libos_dma_handle >> 32) as u32), 249 + ); 250 + 251 + // Start the SEC2 falcon which will trigger GSP-RM to resume on the GSP. 252 + seq.sec2_falcon.start(seq.bar)?; 253 + 254 + // Poll until GSP-RM reload/resume has completed (up to 2 seconds). 255 + seq.gsp_falcon 256 + .check_reload_completed(seq.bar, Delta::from_secs(2))?; 257 + 258 + // Verify SEC2 completed successfully by checking its mailbox for errors. 259 + let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar); 260 + if mbox0 != 0 { 261 + dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0); 262 + return Err(EIO); 263 + } 264 + 265 + // Configure GSP with the bootloader version. 266 + seq.gsp_falcon 267 + .write_os_version(seq.bar, seq.bootloader_app_version); 268 + 269 + // Verify the GSP's RISC-V core is active indicating successful GSP boot. 270 + if !seq.gsp_falcon.is_riscv_active(seq.bar) { 271 + dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n"); 272 + return Err(EIO); 273 + } 274 + Ok(()) 275 + } 276 + } 277 + } 278 + } 279 + 280 + /// Iterator over GSP sequencer commands. 281 + pub(crate) struct GspSeqIter<'a> { 282 + /// Command data buffer. 283 + cmd_data: &'a [u8], 284 + /// Current position in the buffer. 285 + current_offset: usize, 286 + /// Total number of commands to process. 287 + total_cmds: u32, 288 + /// Number of commands processed so far. 289 + cmds_processed: u32, 290 + /// Device for logging. 291 + dev: ARef<device::Device>, 292 + } 293 + 294 + impl<'a> Iterator for GspSeqIter<'a> { 295 + type Item = Result<GspSeqCmd>; 296 + 297 + fn next(&mut self) -> Option<Self::Item> { 298 + // Stop if we've processed all commands or reached the end of data. 299 + if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() { 300 + return None; 301 + } 302 + 303 + // Check if we have enough data for opcode. 304 + if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() { 305 + return Some(Err(EIO)); 306 + } 307 + 308 + let offset = self.current_offset; 309 + 310 + // Handle command creation based on available data, 311 + // zero-pad if necessary (since last command may not be full size). 312 + let mut buffer = [0u8; CMD_SIZE]; 313 + let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() { 314 + CMD_SIZE 315 + } else { 316 + self.cmd_data.len() - offset 317 + }; 318 + buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]); 319 + let cmd_result = GspSeqCmd::new(&buffer, &self.dev); 320 + 321 + cmd_result.map_or_else( 322 + |_err| { 323 + dev_err!(self.dev, "Error parsing command at offset {}", offset); 324 + None 325 + }, 326 + |(cmd, size)| { 327 + self.current_offset += size; 328 + self.cmds_processed += 1; 329 + Some(Ok(cmd)) 330 + }, 331 + ) 332 + } 333 + } 334 + 335 + impl<'a> GspSequencer<'a> { 336 + fn iter(&self) -> GspSeqIter<'_> { 337 + let cmd_data = &self.seq_info.cmd_data[..]; 338 + 339 + GspSeqIter { 340 + cmd_data, 341 + current_offset: 0, 342 + total_cmds: self.seq_info.cmd_index, 343 + cmds_processed: 0, 344 + dev: self.dev.clone(), 345 + } 346 + } 347 + } 348 + 349 + /// Parameters for running the GSP sequencer. 350 + pub(crate) struct GspSequencerParams<'a> { 351 + /// Bootloader application version. 352 + pub(crate) bootloader_app_version: u32, 353 + /// LibOS DMA handle address. 354 + pub(crate) libos_dma_handle: u64, 355 + /// GSP falcon for core operations. 356 + pub(crate) gsp_falcon: &'a Falcon<Gsp>, 357 + /// SEC2 falcon for core operations. 358 + pub(crate) sec2_falcon: &'a Falcon<Sec2>, 359 + /// Device for logging. 360 + pub(crate) dev: ARef<device::Device>, 361 + /// BAR0 for register access. 362 + pub(crate) bar: &'a Bar0, 363 + } 364 + 365 + impl<'a> GspSequencer<'a> { 366 + pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result { 367 + let seq_info = loop { 368 + match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) { 369 + Ok(seq_info) => break seq_info, 370 + Err(ERANGE) => continue, 371 + Err(e) => return Err(e), 372 + } 373 + }; 374 + 375 + let sequencer = GspSequencer { 376 + seq_info, 377 + bar: params.bar, 378 + sec2_falcon: params.sec2_falcon, 379 + gsp_falcon: params.gsp_falcon, 380 + libos_dma_handle: params.libos_dma_handle, 381 + bootloader_app_version: params.bootloader_app_version, 382 + dev: params.dev, 383 + }; 384 + 385 + dev_dbg!(sequencer.dev, "Running CPU Sequencer commands"); 386 + 387 + for cmd_result in sequencer.iter() { 388 + match cmd_result { 389 + Ok(cmd) => cmd.run(&sequencer)?, 390 + Err(e) => { 391 + dev_err!( 392 + sequencer.dev, 393 + "Error running command at index {}", 394 + sequencer.seq_info.cmd_index 395 + ); 396 + return Err(e); 397 + } 398 + } 399 + } 400 + 401 + dev_dbg!( 402 + sequencer.dev, 403 + "CPU Sequencer commands completed successfully" 404 + ); 405 + Ok(()) 406 + } 407 + }
+5
drivers/gpu/nova-core/nova_core.rs
··· 2 2 3 3 //! Nova Core GPU Driver 4 4 5 + #[macro_use] 6 + mod bitfield; 7 + 5 8 mod dma; 6 9 mod driver; 7 10 mod falcon; ··· 13 10 mod gfw; 14 11 mod gpu; 15 12 mod gsp; 13 + mod num; 16 14 mod regs; 15 + mod sbuffer; 17 16 mod util; 18 17 mod vbios; 19 18
+217
drivers/gpu/nova-core/num.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! Numerical helpers functions and traits. 4 + //! 5 + //! This is essentially a staging module for code to mature until it can be moved to the `kernel` 6 + //! crate. 7 + 8 + use kernel::{ 9 + macros::paste, 10 + prelude::*, // 11 + }; 12 + 13 + /// Implements safe `as` conversion functions from a given type into a series of target types. 14 + /// 15 + /// These functions can be used in place of `as`, with the guarantee that they will be lossless. 16 + macro_rules! impl_safe_as { 17 + ($from:ty as { $($into:ty),* }) => { 18 + $( 19 + paste! { 20 + #[doc = ::core::concat!( 21 + "Losslessly converts a [`", 22 + ::core::stringify!($from), 23 + "`] into a [`", 24 + ::core::stringify!($into), 25 + "`].")] 26 + /// 27 + /// This conversion is allowed as it is always lossless. Prefer this over the `as` 28 + /// keyword to ensure no lossy casts are performed. 29 + /// 30 + /// This is for use from a `const` context. For non `const` use, prefer the 31 + /// [`FromSafeCast`] and [`IntoSafeCast`] traits. 32 + /// 33 + /// # Examples 34 + /// 35 + /// ``` 36 + /// use crate::num; 37 + /// 38 + #[doc = ::core::concat!( 39 + "assert_eq!(num::", 40 + ::core::stringify!($from), 41 + "_as_", 42 + ::core::stringify!($into), 43 + "(1", 44 + ::core::stringify!($from), 45 + "), 1", 46 + ::core::stringify!($into), 47 + ");")] 48 + /// ``` 49 + #[allow(unused)] 50 + #[inline(always)] 51 + pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into { 52 + kernel::static_assert!(size_of::<$into>() >= size_of::<$from>()); 53 + 54 + value as $into 55 + } 56 + } 57 + )* 58 + }; 59 + } 60 + 61 + impl_safe_as!(u8 as { u16, u32, u64, usize }); 62 + impl_safe_as!(u16 as { u32, u64, usize }); 63 + impl_safe_as!(u32 as { u64, usize } ); 64 + // `u64` and `usize` have the same size on 64-bit platforms. 65 + #[cfg(CONFIG_64BIT)] 66 + impl_safe_as!(u64 as { usize } ); 67 + 68 + // A `usize` fits into a `u64` on 32 and 64-bit platforms. 69 + #[cfg(any(CONFIG_32BIT, CONFIG_64BIT))] 70 + impl_safe_as!(usize as { u64 }); 71 + 72 + // A `usize` fits into a `u32` on 32-bit platforms. 73 + #[cfg(CONFIG_32BIT)] 74 + impl_safe_as!(usize as { u32 }); 75 + 76 + /// Extension trait providing guaranteed lossless cast to `Self` from `T`. 77 + /// 78 + /// The standard library's `From` implementations do not cover conversions that are not portable or 79 + /// future-proof. For instance, even though it is safe today, `From<usize>` is not implemented for 80 + /// [`u64`] because of the possibility to support larger-than-64bit architectures in the future. 81 + /// 82 + /// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that 83 + /// technically cannot fail, or to use the `as` keyword, which can silently strip data if the 84 + /// destination type is smaller than the source. 85 + /// 86 + /// Both options are hardly acceptable for the kernel. It is also a much more architecture 87 + /// dependent environment, supporting only 32 and 64 bit architectures, with some modules 88 + /// explicitly depending on a specific bus width that could greatly benefit from infallible 89 + /// conversion operations. 90 + /// 91 + /// Thus this extension trait that provides, for the architecture the kernel is built for, safe 92 + /// conversion between types for which such cast is lossless. 93 + /// 94 + /// In other words, this trait is implemented if, for the current build target and with `t: T`, the 95 + /// `t as Self` operation is completely lossless. 96 + /// 97 + /// Prefer this over the `as` keyword to ensure no lossy casts are performed. 98 + /// 99 + /// If you need to perform a conversion in `const` context, use [`u64_as_usize`], [`u32_as_usize`], 100 + /// [`usize_as_u64`], etc. 101 + /// 102 + /// # Examples 103 + /// 104 + /// ``` 105 + /// use crate::num::FromSafeCast; 106 + /// 107 + /// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00u32 as usize); 108 + /// ``` 109 + pub(crate) trait FromSafeCast<T> { 110 + /// Create a `Self` from `value`. This operation is guaranteed to be lossless. 111 + fn from_safe_cast(value: T) -> Self; 112 + } 113 + 114 + impl FromSafeCast<usize> for u64 { 115 + fn from_safe_cast(value: usize) -> Self { 116 + usize_as_u64(value) 117 + } 118 + } 119 + 120 + #[cfg(CONFIG_32BIT)] 121 + impl FromSafeCast<usize> for u32 { 122 + fn from_safe_cast(value: usize) -> Self { 123 + usize_as_u32(value) 124 + } 125 + } 126 + 127 + impl FromSafeCast<u32> for usize { 128 + fn from_safe_cast(value: u32) -> Self { 129 + u32_as_usize(value) 130 + } 131 + } 132 + 133 + #[cfg(CONFIG_64BIT)] 134 + impl FromSafeCast<u64> for usize { 135 + fn from_safe_cast(value: u64) -> Self { 136 + u64_as_usize(value) 137 + } 138 + } 139 + 140 + /// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`] 141 + /// is to [`From`]. 142 + /// 143 + /// See the documentation of [`FromSafeCast`] for the motivation. 144 + /// 145 + /// # Examples 146 + /// 147 + /// ``` 148 + /// use crate::num::IntoSafeCast; 149 + /// 150 + /// assert_eq!(0xf00u32.into_safe_cast(), 0xf00u32 as usize); 151 + /// ``` 152 + pub(crate) trait IntoSafeCast<T> { 153 + /// Convert `self` into a `T`. This operation is guaranteed to be lossless. 154 + fn into_safe_cast(self) -> T; 155 + } 156 + 157 + /// Reverse operation for types implementing [`FromSafeCast`]. 158 + impl<S, T> IntoSafeCast<T> for S 159 + where 160 + T: FromSafeCast<S>, 161 + { 162 + fn into_safe_cast(self) -> T { 163 + T::from_safe_cast(self) 164 + } 165 + } 166 + 167 + /// Implements lossless conversion of a constant from a larger type into a smaller one. 168 + macro_rules! impl_const_into { 169 + ($from:ty => { $($into:ty),* }) => { 170 + $( 171 + paste! { 172 + #[doc = ::core::concat!( 173 + "Performs a build-time safe conversion of a [`", 174 + ::core::stringify!($from), 175 + "`] constant value into a [`", 176 + ::core::stringify!($into), 177 + "`].")] 178 + /// 179 + /// This checks at compile-time that the conversion is lossless, and triggers a build 180 + /// error if it isn't. 181 + /// 182 + /// # Examples 183 + /// 184 + /// ``` 185 + /// use crate::num; 186 + /// 187 + /// // Succeeds because the value of the source fits into the destination's type. 188 + #[doc = ::core::concat!( 189 + "assert_eq!(num::", 190 + ::core::stringify!($from), 191 + "_into_", 192 + ::core::stringify!($into), 193 + "::<1", 194 + ::core::stringify!($from), 195 + ">(), 1", 196 + ::core::stringify!($into), 197 + ");")] 198 + /// ``` 199 + #[allow(unused)] 200 + pub(crate) const fn [<$from _into_ $into>]<const N: $from>() -> $into { 201 + // Make sure that the target type is smaller than the source one. 202 + static_assert!($from::BITS >= $into::BITS); 203 + // CAST: we statically enforced above that `$from` is larger than `$into`, so the 204 + // `as` conversion will be lossless. 205 + build_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from); 206 + 207 + N as $into 208 + } 209 + } 210 + )* 211 + }; 212 + } 213 + 214 + impl_const_into!(usize => { u8, u16, u32 }); 215 + impl_const_into!(u64 => { u8, u16, u32 }); 216 + impl_const_into!(u32 => { u8, u16 }); 217 + impl_const_into!(u16 => { u8 });
+79 -14
drivers/gpu/nova-core/regs.rs
··· 7 7 #[macro_use] 8 8 pub(crate) mod macros; 9 9 10 - use crate::falcon::{ 11 - DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType, FalconFbifTarget, 12 - FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase, PeregrineCoreSelect, 13 - }; 14 - use crate::gpu::{Architecture, Chipset}; 15 10 use kernel::prelude::*; 11 + 12 + use crate::{ 13 + falcon::{ 14 + DmaTrfCmdSize, 15 + FalconCoreRev, 16 + FalconCoreRevSubversion, 17 + FalconFbifMemType, 18 + FalconFbifTarget, 19 + FalconModSelAlgo, 20 + FalconSecurityModel, 21 + PFalcon2Base, 22 + PFalconBase, 23 + PeregrineCoreSelect, // 24 + }, 25 + gpu::{ 26 + Architecture, 27 + Chipset, // 28 + }, 29 + num::FromSafeCast, 30 + }; 16 31 17 32 // PMC 18 33 ··· 40 25 }); 41 26 42 27 impl NV_PMC_BOOT_0 { 43 - /// Combines `architecture_0` and `architecture_1` to obtain the architecture of the chip. 44 - pub(crate) fn architecture(self) -> Result<Architecture> { 45 - Architecture::try_from( 46 - self.architecture_0() | (self.architecture_1() << Self::ARCHITECTURE_0_RANGE.len()), 47 - ) 48 - } 28 + pub(crate) fn is_older_than_fermi(self) -> bool { 29 + // From https://github.com/NVIDIA/open-gpu-doc/tree/master/manuals : 30 + const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u8 = 0xc; 49 31 32 + // Older chips left arch1 zeroed out. That, combined with an arch0 value that is less than 33 + // GF100, means "older than Fermi". 34 + self.architecture_1() == 0 && self.architecture_0() < NV_PMC_BOOT_0_ARCHITECTURE_GF100 35 + } 36 + } 37 + 38 + register!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture information" { 39 + 15:12 minor_revision as u8, "Minor revision of the chip"; 40 + 19:16 major_revision as u8, "Major revision of the chip"; 41 + 23:20 implementation as u8, "Implementation version of the architecture"; 42 + 29:24 architecture as u8 ?=> Architecture, "Architecture value"; 43 + }); 44 + 45 + impl NV_PMC_BOOT_42 { 50 46 /// Combines `architecture` and `implementation` to obtain a code unique to the chipset. 51 47 pub(crate) fn chipset(self) -> Result<Chipset> { 52 48 self.architecture() ··· 66 40 | u32::from(self.implementation()) 67 41 }) 68 42 .and_then(Chipset::try_from) 43 + } 44 + 45 + /// Returns the raw architecture value from the register. 46 + fn architecture_raw(self) -> u8 { 47 + ((self.0 >> Self::ARCHITECTURE_RANGE.start()) & ((1 << Self::ARCHITECTURE_RANGE.len()) - 1)) 48 + as u8 49 + } 50 + } 51 + 52 + impl kernel::fmt::Display for NV_PMC_BOOT_42 { 53 + fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result { 54 + write!( 55 + f, 56 + "boot42 = 0x{:08x} (architecture 0x{:x}, implementation 0x{:x})", 57 + self.0, 58 + self.architecture_raw(), 59 + self.implementation() 60 + ) 69 61 } 70 62 } 71 63 ··· 115 71 30:30 ecc_mode_enabled as bool; 116 72 }); 117 73 74 + register!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 { 75 + 31:0 address as u32; 76 + }); 77 + 118 78 impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE { 119 79 /// Returns the usable framebuffer size, in bytes. 120 80 pub(crate) fn usable_fb_size(self) -> u64 { 121 81 let size = (u64::from(self.lower_mag()) << u64::from(self.lower_scale())) 122 - * kernel::sizes::SZ_1M as u64; 82 + * u64::from_safe_cast(kernel::sizes::SZ_1M); 123 83 124 84 if self.ecc_mode_enabled() { 125 85 // Remove the amount of memory reserved for ECC (one per 16 units). ··· 167 119 // These scratch registers remain powered on even in a low-power state and have a designated group 168 120 // number. 169 121 122 + // Boot Sequence Interface (BSI) register used to determine 123 + // if GSP reload/resume has completed during the boot process. 124 + register!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 { 125 + 26:26 boot_stage_3_handoff as bool; 126 + }); 127 + 170 128 // Privilege level mask register. It dictates whether the host CPU has privilege to access the 171 129 // `PGC6_AON_SECURE_SCRATCH_GROUP_05` register (which it needs to read GFW_BOOT). 172 130 register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128, ··· 212 158 impl NV_USABLE_FB_SIZE_IN_MB { 213 159 /// Returns the usable framebuffer size, in bytes. 214 160 pub(crate) fn usable_fb_size(self) -> u64 { 215 - u64::from(self.value()) * kernel::sizes::SZ_1M as u64 161 + u64::from(self.value()) * u64::from_safe_cast(kernel::sizes::SZ_1M) 216 162 } 217 163 } 218 164 ··· 262 208 }); 263 209 264 210 register!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] { 211 + 31:0 value as u32; 212 + }); 213 + 214 + // Used to store version information about the firmware running 215 + // on the Falcon processor. 216 + register!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] { 265 217 31:0 value as u32; 266 218 }); 267 219 ··· 380 320 381 321 // PRISCV 382 322 383 - register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalconBase[0x00001668] { 323 + register!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] { 324 + 0:0 halted as bool; 325 + 7:7 active_stat as bool; 326 + }); 327 + 328 + register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalcon2Base[0x00000668] { 384 329 0:0 valid as bool; 385 330 4:4 core_select as bool => PeregrineCoreSelect; 386 331 8:8 br_fetch as bool;
+24 -263
drivers/gpu/nova-core/regs/macros.rs
··· 8 8 //! 9 9 //! The `register!` macro in this module provides an intuitive and readable syntax for defining a 10 10 //! dedicated type for each register. Each such type comes with its own field accessors that can 11 - //! return an error if a field's value is invalid. 11 + //! return an error if a field's value is invalid. Please look at the [`bitfield`] macro for the 12 + //! complete syntax of fields definitions. 12 13 13 14 /// Trait providing a base address to be added to the offset of a relative register to obtain 14 15 /// its actual offset. ··· 52 51 /// boot0.set_major_revision(3).set_minor_revision(10).write(&bar); 53 52 /// 54 53 /// // Or, just read and update the register in a single step: 55 - /// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10)); 54 + /// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10)); 56 55 /// ``` 57 - /// 58 - /// Fields are defined as follows: 59 - /// 60 - /// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or 61 - /// `bool`. Note that `bool` fields must have a range of 1 bit. 62 - /// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns 63 - /// the result. 64 - /// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation 65 - /// and returns the result. This is useful with fields for which not all values are valid. 66 56 /// 67 57 /// The documentation strings are optional. If present, they will be added to the type's 68 58 /// definition, or the field getter and setter methods they are attached to. ··· 136 144 /// 0:0 start as bool, "Start the CPU core"; 137 145 /// }); 138 146 /// 139 - /// // The `read`, `write` and `alter` methods of relative registers take an extra `base` argument 147 + /// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument 140 148 /// // that is used to resolve its final address by adding its `BASE` to the offset of the 141 149 /// // register. 142 150 /// 143 151 /// // Start `CPU0`. 144 - /// CPU_CTL::alter(bar, &CPU0, |r| r.set_start(true)); 152 + /// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true)); 145 153 /// 146 154 /// // Start `CPU1`. 147 - /// CPU_CTL::alter(bar, &CPU1, |r| r.set_start(true)); 155 + /// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true)); 148 156 /// 149 157 /// // Aliases can also be defined for relative register. 150 158 /// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" { ··· 152 160 /// }); 153 161 /// 154 162 /// // Start the aliased `CPU0`. 155 - /// CPU_CTL_ALIAS::alter(bar, &CPU0, |r| r.set_alias_start(true)); 163 + /// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true)); 156 164 /// ``` 157 165 /// 158 166 /// ## Arrays of registers ··· 160 168 /// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas 161 169 /// can be defined as an array of identical registers, allowing them to be accessed by index with 162 170 /// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add 163 - /// an `idx` parameter to their `read`, `write` and `alter` methods: 171 + /// an `idx` parameter to their `read`, `write` and `update` methods: 164 172 /// 165 173 /// ```no_run 166 174 /// # fn no_run() -> Result<(), Error> { ··· 276 284 macro_rules! register { 277 285 // Creates a register at a fixed offset of the MMIO space. 278 286 ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => { 279 - register!(@core $name $(, $comment)? { $($fields)* } ); 287 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 280 288 register!(@io_fixed $name @ $offset); 281 289 }; 282 290 283 291 // Creates an alias register of fixed offset register `alias` with its own fields. 284 292 ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => { 285 - register!(@core $name $(, $comment)? { $($fields)* } ); 293 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 286 294 register!(@io_fixed $name @ $alias::OFFSET); 287 295 }; 288 296 289 297 // Creates a register at a relative offset from a base address provider. 290 298 ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => { 291 - register!(@core $name $(, $comment)? { $($fields)* } ); 299 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 292 300 register!(@io_relative $name @ $base [ $offset ]); 293 301 }; 294 302 295 303 // Creates an alias register of relative offset register `alias` with its own fields. 296 304 ($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => { 297 - register!(@core $name $(, $comment)? { $($fields)* } ); 305 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 298 306 register!(@io_relative $name @ $base [ $alias::OFFSET ]); 299 307 }; 300 308 ··· 305 313 } 306 314 ) => { 307 315 static_assert!(::core::mem::size_of::<u32>() <= $stride); 308 - register!(@core $name $(, $comment)? { $($fields)* } ); 316 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 309 317 register!(@io_array $name @ $offset [ $size ; $stride ]); 310 318 }; 311 319 ··· 326 334 $(, $comment:literal)? { $($fields:tt)* } 327 335 ) => { 328 336 static_assert!(::core::mem::size_of::<u32>() <= $stride); 329 - register!(@core $name $(, $comment)? { $($fields)* } ); 337 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 330 338 register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]); 331 339 }; 332 340 ··· 348 356 } 349 357 ) => { 350 358 static_assert!($idx < $alias::SIZE); 351 - register!(@core $name $(, $comment)? { $($fields)* } ); 359 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 352 360 register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] ); 353 361 }; 354 362 ··· 357 365 // to avoid it being interpreted in place of the relative register array alias rule. 358 366 ($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => { 359 367 static_assert!($idx < $alias::SIZE); 360 - register!(@core $name $(, $comment)? { $($fields)* } ); 368 + bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 361 369 register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE ); 362 - }; 363 - 364 - // All rules below are helpers. 365 - 366 - // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, 367 - // `Default`, `BitOr`, and conversion to the value type) and field accessor methods. 368 - (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => { 369 - $( 370 - #[doc=$comment] 371 - )? 372 - #[repr(transparent)] 373 - #[derive(Clone, Copy)] 374 - pub(crate) struct $name(u32); 375 - 376 - impl ::core::ops::BitOr for $name { 377 - type Output = Self; 378 - 379 - fn bitor(self, rhs: Self) -> Self::Output { 380 - Self(self.0 | rhs.0) 381 - } 382 - } 383 - 384 - impl ::core::convert::From<$name> for u32 { 385 - fn from(reg: $name) -> u32 { 386 - reg.0 387 - } 388 - } 389 - 390 - register!(@fields_dispatcher $name { $($fields)* }); 391 - }; 392 - 393 - // Captures the fields and passes them to all the implementers that require field information. 394 - // 395 - // Used to simplify the matching rules for implementers, so they don't need to match the entire 396 - // complex fields rule even though they only make use of part of it. 397 - (@fields_dispatcher $name:ident { 398 - $($hi:tt:$lo:tt $field:ident as $type:tt 399 - $(?=> $try_into_type:ty)? 400 - $(=> $into_type:ty)? 401 - $(, $comment:literal)? 402 - ; 403 - )* 404 - } 405 - ) => { 406 - register!(@field_accessors $name { 407 - $( 408 - $hi:$lo $field as $type 409 - $(?=> $try_into_type)? 410 - $(=> $into_type)? 411 - $(, $comment)? 412 - ; 413 - )* 414 - }); 415 - register!(@debug $name { $($field;)* }); 416 - register!(@default $name { $($field;)* }); 417 - }; 418 - 419 - // Defines all the field getter/methods methods for `$name`. 420 - ( 421 - @field_accessors $name:ident { 422 - $($hi:tt:$lo:tt $field:ident as $type:tt 423 - $(?=> $try_into_type:ty)? 424 - $(=> $into_type:ty)? 425 - $(, $comment:literal)? 426 - ; 427 - )* 428 - } 429 - ) => { 430 - $( 431 - register!(@check_field_bounds $hi:$lo $field as $type); 432 - )* 433 - 434 - #[allow(dead_code)] 435 - impl $name { 436 - $( 437 - register!(@field_accessor $name $hi:$lo $field as $type 438 - $(?=> $try_into_type)? 439 - $(=> $into_type)? 440 - $(, $comment)? 441 - ; 442 - ); 443 - )* 444 - } 445 - }; 446 - 447 - // Boolean fields must have `$hi == $lo`. 448 - (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => { 449 - #[allow(clippy::eq_op)] 450 - const _: () = { 451 - ::kernel::build_assert!( 452 - $hi == $lo, 453 - concat!("boolean field `", stringify!($field), "` covers more than one bit") 454 - ); 455 - }; 456 - }; 457 - 458 - // Non-boolean fields must have `$hi >= $lo`. 459 - (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => { 460 - #[allow(clippy::eq_op)] 461 - const _: () = { 462 - ::kernel::build_assert!( 463 - $hi >= $lo, 464 - concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB") 465 - ); 466 - }; 467 - }; 468 - 469 - // Catches fields defined as `bool` and convert them into a boolean value. 470 - ( 471 - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty 472 - $(, $comment:literal)?; 473 - ) => { 474 - register!( 475 - @leaf_accessor $name $hi:$lo $field 476 - { |f| <$into_type>::from(if f != 0 { true } else { false }) } 477 - $into_type => $into_type $(, $comment)?; 478 - ); 479 - }; 480 - 481 - // Shortcut for fields defined as `bool` without the `=>` syntax. 482 - ( 483 - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?; 484 - ) => { 485 - register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;); 486 - }; 487 - 488 - // Catches the `?=>` syntax for non-boolean fields. 489 - ( 490 - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty 491 - $(, $comment:literal)?; 492 - ) => { 493 - register!(@leaf_accessor $name $hi:$lo $field 494 - { |f| <$try_into_type>::try_from(f as $type) } $try_into_type => 495 - ::core::result::Result< 496 - $try_into_type, 497 - <$try_into_type as ::core::convert::TryFrom<$type>>::Error 498 - > 499 - $(, $comment)?;); 500 - }; 501 - 502 - // Catches the `=>` syntax for non-boolean fields. 503 - ( 504 - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty 505 - $(, $comment:literal)?; 506 - ) => { 507 - register!(@leaf_accessor $name $hi:$lo $field 508 - { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;); 509 - }; 510 - 511 - // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax. 512 - ( 513 - @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt 514 - $(, $comment:literal)?; 515 - ) => { 516 - register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;); 517 - }; 518 - 519 - // Generates the accessor methods for a single field. 520 - ( 521 - @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident 522 - { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?; 523 - ) => { 524 - ::kernel::macros::paste!( 525 - const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi; 526 - const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1); 527 - const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros(); 528 - ); 529 - 530 - $( 531 - #[doc="Returns the value of this field:"] 532 - #[doc=$comment] 533 - )? 534 - #[inline(always)] 535 - pub(crate) fn $field(self) -> $res_type { 536 - ::kernel::macros::paste!( 537 - const MASK: u32 = $name::[<$field:upper _MASK>]; 538 - const SHIFT: u32 = $name::[<$field:upper _SHIFT>]; 539 - ); 540 - let field = ((self.0 & MASK) >> SHIFT); 541 - 542 - $process(field) 543 - } 544 - 545 - ::kernel::macros::paste!( 546 - $( 547 - #[doc="Sets the value of this field:"] 548 - #[doc=$comment] 549 - )? 550 - #[inline(always)] 551 - pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self { 552 - const MASK: u32 = $name::[<$field:upper _MASK>]; 553 - const SHIFT: u32 = $name::[<$field:upper _SHIFT>]; 554 - let value = (u32::from(value) << SHIFT) & MASK; 555 - self.0 = (self.0 & !MASK) | value; 556 - 557 - self 558 - } 559 - ); 560 - }; 561 - 562 - // Generates the `Debug` implementation for `$name`. 563 - (@debug $name:ident { $($field:ident;)* }) => { 564 - impl ::kernel::fmt::Debug for $name { 565 - fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result { 566 - f.debug_struct(stringify!($name)) 567 - .field("<raw>", &::kernel::prelude::fmt!("{:#x}", &self.0)) 568 - $( 569 - .field(stringify!($field), &self.$field()) 570 - )* 571 - .finish() 572 - } 573 - } 574 - }; 575 - 576 - // Generates the `Default` implementation for `$name`. 577 - (@default $name:ident { $($field:ident;)* }) => { 578 - /// Returns a value for the register where all fields are set to their default value. 579 - impl ::core::default::Default for $name { 580 - fn default() -> Self { 581 - #[allow(unused_mut)] 582 - let mut value = Self(Default::default()); 583 - 584 - ::kernel::macros::paste!( 585 - $( 586 - value.[<set_ $field>](Default::default()); 587 - )* 588 - ); 589 - 590 - value 591 - } 592 - } 593 370 }; 594 371 595 372 // Generates the IO accessors for a fixed offset register. ··· 386 625 /// Read the register from its address in `io` and run `f` on its value to obtain a new 387 626 /// value to write back. 388 627 #[inline(always)] 389 - pub(crate) fn alter<const SIZE: usize, T, F>( 628 + pub(crate) fn update<const SIZE: usize, T, F>( 390 629 io: &T, 391 630 f: F, 392 631 ) where ··· 449 688 /// the register's offset to it, then run `f` on its value to obtain a new value to 450 689 /// write back. 451 690 #[inline(always)] 452 - pub(crate) fn alter<const SIZE: usize, T, B, F>( 691 + pub(crate) fn update<const SIZE: usize, T, B, F>( 453 692 io: &T, 454 693 base: &B, 455 694 f: F, ··· 507 746 /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a 508 747 /// new value to write back. 509 748 #[inline(always)] 510 - pub(crate) fn alter<const SIZE: usize, T, F>( 749 + pub(crate) fn update<const SIZE: usize, T, F>( 511 750 io: &T, 512 751 idx: usize, 513 752 f: F, ··· 562 801 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 563 802 /// access was out-of-bounds. 564 803 #[inline(always)] 565 - pub(crate) fn try_alter<const SIZE: usize, T, F>( 804 + pub(crate) fn try_update<const SIZE: usize, T, F>( 566 805 io: &T, 567 806 idx: usize, 568 807 f: F, ··· 571 810 F: ::core::ops::FnOnce(Self) -> Self, 572 811 { 573 812 if idx < Self::SIZE { 574 - Ok(Self::alter(io, idx, f)) 813 + Ok(Self::update(io, idx, f)) 575 814 } else { 576 815 Err(EINVAL) 577 816 } ··· 636 875 /// by `base` and adding the register's offset to it, then run `f` on its value to 637 876 /// obtain a new value to write back. 638 877 #[inline(always)] 639 - pub(crate) fn alter<const SIZE: usize, T, B, F>( 878 + pub(crate) fn update<const SIZE: usize, T, B, F>( 640 879 io: &T, 641 880 base: &B, 642 881 idx: usize, ··· 700 939 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 701 940 /// access was out-of-bounds. 702 941 #[inline(always)] 703 - pub(crate) fn try_alter<const SIZE: usize, T, B, F>( 942 + pub(crate) fn try_update<const SIZE: usize, T, B, F>( 704 943 io: &T, 705 944 base: &B, 706 945 idx: usize, ··· 711 950 F: ::core::ops::FnOnce(Self) -> Self, 712 951 { 713 952 if idx < Self::SIZE { 714 - Ok(Self::alter(io, base, idx, f)) 953 + Ok(Self::update(io, base, idx, f)) 715 954 } else { 716 955 Err(EINVAL) 717 956 }
+227
drivers/gpu/nova-core/sbuffer.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + use core::ops::Deref; 4 + 5 + use kernel::{ 6 + alloc::KVec, 7 + prelude::*, // 8 + }; 9 + 10 + /// A buffer abstraction for discontiguous byte slices. 11 + /// 12 + /// This allows you to treat multiple non-contiguous `&mut [u8]` slices 13 + /// of the same length as a single stream-like read/write buffer. 14 + /// 15 + /// # Examples 16 + /// 17 + /// ``` 18 + // let mut buf1 = [0u8; 5]; 19 + /// let mut buf2 = [0u8; 5]; 20 + /// let mut sbuffer = SBufferIter::new_writer([&mut buf1[..], &mut buf2[..]]); 21 + /// 22 + /// let data = b"hi world!"; 23 + /// sbuffer.write_all(data)?; 24 + /// drop(sbuffer); 25 + /// 26 + /// assert_eq!(buf1, *b"hi wo"); 27 + /// assert_eq!(buf2, *b"rld!\0"); 28 + /// 29 + /// # Ok::<(), Error>(()) 30 + /// ``` 31 + pub(crate) struct SBufferIter<I: Iterator> { 32 + // [`Some`] if we are not at the end of the data yet. 33 + cur_slice: Option<I::Item>, 34 + // All the slices remaining after `cur_slice`. 35 + slices: I, 36 + } 37 + 38 + impl<'a, I> SBufferIter<I> 39 + where 40 + I: Iterator, 41 + { 42 + /// Creates a reader buffer for a discontiguous set of byte slices. 43 + /// 44 + /// # Examples 45 + /// 46 + /// ``` 47 + /// let buf1: [u8; 5] = [0, 1, 2, 3, 4]; 48 + /// let buf2: [u8; 5] = [5, 6, 7, 8, 9]; 49 + /// let sbuffer = SBufferIter::new_reader([&buf1[..], &buf2[..]]); 50 + /// let sum: u8 = sbuffer.sum(); 51 + /// assert_eq!(sum, 45); 52 + /// ``` 53 + pub(crate) fn new_reader(slices: impl IntoIterator<IntoIter = I>) -> Self 54 + where 55 + I: Iterator<Item = &'a [u8]>, 56 + { 57 + Self::new(slices) 58 + } 59 + 60 + /// Creates a writeable buffer for a discontiguous set of byte slices. 61 + /// 62 + /// # Examples 63 + /// 64 + /// ``` 65 + /// let mut buf1 = [0u8; 5]; 66 + /// let mut buf2 = [0u8; 5]; 67 + /// let mut sbuffer = SBufferIter::new_writer([&mut buf1[..], &mut buf2[..]]); 68 + /// sbuffer.write_all(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9][..])?; 69 + /// drop(sbuffer); 70 + /// assert_eq!(buf1, [0, 1, 2, 3, 4]); 71 + /// assert_eq!(buf2, [5, 6, 7, 8, 9]); 72 + /// 73 + /// ``` 74 + pub(crate) fn new_writer(slices: impl IntoIterator<IntoIter = I>) -> Self 75 + where 76 + I: Iterator<Item = &'a mut [u8]>, 77 + { 78 + Self::new(slices) 79 + } 80 + 81 + fn new(slices: impl IntoIterator<IntoIter = I>) -> Self 82 + where 83 + I::Item: Deref<Target = [u8]>, 84 + { 85 + let mut slices = slices.into_iter(); 86 + 87 + Self { 88 + // Skip empty slices. 89 + cur_slice: slices.find(|s| !s.deref().is_empty()), 90 + slices, 91 + } 92 + } 93 + 94 + /// Returns a slice of at most `len` bytes, or [`None`] if we are at the end of the data. 95 + /// 96 + /// If a slice shorter than `len` bytes has been returned, the caller can call this method 97 + /// again until it returns [`None`] to try and obtain the remainder of the data. 98 + /// 99 + /// The closure `f` should split the slice received in it's first parameter 100 + /// at the position given in the second parameter. 101 + fn get_slice_internal( 102 + &mut self, 103 + len: usize, 104 + mut f: impl FnMut(I::Item, usize) -> (I::Item, I::Item), 105 + ) -> Option<I::Item> 106 + where 107 + I::Item: Deref<Target = [u8]>, 108 + { 109 + match self.cur_slice.take() { 110 + None => None, 111 + Some(cur_slice) => { 112 + if len >= cur_slice.len() { 113 + // Caller requested more data than is in the current slice, return it entirely 114 + // and prepare the following slice for being used. Skip empty slices to avoid 115 + // trouble. 116 + self.cur_slice = self.slices.find(|s| !s.is_empty()); 117 + 118 + Some(cur_slice) 119 + } else { 120 + // The current slice can satisfy the request, split it and return a slice of 121 + // the requested size. 122 + let (ret, next) = f(cur_slice, len); 123 + self.cur_slice = Some(next); 124 + 125 + Some(ret) 126 + } 127 + } 128 + } 129 + } 130 + 131 + /// Returns whether this buffer still has data available. 132 + pub(crate) fn is_empty(&self) -> bool { 133 + self.cur_slice.is_none() 134 + } 135 + } 136 + 137 + /// Provides a way to get non-mutable slices of data to read from. 138 + impl<'a, I> SBufferIter<I> 139 + where 140 + I: Iterator<Item = &'a [u8]>, 141 + { 142 + /// Returns a slice of at most `len` bytes, or [`None`] if we are at the end of the data. 143 + /// 144 + /// If a slice shorter than `len` bytes has been returned, the caller can call this method 145 + /// again until it returns [`None`] to try and obtain the remainder of the data. 146 + fn get_slice(&mut self, len: usize) -> Option<&'a [u8]> { 147 + self.get_slice_internal(len, |s, pos| s.split_at(pos)) 148 + } 149 + 150 + /// Ideally we would implement `Read`, but it is not available in `core`. 151 + /// So mimic `std::io::Read::read_exact`. 152 + #[expect(unused)] 153 + pub(crate) fn read_exact(&mut self, mut dst: &mut [u8]) -> Result { 154 + while !dst.is_empty() { 155 + match self.get_slice(dst.len()) { 156 + None => return Err(EINVAL), 157 + Some(src) => { 158 + let dst_slice; 159 + (dst_slice, dst) = dst.split_at_mut(src.len()); 160 + dst_slice.copy_from_slice(src); 161 + } 162 + } 163 + } 164 + 165 + Ok(()) 166 + } 167 + 168 + /// Read all the remaining data into a [`KVec`]. 169 + /// 170 + /// `self` will be empty after this operation. 171 + pub(crate) fn flush_into_kvec(&mut self, flags: kernel::alloc::Flags) -> Result<KVec<u8>> { 172 + let mut buf = KVec::<u8>::new(); 173 + 174 + if let Some(slice) = core::mem::take(&mut self.cur_slice) { 175 + buf.extend_from_slice(slice, flags)?; 176 + } 177 + for slice in &mut self.slices { 178 + buf.extend_from_slice(slice, flags)?; 179 + } 180 + 181 + Ok(buf) 182 + } 183 + } 184 + 185 + /// Provides a way to get mutable slices of data to write into. 186 + impl<'a, I> SBufferIter<I> 187 + where 188 + I: Iterator<Item = &'a mut [u8]>, 189 + { 190 + /// Returns a mutable slice of at most `len` bytes, or [`None`] if we are at the end of the 191 + /// data. 192 + /// 193 + /// If a slice shorter than `len` bytes has been returned, the caller can call this method 194 + /// again until it returns `None` to try and obtain the remainder of the data. 195 + fn get_slice_mut(&mut self, len: usize) -> Option<&'a mut [u8]> { 196 + self.get_slice_internal(len, |s, pos| s.split_at_mut(pos)) 197 + } 198 + 199 + /// Ideally we would implement [`Write`], but it is not available in `core`. 200 + /// So mimic `std::io::Write::write_all`. 201 + pub(crate) fn write_all(&mut self, mut src: &[u8]) -> Result { 202 + while !src.is_empty() { 203 + match self.get_slice_mut(src.len()) { 204 + None => return Err(ETOOSMALL), 205 + Some(dst) => { 206 + let src_slice; 207 + (src_slice, src) = src.split_at(dst.len()); 208 + dst.copy_from_slice(src_slice); 209 + } 210 + } 211 + } 212 + 213 + Ok(()) 214 + } 215 + } 216 + 217 + impl<'a, I> Iterator for SBufferIter<I> 218 + where 219 + I: Iterator<Item = &'a [u8]>, 220 + { 221 + type Item = u8; 222 + 223 + fn next(&mut self) -> Option<Self::Item> { 224 + // Returned slices are guaranteed to not be empty so we can safely index the first entry. 225 + self.get_slice(1).map(|s| s[0]) 226 + } 227 + }
+11 -22
drivers/gpu/nova-core/util.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - use kernel::prelude::*; 4 - use kernel::time::{Delta, Instant, Monotonic}; 5 - 6 - /// Wait until `cond` is true or `timeout` elapsed. 3 + /// Converts a null-terminated byte slice to a string, or `None` if the array does not 4 + /// contains any null byte or contains invalid characters. 7 5 /// 8 - /// When `cond` evaluates to `Some`, its return value is returned. 9 - /// 10 - /// `Err(ETIMEDOUT)` is returned if `timeout` has been reached without `cond` evaluating to 11 - /// `Some`. 12 - /// 13 - /// TODO[DLAY]: replace with `read_poll_timeout` once it is available. 14 - /// (https://lore.kernel.org/lkml/20250220070611.214262-8-fujita.tomonori@gmail.com/) 15 - pub(crate) fn wait_on<R, F: Fn() -> Option<R>>(timeout: Delta, cond: F) -> Result<R> { 16 - let start_time = Instant::<Monotonic>::now(); 6 + /// Contrary to [`kernel::str::CStr::from_bytes_with_nul`], the null byte can be anywhere in the 7 + /// slice, and not only in the last position. 8 + pub(crate) fn str_from_null_terminated(bytes: &[u8]) -> Option<&str> { 9 + use kernel::str::CStr; 17 10 18 - loop { 19 - if let Some(ret) = cond() { 20 - return Ok(ret); 21 - } 22 - 23 - if start_time.elapsed().as_nanos() > timeout.as_nanos() { 24 - return Err(ETIMEDOUT); 25 - } 26 - } 11 + bytes 12 + .iter() 13 + .position(|&b| b == 0) 14 + .and_then(|null_pos| CStr::from_bytes_with_nul(&bytes[..=null_pos]).ok()) 15 + .and_then(|cstr| cstr.to_str().ok()) 27 16 }
+181 -242
drivers/gpu/nova-core/vbios.rs
··· 2 2 3 3 //! VBIOS extraction and parsing. 4 4 5 - use crate::driver::Bar0; 6 - use crate::firmware::fwsec::Bcrt30Rsa3kSignature; 7 - use crate::firmware::FalconUCodeDescV3; 8 5 use core::convert::TryFrom; 9 - use kernel::device; 10 - use kernel::error::Result; 11 - use kernel::prelude::*; 12 - use kernel::ptr::{Alignable, Alignment}; 13 - use kernel::types::ARef; 6 + 7 + use kernel::{ 8 + device, 9 + prelude::*, 10 + ptr::{ 11 + Alignable, 12 + Alignment, // 13 + }, 14 + transmute::FromBytes, 15 + types::ARef, 16 + }; 17 + 18 + use crate::{ 19 + driver::Bar0, 20 + firmware::{ 21 + fwsec::Bcrt30Rsa3kSignature, 22 + FalconUCodeDescV3, // 23 + }, 24 + num::FromSafeCast, 25 + }; 14 26 15 27 /// The offset of the VBIOS ROM in the BAR0 space. 16 28 const ROM_OFFSET: usize = 0x300000; ··· 33 21 /// The bit in the last image indicator byte for the PCI Data Structure that 34 22 /// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit. 35 23 const LAST_IMAGE_BIT_MASK: u8 = 0x80; 24 + 25 + /// BIOS Image Type from PCI Data Structure code_type field. 26 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 27 + #[repr(u8)] 28 + enum BiosImageType { 29 + /// PC-AT compatible BIOS image (x86 legacy) 30 + PciAt = 0x00, 31 + /// EFI (Extensible Firmware Interface) BIOS image 32 + Efi = 0x03, 33 + /// NBSI (Notebook System Information) BIOS image 34 + Nbsi = 0x70, 35 + /// FwSec (Firmware Security) BIOS image 36 + FwSec = 0xE0, 37 + } 38 + 39 + impl TryFrom<u8> for BiosImageType { 40 + type Error = Error; 41 + 42 + fn try_from(code: u8) -> Result<Self> { 43 + match code { 44 + 0x00 => Ok(Self::PciAt), 45 + 0x03 => Ok(Self::Efi), 46 + 0x70 => Ok(Self::Nbsi), 47 + 0xE0 => Ok(Self::FwSec), 48 + _ => Err(EINVAL), 49 + } 50 + } 51 + } 36 52 37 53 // PMU lookup table entry types. Used to locate PMU table entries 38 54 // in the Fwsec image, corresponding to falcon ucodes. ··· 237 197 238 198 // Parse all VBIOS images in the ROM 239 199 for image_result in VbiosIterator::new(dev, bar0)? { 240 - let full_image = image_result?; 200 + let image = image_result?; 241 201 242 202 dev_dbg!( 243 203 dev, 244 - "Found BIOS image: size: {:#x}, type: {}, last: {}\n", 245 - full_image.image_size_bytes(), 246 - full_image.image_type_str(), 247 - full_image.is_last() 204 + "Found BIOS image: size: {:#x}, type: {:?}, last: {}\n", 205 + image.image_size_bytes(), 206 + image.image_type(), 207 + image.is_last() 248 208 ); 249 209 250 - // Get references to images we will need after the loop, in order to 251 - // setup the falcon data offset. 252 - match full_image { 253 - BiosImage::PciAt(image) => { 254 - pci_at_image = Some(image); 210 + // Convert to a specific image type 211 + match BiosImageType::try_from(image.pcir.code_type) { 212 + Ok(BiosImageType::PciAt) => { 213 + pci_at_image = Some(PciAtBiosImage::try_from(image)?); 255 214 } 256 - BiosImage::FwSec(image) => { 215 + Ok(BiosImageType::FwSec) => { 216 + let fwsec = FwSecBiosBuilder { 217 + base: image, 218 + falcon_data_offset: None, 219 + pmu_lookup_table: None, 220 + falcon_ucode_offset: None, 221 + }; 257 222 if first_fwsec_image.is_none() { 258 - first_fwsec_image = Some(image); 223 + first_fwsec_image = Some(fwsec); 259 224 } else { 260 - second_fwsec_image = Some(image); 225 + second_fwsec_image = Some(fwsec); 261 226 } 262 227 } 263 - // For now we don't need to handle these 264 - BiosImage::Efi(_image) => {} 265 - BiosImage::Nbsi(_image) => {} 228 + _ => { 229 + // Ignore other image types or unknown types 230 + } 266 231 } 267 232 } 268 233 ··· 325 280 max_runtime_image_len: u16, 326 281 } 327 282 283 + // SAFETY: all bit patterns are valid for `PcirStruct`. 284 + unsafe impl FromBytes for PcirStruct {} 285 + 328 286 impl PcirStruct { 329 287 fn new(dev: &device::Device, data: &[u8]) -> Result<Self> { 330 - if data.len() < core::mem::size_of::<PcirStruct>() { 331 - dev_err!(dev, "Not enough data for PcirStruct\n"); 332 - return Err(EINVAL); 333 - } 334 - 335 - let mut signature = [0u8; 4]; 336 - signature.copy_from_slice(&data[0..4]); 288 + let (pcir, _) = PcirStruct::from_bytes_copy_prefix(data).ok_or(EINVAL)?; 337 289 338 290 // Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e). 339 - if &signature != b"PCIR" && &signature != b"NPDS" { 340 - dev_err!(dev, "Invalid signature for PcirStruct: {:?}\n", signature); 291 + if &pcir.signature != b"PCIR" && &pcir.signature != b"NPDS" { 292 + dev_err!( 293 + dev, 294 + "Invalid signature for PcirStruct: {:?}\n", 295 + pcir.signature 296 + ); 341 297 return Err(EINVAL); 342 298 } 343 299 344 - let mut class_code = [0u8; 3]; 345 - class_code.copy_from_slice(&data[13..16]); 346 - 347 - let image_len = u16::from_le_bytes([data[16], data[17]]); 348 - if image_len == 0 { 300 + if pcir.image_len == 0 { 349 301 dev_err!(dev, "Invalid image length: 0\n"); 350 302 return Err(EINVAL); 351 303 } 352 304 353 - Ok(PcirStruct { 354 - signature, 355 - vendor_id: u16::from_le_bytes([data[4], data[5]]), 356 - device_id: u16::from_le_bytes([data[6], data[7]]), 357 - device_list_ptr: u16::from_le_bytes([data[8], data[9]]), 358 - pci_data_struct_len: u16::from_le_bytes([data[10], data[11]]), 359 - pci_data_struct_rev: data[12], 360 - class_code, 361 - image_len, 362 - vendor_rom_rev: u16::from_le_bytes([data[18], data[19]]), 363 - code_type: data[20], 364 - last_image: data[21], 365 - max_runtime_image_len: u16::from_le_bytes([data[22], data[23]]), 366 - }) 305 + Ok(pcir) 367 306 } 368 307 369 308 /// Check if this is the last image in the ROM. ··· 357 328 358 329 /// Calculate image size in bytes from 512-byte blocks. 359 330 fn image_size_bytes(&self) -> usize { 360 - self.image_len as usize * 512 331 + usize::from(self.image_len) * 512 361 332 } 362 333 } 363 334 ··· 385 356 checksum: u8, 386 357 } 387 358 359 + // SAFETY: all bit patterns are valid for `BitHeader`. 360 + unsafe impl FromBytes for BitHeader {} 361 + 388 362 impl BitHeader { 389 363 fn new(data: &[u8]) -> Result<Self> { 390 - if data.len() < core::mem::size_of::<Self>() { 391 - return Err(EINVAL); 392 - } 393 - 394 - let mut signature = [0u8; 4]; 395 - signature.copy_from_slice(&data[2..6]); 364 + let (header, _) = BitHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?; 396 365 397 366 // Check header ID and signature 398 - let id = u16::from_le_bytes([data[0], data[1]]); 399 - if id != 0xB8FF || &signature != b"BIT\0" { 367 + if header.id != 0xB8FF || &header.signature != b"BIT\0" { 400 368 return Err(EINVAL); 401 369 } 402 370 403 - Ok(BitHeader { 404 - id, 405 - signature, 406 - bcd_version: u16::from_le_bytes([data[6], data[7]]), 407 - header_size: data[8], 408 - token_size: data[9], 409 - token_entries: data[10], 410 - checksum: data[11], 411 - }) 371 + Ok(header) 412 372 } 413 373 } 414 374 ··· 424 406 let header = &image.bit_header; 425 407 426 408 // Offset to the first token entry 427 - let tokens_start = image.bit_offset + header.header_size as usize; 409 + let tokens_start = image.bit_offset + usize::from(header.header_size); 428 410 429 - for i in 0..header.token_entries as usize { 430 - let entry_offset = tokens_start + (i * header.token_size as usize); 411 + for i in 0..usize::from(header.token_entries) { 412 + let entry_offset = tokens_start + (i * usize::from(header.token_size)); 431 413 432 414 // Make sure we don't go out of bounds 433 - if entry_offset + header.token_size as usize > image.base.data.len() { 415 + if entry_offset + usize::from(header.token_size) > image.base.data.len() { 434 416 return Err(EINVAL); 435 417 } 436 418 ··· 548 530 last_image: u8, 549 531 } 550 532 533 + // SAFETY: all bit patterns are valid for `NpdeStruct`. 534 + unsafe impl FromBytes for NpdeStruct {} 535 + 551 536 impl NpdeStruct { 552 537 fn new(dev: &device::Device, data: &[u8]) -> Option<Self> { 553 - if data.len() < core::mem::size_of::<Self>() { 554 - dev_dbg!(dev, "Not enough data for NpdeStruct\n"); 555 - return None; 556 - } 557 - 558 - let mut signature = [0u8; 4]; 559 - signature.copy_from_slice(&data[0..4]); 538 + let (npde, _) = NpdeStruct::from_bytes_copy_prefix(data)?; 560 539 561 540 // Signature should be "NPDE" (0x4544504E). 562 - if &signature != b"NPDE" { 563 - dev_dbg!(dev, "Invalid signature for NpdeStruct: {:?}\n", signature); 541 + if &npde.signature != b"NPDE" { 542 + dev_dbg!( 543 + dev, 544 + "Invalid signature for NpdeStruct: {:?}\n", 545 + npde.signature 546 + ); 564 547 return None; 565 548 } 566 549 567 - let subimage_len = u16::from_le_bytes([data[8], data[9]]); 568 - if subimage_len == 0 { 550 + if npde.subimage_len == 0 { 569 551 dev_dbg!(dev, "Invalid subimage length: 0\n"); 570 552 return None; 571 553 } 572 554 573 - Some(NpdeStruct { 574 - signature, 575 - npci_data_ext_rev: u16::from_le_bytes([data[4], data[5]]), 576 - npci_data_ext_len: u16::from_le_bytes([data[6], data[7]]), 577 - subimage_len, 578 - last_image: data[10], 579 - }) 555 + Some(npde) 580 556 } 581 557 582 558 /// Check if this is the last image in the ROM. ··· 580 568 581 569 /// Calculate image size in bytes from 512-byte blocks. 582 570 fn image_size_bytes(&self) -> usize { 583 - self.subimage_len as usize * 512 571 + usize::from(self.subimage_len) * 512 584 572 } 585 573 586 574 /// Try to find NPDE in the data, the NPDE is right after the PCIR. ··· 592 580 ) -> Option<Self> { 593 581 // Calculate the offset where NPDE might be located 594 582 // NPDE should be right after the PCIR structure, aligned to 16 bytes 595 - let pcir_offset = rom_header.pci_data_struct_offset as usize; 596 - let npde_start = (pcir_offset + pcir.pci_data_struct_len as usize + 0x0F) & !0x0F; 583 + let pcir_offset = usize::from(rom_header.pci_data_struct_offset); 584 + let npde_start = (pcir_offset + usize::from(pcir.pci_data_struct_len) + 0x0F) & !0x0F; 597 585 598 586 // Check if we have enough data 599 587 if npde_start + core::mem::size_of::<Self>() > data.len() { ··· 606 594 } 607 595 } 608 596 609 - // Use a macro to implement BiosImage enum and methods. This avoids having to 610 - // repeat each enum type when implementing functions like base() in BiosImage. 611 - macro_rules! bios_image { 612 - ( 613 - $($variant:ident: $class:ident),* $(,)? 614 - ) => { 615 - // BiosImage enum with variants for each image type 616 - enum BiosImage { 617 - $($variant($class)),* 618 - } 619 - 620 - impl BiosImage { 621 - /// Get a reference to the common BIOS image data regardless of type 622 - fn base(&self) -> &BiosImageBase { 623 - match self { 624 - $(Self::$variant(img) => &img.base),* 625 - } 626 - } 627 - 628 - /// Returns a string representing the type of BIOS image 629 - fn image_type_str(&self) -> &'static str { 630 - match self { 631 - $(Self::$variant(_) => stringify!($variant)),* 632 - } 633 - } 634 - } 635 - } 636 - } 637 - 638 - impl BiosImage { 639 - /// Check if this is the last image. 640 - fn is_last(&self) -> bool { 641 - let base = self.base(); 642 - 643 - // For NBSI images (type == 0x70), return true as they're 644 - // considered the last image 645 - if matches!(self, Self::Nbsi(_)) { 646 - return true; 647 - } 648 - 649 - // For other image types, check the NPDE first if available 650 - if let Some(ref npde) = base.npde { 651 - return npde.is_last(); 652 - } 653 - 654 - // Otherwise, fall back to checking the PCIR last_image flag 655 - base.pcir.is_last() 656 - } 657 - 658 - /// Get the image size in bytes. 659 - fn image_size_bytes(&self) -> usize { 660 - let base = self.base(); 661 - 662 - // Prefer NPDE image size if available 663 - if let Some(ref npde) = base.npde { 664 - return npde.image_size_bytes(); 665 - } 666 - 667 - // Otherwise, fall back to the PCIR image size 668 - base.pcir.image_size_bytes() 669 - } 670 - 671 - /// Create a [`BiosImageBase`] from a byte slice and convert it to a [`BiosImage`] which 672 - /// triggers the constructor of the specific BiosImage enum variant. 673 - fn new(dev: &device::Device, data: &[u8]) -> Result<Self> { 674 - let base = BiosImageBase::new(dev, data)?; 675 - let image = base.into_image().inspect_err(|e| { 676 - dev_err!(dev, "Failed to create BiosImage: {:?}\n", e); 677 - })?; 678 - 679 - Ok(image) 680 - } 681 - } 682 - 683 - bios_image! { 684 - PciAt: PciAtBiosImage, // PCI-AT compatible BIOS image 685 - Efi: EfiBiosImage, // EFI (Extensible Firmware Interface) 686 - Nbsi: NbsiBiosImage, // NBSI (Nvidia Bios System Interface) 687 - FwSec: FwSecBiosBuilder, // FWSEC (Firmware Security) 688 - } 689 - 690 597 /// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain. 691 598 /// 692 599 /// It contains the BIT header and the BIT tokens. 693 600 struct PciAtBiosImage { 694 - base: BiosImageBase, 601 + base: BiosImage, 695 602 bit_header: BitHeader, 696 603 bit_offset: usize, 697 604 } 698 605 606 + #[expect(dead_code)] 699 607 struct EfiBiosImage { 700 - base: BiosImageBase, 608 + base: BiosImage, 701 609 // EFI-specific fields can be added here in the future. 702 610 } 703 611 612 + #[expect(dead_code)] 704 613 struct NbsiBiosImage { 705 - base: BiosImageBase, 614 + base: BiosImage, 706 615 // NBSI-specific fields can be added here in the future. 707 616 } 708 617 709 618 struct FwSecBiosBuilder { 710 - base: BiosImageBase, 619 + base: BiosImage, 711 620 /// These are temporary fields that are used during the construction of the 712 621 /// [`FwSecBiosBuilder`]. 713 622 /// ··· 647 714 /// 648 715 /// The PMU table contains voltage/frequency tables as well as a pointer to the Falcon Ucode. 649 716 pub(crate) struct FwSecBiosImage { 650 - base: BiosImageBase, 717 + base: BiosImage, 651 718 /// The offset of the Falcon ucode. 652 719 falcon_ucode_offset: usize, 653 720 } 654 721 655 - // Convert from BiosImageBase to BiosImage 656 - impl TryFrom<BiosImageBase> for BiosImage { 657 - type Error = Error; 658 - 659 - fn try_from(base: BiosImageBase) -> Result<Self> { 660 - match base.pcir.code_type { 661 - 0x00 => Ok(BiosImage::PciAt(base.try_into()?)), 662 - 0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })), 663 - 0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })), 664 - 0xE0 => Ok(BiosImage::FwSec(FwSecBiosBuilder { 665 - base, 666 - falcon_data_offset: None, 667 - pmu_lookup_table: None, 668 - falcon_ucode_offset: None, 669 - })), 670 - _ => Err(EINVAL), 671 - } 672 - } 673 - } 674 - 675 722 /// BIOS Image structure containing various headers and reference fields to all BIOS images. 676 723 /// 677 - /// Each BiosImage type has a BiosImageBase type along with other image-specific fields. Note that 678 - /// Rust favors composition of types over inheritance. 724 + /// A BiosImage struct is embedded into all image types and implements common operations. 679 725 #[expect(dead_code)] 680 - struct BiosImageBase { 726 + struct BiosImage { 681 727 /// Used for logging. 682 728 dev: ARef<device::Device>, 683 729 /// PCI ROM Expansion Header ··· 669 757 data: KVec<u8>, 670 758 } 671 759 672 - impl BiosImageBase { 673 - fn into_image(self) -> Result<BiosImage> { 674 - BiosImage::try_from(self) 760 + impl BiosImage { 761 + /// Get the image size in bytes. 762 + fn image_size_bytes(&self) -> usize { 763 + // Prefer NPDE image size if available 764 + if let Some(ref npde) = self.npde { 765 + npde.image_size_bytes() 766 + } else { 767 + // Otherwise, fall back to the PCIR image size 768 + self.pcir.image_size_bytes() 769 + } 675 770 } 676 771 677 - /// Creates a new BiosImageBase from raw byte data. 772 + /// Get the BIOS image type. 773 + fn image_type(&self) -> Result<BiosImageType> { 774 + BiosImageType::try_from(self.pcir.code_type) 775 + } 776 + 777 + /// Check if this is the last image. 778 + fn is_last(&self) -> bool { 779 + // For NBSI images, return true as they're considered the last image. 780 + if self.image_type() == Ok(BiosImageType::Nbsi) { 781 + return true; 782 + } 783 + 784 + // For other image types, check the NPDE first if available 785 + if let Some(ref npde) = self.npde { 786 + return npde.is_last(); 787 + } 788 + 789 + // Otherwise, fall back to checking the PCIR last_image flag 790 + self.pcir.is_last() 791 + } 792 + 793 + /// Creates a new BiosImage from raw byte data. 678 794 fn new(dev: &device::Device, data: &[u8]) -> Result<Self> { 679 795 // Ensure we have enough data for the ROM header. 680 796 if data.len() < 26 { ··· 715 775 .inspect_err(|e| dev_err!(dev, "Failed to create PciRomHeader: {:?}\n", e))?; 716 776 717 777 // Get the PCI Data Structure using the pointer from the ROM header. 718 - let pcir_offset = rom_header.pci_data_struct_offset as usize; 778 + let pcir_offset = usize::from(rom_header.pci_data_struct_offset); 719 779 let pcir_data = data 720 780 .get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>()) 721 781 .ok_or(EINVAL) ··· 742 802 let mut data_copy = KVec::new(); 743 803 data_copy.extend_from_slice(data, GFP_KERNEL)?; 744 804 745 - Ok(BiosImageBase { 805 + Ok(BiosImage { 746 806 dev: dev.into(), 747 807 rom_header, 748 808 pcir, ··· 783 843 let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?; 784 844 785 845 // Make sure we don't go out of bounds 786 - if token.data_offset as usize + 4 > self.base.data.len() { 846 + if usize::from(token.data_offset) + 4 > self.base.data.len() { 787 847 return Err(EINVAL); 788 848 } 789 849 790 850 // read the 4 bytes at the offset specified in the token 791 - let offset = token.data_offset as usize; 851 + let offset = usize::from(token.data_offset); 792 852 let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| { 793 853 dev_err!(self.base.dev, "Failed to convert data slice to array"); 794 854 EINVAL ··· 796 856 797 857 let data_ptr = u32::from_le_bytes(bytes); 798 858 799 - if (data_ptr as usize) < self.base.data.len() { 859 + if (usize::from_safe_cast(data_ptr)) < self.base.data.len() { 800 860 dev_err!(self.base.dev, "Falcon data pointer out of bounds\n"); 801 861 return Err(EINVAL); 802 862 } ··· 805 865 } 806 866 } 807 867 808 - impl TryFrom<BiosImageBase> for PciAtBiosImage { 868 + impl TryFrom<BiosImage> for PciAtBiosImage { 809 869 type Error = Error; 810 870 811 - fn try_from(base: BiosImageBase) -> Result<Self> { 871 + fn try_from(base: BiosImage) -> Result<Self> { 812 872 let data_slice = &base.data; 813 873 let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?; 814 874 ··· 844 904 } 845 905 } 846 906 907 + #[repr(C)] 908 + struct PmuLookupTableHeader { 909 + version: u8, 910 + header_len: u8, 911 + entry_len: u8, 912 + entry_count: u8, 913 + } 914 + 915 + // SAFETY: all bit patterns are valid for `PmuLookupTableHeader`. 916 + unsafe impl FromBytes for PmuLookupTableHeader {} 917 + 847 918 /// The [`PmuLookupTableEntry`] structure is used to find the [`PmuLookupTableEntry`] for a given 848 919 /// application ID. 849 920 /// 850 921 /// The table of entries is pointed to by the falcon data pointer in the BIT table, and is used to 851 922 /// locate the Falcon Ucode. 852 - #[expect(dead_code)] 853 923 struct PmuLookupTable { 854 - version: u8, 855 - header_len: u8, 856 - entry_len: u8, 857 - entry_count: u8, 924 + header: PmuLookupTableHeader, 858 925 table_data: KVec<u8>, 859 926 } 860 927 861 928 impl PmuLookupTable { 862 929 fn new(dev: &device::Device, data: &[u8]) -> Result<Self> { 863 - if data.len() < 4 { 864 - return Err(EINVAL); 865 - } 930 + let (header, _) = PmuLookupTableHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?; 866 931 867 - let header_len = data[1] as usize; 868 - let entry_len = data[2] as usize; 869 - let entry_count = data[3] as usize; 932 + let header_len = usize::from(header.header_len); 933 + let entry_len = usize::from(header.entry_len); 934 + let entry_count = usize::from(header.entry_count); 870 935 871 936 let required_bytes = header_len + (entry_count * entry_len); 872 937 ··· 892 947 dev_dbg!(dev, "PMU entry: {:02x?}\n", &data[i..][..entry_len]); 893 948 } 894 949 895 - Ok(PmuLookupTable { 896 - version: data[0], 897 - header_len: header_len as u8, 898 - entry_len: entry_len as u8, 899 - entry_count: entry_count as u8, 900 - table_data, 901 - }) 950 + Ok(PmuLookupTable { header, table_data }) 902 951 } 903 952 904 953 fn lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry> { 905 - if idx >= self.entry_count { 954 + if idx >= self.header.entry_count { 906 955 return Err(EINVAL); 907 956 } 908 957 909 - let index = (idx as usize) * self.entry_len as usize; 958 + let index = (usize::from(idx)) * usize::from(self.header.entry_len); 910 959 PmuLookupTableEntry::new(&self.table_data[index..]) 911 960 } 912 961 913 962 // find entry by type value 914 963 fn find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry> { 915 - for i in 0..self.entry_count { 964 + for i in 0..self.header.entry_count { 916 965 let entry = self.lookup_index(i)?; 917 966 if entry.application_id == entry_type { 918 967 return Ok(entry); ··· 923 984 pci_at_image: &PciAtBiosImage, 924 985 first_fwsec: &FwSecBiosBuilder, 925 986 ) -> Result { 926 - let mut offset = pci_at_image.falcon_data_ptr()? as usize; 987 + let mut offset = usize::from_safe_cast(pci_at_image.falcon_data_ptr()?); 927 988 let mut pmu_in_first_fwsec = false; 928 989 929 990 // The falcon data pointer assumes that the PciAt and FWSEC images ··· 964 1025 .find_entry_by_type(FALCON_UCODE_ENTRY_APPID_FWSEC_PROD) 965 1026 { 966 1027 Ok(entry) => { 967 - let mut ucode_offset = entry.data as usize; 1028 + let mut ucode_offset = usize::from_safe_cast(entry.data); 968 1029 ucode_offset -= pci_at_image.base.data.len(); 969 1030 if ucode_offset < first_fwsec.base.data.len() { 970 1031 dev_err!(self.base.dev, "Falcon Ucode offset not in second Fwsec.\n"); ··· 1050 1111 1051 1112 // The ucode data follows the descriptor. 1052 1113 let ucode_data_offset = falcon_ucode_offset + desc.size(); 1053 - let size = (desc.imem_load_size + desc.dmem_load_size) as usize; 1114 + let size = usize::from_safe_cast(desc.imem_load_size + desc.dmem_load_size); 1054 1115 1055 1116 // Get the data slice, checking bounds in a single operation. 1056 1117 self.base ··· 1069 1130 pub(crate) fn sigs(&self, desc: &FalconUCodeDescV3) -> Result<&[Bcrt30Rsa3kSignature]> { 1070 1131 // The signatures data follows the descriptor. 1071 1132 let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>(); 1072 - let sigs_size = 1073 - desc.signature_count as usize * core::mem::size_of::<Bcrt30Rsa3kSignature>(); 1133 + let sigs_count = usize::from(desc.signature_count); 1134 + let sigs_size = sigs_count * core::mem::size_of::<Bcrt30Rsa3kSignature>(); 1074 1135 1075 1136 // Make sure the data is within bounds. 1076 1137 if sigs_data_offset + sigs_size > self.base.data.len() { ··· 1090 1151 .as_ptr() 1091 1152 .add(sigs_data_offset) 1092 1153 .cast::<Bcrt30Rsa3kSignature>(), 1093 - desc.signature_count as usize, 1154 + sigs_count, 1094 1155 ) 1095 1156 }) 1096 1157 }
+16
include/drm/drm_gpuvm.h
··· 27 27 28 28 #include <linux/dma-resv.h> 29 29 #include <linux/list.h> 30 + #include <linux/llist.h> 30 31 #include <linux/rbtree.h> 31 32 #include <linux/types.h> 32 33 ··· 153 152 154 153 void drm_gpuva_link(struct drm_gpuva *va, struct drm_gpuvm_bo *vm_bo); 155 154 void drm_gpuva_unlink(struct drm_gpuva *va); 155 + void drm_gpuva_unlink_defer(struct drm_gpuva *va); 156 156 157 157 struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm, 158 158 u64 addr, u64 range); ··· 333 331 */ 334 332 spinlock_t lock; 335 333 } evict; 334 + 335 + /** 336 + * @bo_defer: structure holding vm_bos that need to be destroyed 337 + */ 338 + struct llist_head bo_defer; 336 339 }; 337 340 338 341 void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, ··· 721 714 * &drm_gpuvms evict list. 722 715 */ 723 716 struct list_head evict; 717 + 718 + /** 719 + * @list.entry.bo_defer: List entry to attach to 720 + * the &drm_gpuvms bo_defer list. 721 + */ 722 + struct llist_node bo_defer; 724 723 } entry; 725 724 } list; 726 725 }; ··· 758 745 } 759 746 760 747 bool drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo); 748 + 749 + bool drm_gpuvm_bo_put_deferred(struct drm_gpuvm_bo *vm_bo); 750 + void drm_gpuvm_bo_deferred_cleanup(struct drm_gpuvm *gpuvm); 761 751 762 752 struct drm_gpuvm_bo * 763 753 drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,
+3
init/Kconfig
··· 147 147 # https://github.com/llvm/llvm-project/pull/130661 148 148 def_bool LD_IS_BFD || LLD_VERSION >= 210000 149 149 150 + config RUSTC_HAS_SLICE_AS_FLATTENED 151 + def_bool RUSTC_VERSION >= 108000 152 + 150 153 config RUSTC_HAS_COERCE_POINTEE 151 154 def_bool RUSTC_VERSION >= 108400 152 155
+24 -29
rust/kernel/drm/gem/mod.rs
··· 55 55 unsafe fn from_raw<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self; 56 56 } 57 57 58 - // SAFETY: All gem objects are refcounted. 59 - unsafe impl<T: IntoGEMObject> AlwaysRefCounted for T { 60 - fn inc_ref(&self) { 61 - // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. 62 - unsafe { bindings::drm_gem_object_get(self.as_raw()) }; 63 - } 64 - 65 - unsafe fn dec_ref(obj: NonNull<Self>) { 66 - // SAFETY: We either hold the only refcount on `obj`, or one of many - meaning that no one 67 - // else could possibly hold a mutable reference to `obj` and thus this immutable reference 68 - // is safe. 69 - let obj = unsafe { obj.as_ref() }.as_raw(); 70 - 71 - // SAFETY: 72 - // - The safety requirements guarantee that the refcount is non-zero. 73 - // - We hold no references to `obj` now, making it safe for us to potentially deallocate it. 74 - unsafe { bindings::drm_gem_object_put(obj) }; 75 - } 76 - } 77 - 78 58 extern "C" fn open_callback<T: DriverObject>( 79 59 raw_obj: *mut bindings::drm_gem_object, 80 60 raw_file: *mut bindings::drm_file, ··· 164 184 165 185 /// A base GEM object. 166 186 /// 167 - /// Invariants 187 + /// # Invariants 168 188 /// 169 189 /// - `self.obj` is a valid instance of a `struct drm_gem_object`. 170 - /// - `self.dev` is always a valid pointer to a `struct drm_device`. 171 190 #[repr(C)] 172 191 #[pin_data] 173 192 pub struct Object<T: DriverObject + Send + Sync> { 174 193 obj: Opaque<bindings::drm_gem_object>, 175 - dev: NonNull<drm::Device<T::Driver>>, 176 194 #[pin] 177 195 data: T, 178 196 } ··· 200 222 try_pin_init!(Self { 201 223 obj: Opaque::new(bindings::drm_gem_object::default()), 202 224 data <- T::new(dev, size), 203 - // INVARIANT: The drm subsystem guarantees that the `struct drm_device` will live 204 - // as long as the GEM object lives. 205 - dev: dev.into(), 206 225 }), 207 226 GFP_KERNEL, 208 227 )?; ··· 222 247 223 248 /// Returns the `Device` that owns this GEM object. 224 249 pub fn dev(&self) -> &drm::Device<T::Driver> { 225 - // SAFETY: The DRM subsystem guarantees that the `struct drm_device` will live as long as 226 - // the GEM object lives, hence the pointer must be valid. 227 - unsafe { self.dev.as_ref() } 250 + // SAFETY: 251 + // - `struct drm_gem_object.dev` is initialized and valid for as long as the GEM 252 + // object lives. 253 + // - The device we used for creating the gem object is passed as &drm::Device<T::Driver> to 254 + // Object::<T>::new(), so we know that `T::Driver` is the right generic parameter to use 255 + // here. 256 + unsafe { drm::Device::from_raw((*self.as_raw()).dev) } 228 257 } 229 258 230 259 fn as_raw(&self) -> *mut bindings::drm_gem_object { ··· 249 270 // free callback which guarantees this object has zero remaining references, 250 271 // so we can drop it. 251 272 let _ = unsafe { KBox::from_raw(this) }; 273 + } 274 + } 275 + 276 + // SAFETY: Instances of `Object<T>` are always reference-counted. 277 + unsafe impl<T: DriverObject> crate::types::AlwaysRefCounted for Object<T> { 278 + fn inc_ref(&self) { 279 + // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero. 280 + unsafe { bindings::drm_gem_object_get(self.as_raw()) }; 281 + } 282 + 283 + unsafe fn dec_ref(obj: NonNull<Self>) { 284 + // SAFETY: `obj` is a valid pointer to an `Object<T>`. 285 + let obj = unsafe { obj.as_ref() }; 286 + 287 + // SAFETY: The safety requirements guarantee that the refcount is non-zero. 288 + unsafe { bindings::drm_gem_object_put(obj.as_raw()) } 252 289 } 253 290 } 254 291
+4
rust/kernel/lib.rs
··· 21 21 #![feature(inline_const)] 22 22 #![feature(pointer_is_aligned)] 23 23 // 24 + // Stable since Rust 1.80.0. 25 + #![feature(slice_flatten)] 26 + // 24 27 // Stable since Rust 1.81.0. 25 28 #![feature(lint_reasons)] 26 29 // ··· 131 128 pub mod security; 132 129 pub mod seq_file; 133 130 pub mod sizes; 131 + pub mod slice; 134 132 mod static_assert; 135 133 #[doc(hidden)] 136 134 pub mod std_vendor;
+3
rust/kernel/prelude.rs
··· 51 51 pub use super::current; 52 52 53 53 pub use super::uaccess::UserPtr; 54 + 55 + #[cfg(not(CONFIG_RUSTC_HAS_SLICE_AS_FLATTENED))] 56 + pub use super::slice::AsFlattened;
+49
rust/kernel/slice.rs
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + //! Additional (and temporary) slice helpers. 4 + 5 + /// Extension trait providing a portable version of [`as_flattened`] and 6 + /// [`as_flattened_mut`]. 7 + /// 8 + /// In Rust 1.80, the previously unstable `slice::flatten` family of methods 9 + /// have been stabilized and renamed from `flatten` to `as_flattened`. 10 + /// 11 + /// This creates an issue for as long as the MSRV is < 1.80, as the same functionality is provided 12 + /// by different methods depending on the compiler version. 13 + /// 14 + /// This extension trait solves this by abstracting `as_flatten` and calling the correct method 15 + /// depending on the Rust version. 16 + /// 17 + /// This trait can be removed once the MSRV passes 1.80. 18 + /// 19 + /// [`as_flattened`]: slice::as_flattened 20 + /// [`as_flattened_mut`]: slice::as_flattened_mut 21 + #[cfg(not(CONFIG_RUSTC_HAS_SLICE_AS_FLATTENED))] 22 + pub trait AsFlattened<T> { 23 + /// Takes a `&[[T; N]]` and flattens it to a `&[T]`. 24 + /// 25 + /// This is an portable layer on top of [`as_flattened`]; see its documentation for details. 26 + /// 27 + /// [`as_flattened`]: slice::as_flattened 28 + fn as_flattened(&self) -> &[T]; 29 + 30 + /// Takes a `&mut [[T; N]]` and flattens it to a `&mut [T]`. 31 + /// 32 + /// This is an portable layer on top of [`as_flattened_mut`]; see its documentation for details. 33 + /// 34 + /// [`as_flattened_mut`]: slice::as_flattened_mut 35 + fn as_flattened_mut(&mut self) -> &mut [T]; 36 + } 37 + 38 + #[cfg(not(CONFIG_RUSTC_HAS_SLICE_AS_FLATTENED))] 39 + impl<T, const N: usize> AsFlattened<T> for [[T; N]] { 40 + #[allow(clippy::incompatible_msrv)] 41 + fn as_flattened(&self) -> &[T] { 42 + self.flatten() 43 + } 44 + 45 + #[allow(clippy::incompatible_msrv)] 46 + fn as_flattened_mut(&mut self) -> &mut [T] { 47 + self.flatten_mut() 48 + } 49 + }
+63
rust/kernel/transmute.rs
··· 58 58 } 59 59 } 60 60 61 + /// Converts the beginning of `bytes` to a reference to `Self`. 62 + /// 63 + /// This method is similar to [`Self::from_bytes`], with the difference that `bytes` does not 64 + /// need to be the same size of `Self` - the appropriate portion is cut from the beginning of 65 + /// `bytes`, and the remainder returned alongside `Self`. 66 + fn from_bytes_prefix(bytes: &[u8]) -> Option<(&Self, &[u8])> 67 + where 68 + Self: Sized, 69 + { 70 + if bytes.len() < size_of::<Self>() { 71 + None 72 + } else { 73 + // PANIC: We checked that `bytes.len() >= size_of::<Self>`, thus `split_at` cannot 74 + // panic. 75 + // TODO: replace with `split_at_checked` once the MSRV is >= 1.80. 76 + let (prefix, remainder) = bytes.split_at(size_of::<Self>()); 77 + 78 + Self::from_bytes(prefix).map(|s| (s, remainder)) 79 + } 80 + } 81 + 61 82 /// Converts a mutable slice of bytes to a reference to `Self`. 62 83 /// 63 84 /// Succeeds if the reference is properly aligned, and the size of `bytes` is equal to that of ··· 101 80 } 102 81 } 103 82 83 + /// Converts the beginning of `bytes` to a mutable reference to `Self`. 84 + /// 85 + /// This method is similar to [`Self::from_bytes_mut`], with the difference that `bytes` does 86 + /// not need to be the same size of `Self` - the appropriate portion is cut from the beginning 87 + /// of `bytes`, and the remainder returned alongside `Self`. 88 + fn from_bytes_mut_prefix(bytes: &mut [u8]) -> Option<(&mut Self, &mut [u8])> 89 + where 90 + Self: AsBytes + Sized, 91 + { 92 + if bytes.len() < size_of::<Self>() { 93 + None 94 + } else { 95 + // PANIC: We checked that `bytes.len() >= size_of::<Self>`, thus `split_at_mut` cannot 96 + // panic. 97 + // TODO: replace with `split_at_mut_checked` once the MSRV is >= 1.80. 98 + let (prefix, remainder) = bytes.split_at_mut(size_of::<Self>()); 99 + 100 + Self::from_bytes_mut(prefix).map(|s| (s, remainder)) 101 + } 102 + } 103 + 104 104 /// Creates an owned instance of `Self` by copying `bytes`. 105 105 /// 106 106 /// Unlike [`FromBytes::from_bytes`], which requires aligned input, this method can be used on ··· 137 95 Some(unsafe { core::ptr::read_unaligned(bytes.as_ptr().cast::<Self>()) }) 138 96 } else { 139 97 None 98 + } 99 + } 100 + 101 + /// Creates an owned instance of `Self` from the beginning of `bytes`. 102 + /// 103 + /// This method is similar to [`Self::from_bytes_copy`], with the difference that `bytes` does 104 + /// not need to be the same size of `Self` - the appropriate portion is cut from the beginning 105 + /// of `bytes`, and the remainder returned alongside `Self`. 106 + fn from_bytes_copy_prefix(bytes: &[u8]) -> Option<(Self, &[u8])> 107 + where 108 + Self: Sized, 109 + { 110 + if bytes.len() < size_of::<Self>() { 111 + None 112 + } else { 113 + // PANIC: We checked that `bytes.len() >= size_of::<Self>`, thus `split_at` cannot 114 + // panic. 115 + // TODO: replace with `split_at_checked` once the MSRV is >= 1.80. 116 + let (prefix, remainder) = bytes.split_at(size_of::<Self>()); 117 + 118 + Self::from_bytes_copy(prefix).map(|s| (s, remainder)) 140 119 } 141 120 } 142 121 }