Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions arch/arm64/boot/dts/apple/t6030.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,8 @@
#clock-cells = <1>;
};

/* The display handoff gate installs PMP unless explicitly static. */
#ifdef APPLE_USE_PMP
pmp_dart: iommu@350300000 {
compatible = "apple,t6030-dart", "apple,t8110-dart";
reg = <0x3 0x50300000 0x0 0x4000>;
Expand Down Expand Up @@ -985,6 +987,8 @@
power-domains = <&ps_pmp>, <&ps_pms_sram>;
};

#endif /* APPLE_USE_PMP */

aic: interrupt-controller@351000000 {
compatible = "apple,t6030-aic3", "apple,t8122-aic3";
#interrupt-cells = <3>;
Expand Down Expand Up @@ -1058,7 +1062,7 @@
interrupts = <AIC_IRQ 485 IRQ_TYPE_LEVEL_HIGH>;

pmic1: pmic@b {
compatible = "apple,chapel-pmic", "apple,spmi-nvmem";

Check warning on line 1065 in arch/arm64/boot/dts/apple/t6030.dtsi

View workflow job for this annotation

GitHub Actions / ci / device trees

dt-check style

[line-length] line exceeds 80 columns (85)
reg = <0xb SPMI_USID>;

nvmem-layout {
Expand Down
29 changes: 20 additions & 9 deletions drivers/gpu/drm/asahi/g15/glue.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT
/*
* G15 (T6030/T6031, M3 Pro/Max) helpers that have no Rust bindings: static inlines,
Expand Down Expand Up @@ -88,20 +88,31 @@
*/
bool asahi_g15_reserved_uat(u32 index, u64 start, u64 size)
{
static const char *const paths[] = {
"/reserved-memory/uat-ttbs",
"/reserved-memory/uat-pagetables",
"/reserved-memory/uat-handoff",
"/reserved-memory/uat-pagetables-l2",
static const char *const names[] = {
"uat-ttbs",
"uat-pagetables",
"uat-handoff",
"uat-pagetables-l2",
};
struct device_node *np;
struct device_node *rmem, *child, *np = NULL;
struct resource res;
unsigned int count = 0;
bool match;

if (index >= ARRAY_SIZE(paths))
if (index >= ARRAY_SIZE(names))
return false;
np = of_find_node_by_path(paths[index]);
match = np && of_device_is_available(np) &&

/* Unit addresses belong to the DT layout, not the reservation name. */
rmem = of_find_node_by_path("/reserved-memory");
for_each_child_of_node(rmem, child) {
if (!of_node_name_eq(child, names[index]))
continue;
count++;
if (!np)
np = of_node_get(child);
}
of_node_put(rmem);
match = count == 1 && np && of_device_is_available(np) &&
!of_property_read_bool(np, "reusable") &&
!of_get_address(np, 1, NULL, NULL) &&
!of_address_to_resource(np, 0, &res) &&
Expand Down
65 changes: 65 additions & 0 deletions tools/asahi/t6030-pmp-dt-tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-only
"""Check static PMP ownership in the compiled T6030 device trees."""

import argparse
from pathlib import Path
import subprocess


ROOT = Path(__file__).resolve().parents[2]
BOARDS = ("t6030-j514s", "t6030-j516s")
PMP_NODES = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: the gate refuses on the apple,t6000-pmp-v2 and apple,t6030-pmp-v2-report compatibles anywhere in the tree (t6030-display-gate.c:693-695), not on these names. Asserting that neither compatible is present in the default profile would catch a renamed static node too.

"iommu@350300000",
"pmp_report@3503c0000",
"pmp@350500000",
"mbox@350c08000",
}


def check_profile(tree, output, dtc, board, static):
profile = "static" if static else "default"
dtb = output / f"{board}-{profile}.dtb"
command = [
"cpp", "-nostdinc", "-undef", "-D__DTS__",
"-I", str(tree / "scripts/dtc/include-prefixes"),
"-x", "assembler-with-cpp",
]
if static:
command.append("-DAPPLE_USE_PMP")
command.append(str(tree / f"arch/arm64/boot/dts/apple/{board}.dts"))
source = subprocess.check_output(command)
subprocess.run([dtc, "-q", "-I", "dts", "-O", "dtb", "-o", str(dtb)],
input=source, check=True)

children = subprocess.check_output(["fdtget", "-l", str(dtb), "/soc"], text=True)
present = PMP_NODES.intersection(children.split())
expected = PMP_NODES if static else set()
if present != expected:
raise AssertionError(f"{board} {profile}: static PMP nodes "
f"{sorted(present)}, expected {sorted(expected)}")

alias = subprocess.run(["fdtget", "-t", "s", str(dtb), "/aliases", "pmp"],
capture_output=True, text=True)
if static:
if alias.returncode or alias.stdout.strip() != "/soc/pmp@350500000":
raise AssertionError(f"{board} {profile}: missing or incorrect PMP alias")
elif alias.returncode == 0:
raise AssertionError(f"{board} {profile}: unexpected static PMP alias")
print(f"{board} {profile}: PASS")


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--tree", type=Path, default=ROOT)
parser.add_argument("--dtc", required=True, help="this kernel's built scripts/dtc/dtc")
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
args.output.mkdir(parents=True, exist_ok=True)
for board in BOARDS:
for static in (False, True):
check_profile(args.tree.resolve(), args.output, args.dtc, board, static)


if __name__ == "__main__":
main()
29 changes: 29 additions & 0 deletions tools/aurora-ci/host-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,35 @@
"rust": "1.93.1",
"note": "Public synthetic Rust/C host cases; two unavailable private-hardware fixture cases remain explicitly ignored."
},
{
"name": "tools/asahi/t6030-pmp-dt-tests.py",
"group": "tools",
"setup": [
[
"make",
"-s",
"O={output}/kbuild",
"ARCH=arm64",
"allnoconfig"
],
[
"make",
"-s",
"O={output}/kbuild",
"ARCH=arm64",
"scripts_dtc"
]
],
"argv": [
"python3",
"tools/asahi/t6030-pmp-dt-tests.py",
"--dtc",
"{output}/kbuild/scripts/dtc/dtc",
"--output",
"{output}/dtbs"
],
"note": "Compile J514S/J516S default and static PMP profiles; check supplier nodes and alias in the resulting DTBs."
},
{
"name": "tools/asahi/t6030-pmp-graph-tests.py",
"group": "tools",
Expand Down
95 changes: 84 additions & 11 deletions tools/testing/selftests/drivers/gpu/asahi-g15/uat-owner.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
#include <assert.h>
#include <stdbool.h>
Expand All @@ -8,22 +8,58 @@
typedef uint32_t u32;
typedef uint64_t u64;
struct device_node {
const char *name;
struct device_node *child, *next;
bool available, reusable, extra, bad_reg;
u64 start, size;
int refs;
};
struct resource {
u64 start, end;
};
static struct device_node node = { .available = true };
static struct device_node nodes[5];
static struct device_node rmem;
#define node nodes[0]
static bool missing;
static u64 start = 0x100000, size = 0x4000;
static int refs;
static inline struct device_node *of_node_get(struct device_node *np)
{
if (np)
np->refs++;
return np;
}
static void of_node_put(struct device_node *np);
static struct device_node *of_find_node_by_path(const char *path)
{
assert(!strncmp(path, "/reserved-memory/uat-", 21));
const char *prefix = "/reserved-memory/";

if (missing)
return NULL;
refs++;
return &node;
if (!strcmp(path, "/reserved-memory"))
return of_node_get(&rmem);
assert(!strncmp(path, prefix, strlen(prefix)));
for (struct device_node *np = rmem.child; np; np = np->next)
if (!strcmp(np->name, path + strlen(prefix)))
return of_node_get(np);
return NULL;
}
static inline struct device_node *of_get_next_child(struct device_node *parent,
struct device_node *prev)
{
struct device_node *next = NULL;

if (parent)
next = prev ? prev->next : parent->child;
of_node_put(prev);
return of_node_get(next);
}
#define for_each_child_of_node(parent, child) \
for ((child) = of_get_next_child((parent), NULL); (child); \
(child) = of_get_next_child((parent), (child)))
static inline bool of_node_name_eq(struct device_node *np, const char *name)
{
return np && strlen(name) == strcspn(np->name, "@") &&
!strncmp(np->name, name, strlen(name));
}
static bool of_device_is_available(struct device_node *np)
{
Expand All @@ -44,8 +80,8 @@
struct resource *res)
{
assert(index == 0);
res->start = start;
res->end = start + size - 1;
res->start = np->start;
res->end = np->start + np->size - 1;
return np->bad_reg ? -1 : 0;
}
static u64 resource_size(struct resource *res)
Expand All @@ -55,14 +91,49 @@
static void of_node_put(struct device_node *np)
{
if (np)
refs--;
assert(np->refs-- > 0);
}
#include "uat-production.h"

int main(void)
{
static const char *const names[] = {
"uat-ttbs", "uat-pagetables", "uat-handoff", "uat-pagetables-l2"
};
static const char *const unit_names[] = {
"uat-ttbs@10", "uat-pagetables@20", "uat-handoff@30", "uat-pagetables-l2@40"
};
rmem.child = nodes;
for (u32 i = 0; i < 4; ++i) {
nodes[i].available = true;
nodes[i].start = start + i * size;
nodes[i].size = size;
nodes[i].name = names[i];
nodes[i].next = i < 3 ? &nodes[i + 1] : NULL;
}
for (u32 i = 0; i < 4; ++i)
assert(asahi_g15_reserved_uat(i, nodes[i].start, size));
for (u32 i = 0; i < 4; ++i)
assert(asahi_g15_reserved_uat(i, start, size));
nodes[i].name = unit_names[i];
for (u32 i = 0; i < 4; ++i)
assert(asahi_g15_reserved_uat(i, nodes[i].start, size));
nodes[3].next = &nodes[4];
nodes[4].name = "uat-ttbs@99";
nodes[4].available = true;
assert(!asahi_g15_reserved_uat(0, start, size));
nodes[4].available = false;
assert(!asahi_g15_reserved_uat(0, start, size));
nodes[3].next = NULL;
node.name = "uat-ttbs-other@10";
assert(!asahi_g15_reserved_uat(0, start, size));
node.name = unit_names[0];
for (u32 i = 0; i < 4; ++i) {
const char *saved = nodes[i].name;

nodes[i].name = "unrelated@10";
assert(!asahi_g15_reserved_uat(i, nodes[i].start, size));
nodes[i].name = saved;
}
assert(!asahi_g15_reserved_uat(4, start, size));
assert(!asahi_g15_reserved_uat(0, start + 0x4000, size));
assert(!asahi_g15_reserved_uat(0, start, size + 0x4000));
Expand All @@ -80,6 +151,8 @@
node.extra = false;
node.bad_reg = true;
assert(!asahi_g15_reserved_uat(0, start, size));
assert(refs == 0);
puts("production UAT owner: exact ranges, unavailable/reusable/malformed/extra reservations: PASS");
assert(rmem.refs == 0);
for (u32 i = 0; i < ARRAY_SIZE(nodes); ++i)
assert(nodes[i].refs == 0);
puts("production UAT owner: unit addresses, unique owners and exact ranges: PASS");
}
Loading