From 0ed578243bab1b1969d13c220d5d5f1b859a36c4 Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Tue, 3 Nov 2026 11:29:53 -0500 Subject: [PATCH 1/3] asahi: Admit the G15G firmware interface independently of GPU records Keep T8122 G15 command streams, Gen4 samplers and the native sampler heap for both firmware interfaces. The HAL200 profile checks C0 identity and the reported enabled-core mask without changing physical chip selection. Keep the scheduled queue feature contract and compiler requirements. Test profile axes and malformed identities, and allow the no-op shim to present the additional interface for offline compiler and device-open coverage. Signed-off-by: Satya Benson --- src/asahi/drm-shim/asahi_noop.c | 5 +++ src/asahi/lib/agx_chip.c | 45 ++++++++++++++++++---- src/asahi/lib/tests/test-chip-admission.c | 46 ++++++++++++++++++++++- 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/src/asahi/drm-shim/asahi_noop.c b/src/asahi/drm-shim/asahi_noop.c index 2beb6d6fdf17..44249af1dad1 100644 --- a/src/asahi/drm-shim/asahi_noop.c +++ b/src/asahi/drm-shim/asahi_noop.c @@ -1323,6 +1323,11 @@ asahi_ioctl_get_param(int fd, unsigned long request, void *arg) size_t size = MIN2((size_t)gp->size, sizeof(struct drm_asahi_params_global)); struct drm_asahi_params_global params = *shim_params(); params.features = shim_features(); + if (shim_params() == ¶ms_g15g && + shim_env_on("ASAHI_SHIM_G15G_HAL200")) { + params.gpu_hal_generation = DRM_ASAHI_GPU_HAL_200; + params.vm_end = UINT64_C(0x3ffffff8000); + } const char *command_limit = getenv("ASAHI_SHIM_COMMAND_LIMIT"); if (command_limit) { unsigned limit = strtoul(command_limit, NULL, 0); diff --git a/src/asahi/lib/agx_chip.c b/src/asahi/lib/agx_chip.c index da139589e67c..a02fc564462b 100644 --- a/src/asahi/lib/agx_chip.c +++ b/src/asahi/lib/agx_chip.c @@ -7,6 +7,8 @@ #include +#include "util/bitscan.h" + enum agx_chip agx_chip_from_params(const struct drm_asahi_params_global *params) { @@ -276,6 +278,41 @@ agx_chip_driver_supported(enum agx_chip chip) } } +/* The firmware interface version does not determine the GPU record layout. + * Both T8122 interfaces consume G15 records and Gen4 sampler words. Keep the + * physical chip as G15G so compiler scheduling and hardware workarounds agree. + */ +static bool +agx_t8122_params_supported(const struct drm_asahi_params_global *params) +{ + if (params->chip_id != 0x8122 || params->num_dies != 1 || + params->num_clusters_total != 1 || + params->usc_generation != AGX_ISA_AGX3) + return false; + + if (params->gpu_hal_generation == DRM_ASAHI_GPU_HAL_LEGACY) + return true; + + if (params->gpu_hal_generation != DRM_ASAHI_GPU_HAL_200 || + params->gpu_revision != 0x20 || params->num_cores_per_cluster != 10) + return false; + + const uint64_t mask = params->core_masks[0]; + const unsigned cores = util_bitcount64(mask); + if ((mask >> 10) || (cores != 8 && cores != 10)) + return false; + for (unsigned i = 1; i < DRM_ASAHI_MAX_CLUSTERS; ++i) { + if (params->core_masks[i]) + return false; + } + + return agx_chip_command_stream_generation(AGX_CHIP_G15G) == + AGX_CS_GENERATION_G15 && + agx_chip_sampler_generation(AGX_CHIP_G15G) == + AGX_SAMPLER_GENERATION_GEN4 && + agx_chip_sampler_heap_element_size(AGX_CHIP_G15G) == 32; +} + bool agx_params_driver_supported(const struct drm_asahi_params_global *params) { @@ -301,13 +338,7 @@ agx_params_driver_supported(const struct drm_asahi_params_global *params) params->gpu_hal_generation == DRM_ASAHI_GPU_HAL_200; } case AGX_CHIP_G15G: - /* T8122/M3: one die, one cluster, AGX3 shaders on the legacy firmware - * interface (14.8.3), through the kernel's T8122 runtime. The exact - * identity/runtime checks apply without a Mesa environment opt-in. */ - return params->chip_id == 0x8122 && params->num_dies == 1 && - params->num_clusters_total == 1 && - params->usc_generation == AGX_ISA_AGX3 && - params->gpu_hal_generation == DRM_ASAHI_GPU_HAL_LEGACY; + return agx_t8122_params_supported(params); case AGX_CHIP_G15S: /* T6030/M3 Pro: one die, two clusters, AGX3 shaders on the legacy * (pre-HAL200) firmware interface. */ diff --git a/src/asahi/lib/tests/test-chip-admission.c b/src/asahi/lib/tests/test-chip-admission.c index 532be1c49646..62bf2de3139d 100644 --- a/src/asahi/lib/tests/test-chip-admission.c +++ b/src/asahi/lib/tests/test-chip-admission.c @@ -223,9 +223,51 @@ main(void) bad.num_clusters_total = 2; assert(!agx_params_driver_supported(&bad)); - bad = g15g; - bad.gpu_hal_generation = DRM_ASAHI_GPU_HAL_200; + struct drm_asahi_params_global modern = g15g; + modern.gpu_hal_generation = DRM_ASAHI_GPU_HAL_200; + modern.gpu_revision = 0x20; + assert(agx_params_driver_supported(&modern)); + assert(agx_params_native_kernel_abi_supported(&modern)); + enum agx_chip modern_chip = agx_chip_from_params(&modern); + assert(modern_chip == AGX_CHIP_G15G); + assert(agx_chip_command_stream_generation(modern_chip) == + AGX_CS_GENERATION_G15); + assert(agx_chip_sampler_generation(modern_chip) == + AGX_SAMPLER_GENERATION_GEN4); + assert(agx_chip_sampler_heap_element_size(modern_chip) == 32); + assert(agx_chip_isa_encoding(modern_chip) == + agx_chip_isa_encoding(agx_chip_from_params(&g15g))); + assert(agx_chip_cdm_wide_visibility(modern_chip)); + assert(!agx_chip_sampler_has_lod_bias(modern_chip)); + modern.core_masks[0] = 0x3cf; + assert(agx_params_driver_supported(&modern)); + modern.core_masks[0] = 0x3ff; + + bad = modern; + bad.gpu_hal_generation = DRM_ASAHI_GPU_HAL_300; + assert(!agx_params_driver_supported(&bad)); + bad = modern; + bad.gpu_revision = 0x21; + assert(!agx_params_driver_supported(&bad)); + bad = modern; + bad.num_cores_per_cluster = 9; + assert(!agx_params_driver_supported(&bad)); + bad = modern; + bad.core_masks[0] = 0x1ff; assert(!agx_params_driver_supported(&bad)); + bad = modern; + bad.core_masks[0] = 0x7ff; + assert(!agx_params_driver_supported(&bad)); + bad = modern; + bad.core_masks[1] = 1; + assert(!agx_params_driver_supported(&bad)); + bad = modern; + bad.gpu_variant = 'S'; + assert(!agx_params_driver_supported(&bad)); + bad = modern; + bad.features = DRM_ASAHI_FEATURE_EXACT_PRIOR_BARRIERS | + DRM_ASAHI_FEATURE_PER_COMMAND_INPUT_SYNCS; + assert(!agx_params_native_kernel_abi_supported(&bad)); bad = g15g; bad.usc_generation = 2; From 7cedaf8d36a8cb8d182eac81cd8111a60165bffe Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Tue, 3 Nov 2026 11:29:53 -0500 Subject: [PATCH 2/3] asahi: Keep T8122 sampler state in the native GPU stream The G15G HAL200 interface consumes sampler state from the native stream. Leave the optional firmware-command sampler fields zero in both Gallium and Honeykrisp while preserving the native heap and shader lowering. Signed-off-by: Satya Benson --- src/asahi/lib/agx_device.h | 9 +++++++++ src/asahi/vulkan/hk_queue.c | 4 ++-- src/gallium/drivers/asahi/agx_pipe.c | 8 +++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/asahi/lib/agx_device.h b/src/asahi/lib/agx_device.h index d53e5eeddcc0..24f84eb15924 100644 --- a/src/asahi/lib/agx_device.h +++ b/src/asahi/lib/agx_device.h @@ -520,6 +520,15 @@ agx_native_graphics(const struct agx_device *dev) return agx_chip_native_agx3(dev->chip); } +/* This interface carries native sampler state in GPU records, not in the + * firmware command's optional sampler-heap fields. */ +static inline bool +agx_stream_sampler_state(const struct agx_device *dev) +{ + return dev->chip == AGX_CHIP_G15G && + dev->params.gpu_hal_generation == DRM_ASAHI_GPU_HAL_200; +} + /* T6040 uses the G17P architecture profile, while capability rollout may * still be product-specific. Device admission has already checked USC/HAL. */ static inline bool diff --git a/src/asahi/vulkan/hk_queue.c b/src/asahi/vulkan/hk_queue.c index 4c7eb7eeee4f..bf443fdeceaf 100644 --- a/src/asahi/vulkan/hk_queue.c +++ b/src/asahi/vulkan/hk_queue.c @@ -134,7 +134,7 @@ asahi_fill_cdm_command(struct hk_device *dev, struct hk_cs *cs, .ts.end.offset = cs->timestamp.end.offset_B, }; - if (cs->uses_sampler_heap) { + if (cs->uses_sampler_heap && !agx_stream_sampler_state(&dev->dev)) { cmd->sampler_heap = dev->samplers.table.bo->va->addr; cmd->sampler_count = dev->samplers.table.alloc; } @@ -296,7 +296,7 @@ asahi_fill_vdm_command(struct hk_device *dev, struct hk_cs *cs, c->isp_scissor_base = cs->uploaded_scissor; c->isp_dbias_base = cs->uploaded_zbias; - if (cs->uses_sampler_heap) { + if (cs->uses_sampler_heap && !agx_stream_sampler_state(&dev->dev)) { c->sampler_heap = dev->samplers.table.bo->va->addr; c->sampler_count = dev->samplers.table.alloc; } diff --git a/src/gallium/drivers/asahi/agx_pipe.c b/src/gallium/drivers/asahi/agx_pipe.c index c769f062345d..a25aba3fdc13 100644 --- a/src/gallium/drivers/asahi/agx_pipe.c +++ b/src/gallium/drivers/asahi/agx_pipe.c @@ -1508,7 +1508,8 @@ agx_cmdbuf(struct agx_device *dev, struct drm_asahi_cmd_render *c, c->isp_dbias_base = depth_bias_ptr; c->isp_oclqry_base = visibility_result_ptr; - if (batch->sampler_heap.bo) { + if (batch->sampler_heap.bo && + !agx_stream_sampler_state(dev)) { c->sampler_heap = batch->sampler_heap.bo->va->addr; c->sampler_count = batch->sampler_heap.count; } @@ -1673,6 +1674,11 @@ agx_flush_compute(struct agx_context *ctx, struct agx_batch *batch, .sampler_count = batch->sampler_heap.count, }; + if (agx_stream_sampler_state(dev)) { + cmdbuf->sampler_heap = 0; + cmdbuf->sampler_count = 0; + } + if (batch->cs_scratch) { // The commented out lines *may* be related to subgroup-level preemption, // which we can't support without implementing threadgroup memory in the From 0803cd5f5608a2d5f695937bdf662228dd8eb2f3 Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Wed, 4 Nov 2026 00:52:20 -0500 Subject: [PATCH 3/3] Match HAL200 shim bind admission to reported VM limits Signed-off-by: Satya Benson --- src/asahi/drm-shim/asahi_noop.c | 15 ++++++++-- src/asahi/drm-shim/meson.build | 18 ++++++++++++ src/asahi/drm-shim/test-vm-limits.c | 43 +++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 src/asahi/drm-shim/test-vm-limits.c diff --git a/src/asahi/drm-shim/asahi_noop.c b/src/asahi/drm-shim/asahi_noop.c index 44249af1dad1..37ef11143e3e 100644 --- a/src/asahi/drm-shim/asahi_noop.c +++ b/src/asahi/drm-shim/asahi_noop.c @@ -1308,6 +1308,14 @@ shim_features(void) return features; } +static uint64_t +shim_vm_end(const struct drm_asahi_params_global *params) +{ + if (params == ¶ms_g15g && shim_env_on("ASAHI_SHIM_G15G_HAL200")) + return UINT64_C(0x3ffffff8000); + return params->vm_end; +} + static int asahi_ioctl_get_param(int fd, unsigned long request, void *arg) { @@ -1326,7 +1334,7 @@ asahi_ioctl_get_param(int fd, unsigned long request, void *arg) if (shim_params() == ¶ms_g15g && shim_env_on("ASAHI_SHIM_G15G_HAL200")) { params.gpu_hal_generation = DRM_ASAHI_GPU_HAL_200; - params.vm_end = UINT64_C(0x3ffffff8000); + params.vm_end = shim_vm_end(shim_params()); } const char *command_limit = getenv("ASAHI_SHIM_COMMAND_LIMIT"); if (command_limit) { @@ -1415,6 +1423,7 @@ asahi_ioctl_vm_bind_unlocked(int fd, unsigned long request, void *arg) { const struct drm_asahi_vm_bind *bind = arg; const struct drm_asahi_params_global *params = shim_params(); + const uint64_t vm_end = shim_vm_end(params); for (uint32_t i = 0; i < bind->num_binds; ++i) { const struct drm_asahi_gem_bind_op *op = @@ -1430,13 +1439,13 @@ asahi_ioctl_vm_bind_unlocked(int fd, unsigned long request, void *arg) return -EINVAL; } - if (end < start || start < params->vm_start || end > params->vm_end) { + if (end < start || start < params->vm_start || end > vm_end) { fprintf(stderr, "shim: gem_bind: Invalid map range %#llx..%#llx " "(not contained in user range %#llx..%#llx)\n", (unsigned long long)start, (unsigned long long)end, (unsigned long long)params->vm_start, - (unsigned long long)params->vm_end); + (unsigned long long)vm_end); return -EINVAL; } diff --git a/src/asahi/drm-shim/meson.build b/src/asahi/drm-shim/meson.build index 80821e6c18d0..8f96ca8ece99 100644 --- a/src/asahi/drm-shim/meson.build +++ b/src/asahi/drm-shim/meson.build @@ -29,3 +29,21 @@ libasahi_noop_drm_shim = shared_library( gnu_symbol_visibility : 'hidden', install : true, ) + +if with_tests + asahi_vm_limits_test = executable( + 'asahi-vm-limits', 'test-vm-limits.c', + include_directories : [inc_include], + dependencies : [dep_libdrm], + ) + foreach hal : ['legacy', 'hal200'] + test( + 'asahi-vm-limits-' + hal, prog_python, + args : [files('../../gallium/drivers/asahi/tests/run-shim.py'), + libasahi_noop_drm_shim, asahi_vm_limits_test], + env : ['ASAHI_SHIM_CHIP=g15g', + 'ASAHI_SHIM_G15G_HAL200=' + (hal == 'hal200' ? '1' : '0')], + suite : ['asahi'], + ) + endforeach +endif diff --git a/src/asahi/drm-shim/test-vm-limits.c b/src/asahi/drm-shim/test-vm-limits.c new file mode 100644 index 000000000000..acd531c66a28 --- /dev/null +++ b/src/asahi/drm-shim/test-vm-limits.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: MIT */ +#include +#include +#include +#include +#include +#include +#include +#include "drm-uapi/asahi_drm.h" + +#define CHECK(x) do { if (!(x)) { fprintf(stderr, "%u: %s\n", __LINE__, #x); abort(); } } while (0) + +int main(void) +{ + int fd = open("/dev/dri/renderD128", O_RDWR | O_CLOEXEC); + CHECK(fd >= 0); + struct drm_asahi_params_global params = {0}; + struct drm_asahi_get_params get = { + .pointer = (uintptr_t)¶ms, .size = sizeof(params), + }; + CHECK(!drmIoctl(fd, DRM_IOCTL_ASAHI_GET_PARAMS, &get)); + struct drm_asahi_vm_create vm = {0}; + CHECK(!drmIoctl(fd, DRM_IOCTL_ASAHI_VM_CREATE, &vm)); + struct drm_asahi_gem_create bo = {.size = 0x4000}; + CHECK(!drmIoctl(fd, DRM_IOCTL_ASAHI_GEM_CREATE, &bo)); + struct drm_asahi_gem_bind_op op = { + .handle = bo.handle, .range = 0x4000, + }; + struct drm_asahi_vm_bind bind = { + .vm_id = vm.vm_id, .num_binds = 1, + .stride = sizeof(op), .userptr = (uintptr_t)&op, + }; + const uint64_t addresses[] = {params.vm_start, UINT64_C(0x30000000000), + params.vm_end - 0x4000, params.vm_end}; + for (unsigned i = 0; i < 4; ++i) { + op.addr = addresses[i]; + int ret = drmIoctl(fd, DRM_IOCTL_ASAHI_VM_BIND, &bind); + CHECK((ret == 0) == (op.addr + op.range <= params.vm_end)); + } + close(fd); + puts("PASS reported VM limits match bind admission"); + return 0; +}