diff --git a/Documentation/devicetree/bindings/iommu/apple,dart.yaml b/Documentation/devicetree/bindings/iommu/apple,dart.yaml index 9c24d63e9c673..720ed9da93900 100644 --- a/Documentation/devicetree/bindings/iommu/apple,dart.yaml +++ b/Documentation/devicetree/bindings/iommu/apple,dart.yaml @@ -41,6 +41,7 @@ properties: - enum: - apple,t6020-dart - apple,t6041-dart + - apple,t6050-dart - apple,t8122-dart - apple,t8132-dart - apple,t8140-dart diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index fbe1ba33089b4..7bbb932c8c89c 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -258,6 +259,21 @@ struct apple_dart_fw_root { u64 *accepted; }; +/* + * The subtrees behind the slots firmware holds in a four-level locked root + * (fw_mirror), recorded when the root is mapped: per slot, the root entry and + * a mapping of its mid-level table, and per mid entry the entry and a mapping + * of its leaf table. Leaf entries are read live. A table entry that no longer + * matches what was recorded resolves nothing. Only lookups use it: Linux + * still never publishes into those slots. + */ +struct apple_dart_fw_tree { + u64 *entry; + u64 **mid; + u64 **mid_entry; + u64 ***leaf; +}; + /* * Private structure associated with each DART device. * @@ -298,6 +314,15 @@ struct apple_dart { u32 power_retained : 1; /* Locked roots may hold firmware slots that mirror Linux mappings. */ u32 fw_mirror : 1; + /* + * fw_mirror's four-level locked roots hold firmware's own subtrees, which + * are recorded and reserved (apple_dart_snapshot_fw_tree()). Set only for + * apple,t6050-dart, where it was tested (the DCP's log and crash log). Before + * widening it, check on T6041/T8140/T8142 whether any locked stream is + * four-level and what its foreign slots hold: the reservation moves DMA + * placement for every client of that stream. + */ + u32 fw_tree : 1; u32 fw_handoff : 1; u32 tunnel_state_saved : 1; u32 tunnel_state_restored : 1; @@ -339,6 +364,8 @@ struct apple_dart { u64 *locked_owned[DART_MAX_STREAMS][DART_MAX_TTBR]; /* Leaf tables behind the root slots firmware held when we attached. */ struct apple_dart_fw_root *locked_fw[DART_MAX_STREAMS][DART_MAX_TTBR]; + /* Firmware's subtrees in a four-level locked root (TTBR 0 only). */ + struct apple_dart_fw_tree *locked_fw_tree[DART_MAX_STREAMS]; /* * First DVA of the window of the domain using each locked stream. The * window can be the client's own (apple,dma-range on the client node) @@ -804,15 +831,22 @@ static bool apple_dart_fw_handoff_enabled(const struct apple_dart *dart) handoff == 1; } +/* A 16K table page, aligned and inside the output address space. */ +static bool apple_dart_fw_table_page(const struct apple_dart *dart, + phys_addr_t phys, phys_addr_t *end) +{ + return dart->pgsize == SZ_16K && IS_ALIGNED(phys, dart->pgsize) && + !check_add_overflow(phys, (phys_addr_t)dart->pgsize - 1, end) && + dart->oas < 64 && *end < BIT_ULL(dart->oas); +} + static bool apple_dart_fw_table_ram(const struct apple_dart *dart, phys_addr_t phys) { unsigned long first, last, pfn; phys_addr_t end; - if (dart->pgsize != SZ_16K || !IS_ALIGNED(phys, dart->pgsize) || - check_add_overflow(phys, (phys_addr_t)dart->pgsize - 1, &end) || - dart->oas >= 64 || end >= BIT_ULL(dart->oas)) + if (!apple_dart_fw_table_page(dart, phys, &end)) return false; first = PHYS_PFN(phys); last = PHYS_PFN(end); @@ -822,14 +856,41 @@ static bool apple_dart_fw_table_ram(const struct apple_dart *dart, return true; } -static bool apple_dart_fw_table_decode(const struct apple_dart *dart, - u64 pte, phys_addr_t *phys) +/* A valid table entry with no bits but the next table's address. */ +static bool apple_dart_fw_table_pte(const struct apple_dart *dart, u64 pte, + phys_addr_t *phys) { if (!(pte & APPLE_DART_PTE_VALID) || (pte & ~(GENMASK_ULL(37, 10) | APPLE_DART_PTE_VALID))) return false; *phys = apple_dart_pte_to_paddr(dart, pte); - return apple_dart_fw_table_ram(dart, *phys); + return true; +} + +static bool apple_dart_fw_table_decode(const struct apple_dart *dart, + u64 pte, phys_addr_t *phys) +{ + return apple_dart_fw_table_pte(dart, pte, phys) && + apple_dart_fw_table_ram(dart, *phys); +} + +/* + * A table page of firmware's four-level subtrees, which is mapped write-back: + * reserved RAM as for apple_dart_fw_table_ram(), or memory no resource + * claims. T6050's boot loader places these tables outside the memory node, + * so they are not RAM to Linux. An entry naming Linux's own RAM or a + * device's registers is refused, and its DVAs resolve nothing. + */ +static bool apple_dart_fw_tree_table(const struct apple_dart *dart, + phys_addr_t phys) +{ + phys_addr_t end; + + if (apple_dart_fw_table_ram(dart, phys)) + return true; + return apple_dart_fw_table_page(dart, phys, &end) && + region_intersects(phys, dart->pgsize, IORESOURCE_MEM, + IORES_DESC_NONE) == REGION_DISJOINT; } static bool apple_dart_fw_slot_owned(const struct apple_dart_fw_root *fw, @@ -1056,6 +1117,116 @@ apple_dart_snapshot_fw_root(struct apple_dart *dart, int sid, const u64 *live) return fw; } +static void apple_dart_free_fw_tree(struct apple_dart *dart, + struct apple_dart_fw_tree *tree) +{ + size_t i, j, entries = dart->pgsize / sizeof(u64); + + if (!tree) + return; + /* Any of the four arrays may be missing after a failed allocation. */ + for (i = 0; i < entries; i++) { + if (tree->leaf && tree->leaf[i]) { + for (j = 0; j < entries; j++) + if (tree->leaf[i][j]) + memunmap(tree->leaf[i][j]); + kfree(tree->leaf[i]); + } + if (tree->mid_entry) + kfree(tree->mid_entry[i]); + if (tree->mid && tree->mid[i]) + memunmap(tree->mid[i]); + } + kfree(tree->leaf); + kfree(tree->mid_entry); + kfree(tree->mid); + kfree(tree->entry); + kfree(tree); +} + +/* + * Record firmware's subtrees in the four-level root @live of @sid, so that + * firmware-placed buffers (an RTKit co-processor's shared segments, which + * it asks the AP to map at its own DVA) resolve through iova_to_phys. A + * table that cannot be mapped, or that apple_dart_fw_tree_table() refuses, + * is left out; its DVAs resolve nothing. A slot can map up to one leaf table + * per mid-level entry (2048 with 16K pages); firmware holds a few slots. + */ +static struct apple_dart_fw_tree * +apple_dart_snapshot_fw_tree(struct apple_dart *dart, int sid, const u64 *live) +{ + size_t i, j, entries = dart->pgsize / sizeof(u64); + struct apple_dart_fw_tree *tree; + + if (!dart->fw_tree || !dart->fw_mirror || dart->fw_handoff || + dart->hw->fmt != APPLE_DART2 || + !dart->hw->tcr_4level || + !(apple_dart_readl(dart, DART_TCR(dart, sid)) & dart->hw->tcr_4level)) + return NULL; + + tree = kzalloc(sizeof(*tree), GFP_KERNEL); + if (!tree) + return NULL; + tree->entry = kcalloc(entries, sizeof(*tree->entry), GFP_KERNEL); + tree->mid = kcalloc(entries, sizeof(*tree->mid), GFP_KERNEL); + tree->mid_entry = kcalloc(entries, sizeof(*tree->mid_entry), GFP_KERNEL); + tree->leaf = kcalloc(entries, sizeof(*tree->leaf), GFP_KERNEL); + if (!tree->entry || !tree->mid || !tree->mid_entry || !tree->leaf) { + apple_dart_free_fw_tree(dart, tree); + return NULL; + } + + for (i = 0; i < entries; i++) { + u64 pte = READ_ONCE(live[i]); + phys_addr_t phys; + + if (!apple_dart_fw_table_pte(dart, pte, &phys) || + !apple_dart_fw_tree_table(dart, phys)) + continue; + tree->mid_entry[i] = kcalloc(entries, sizeof(u64), GFP_KERNEL); + tree->leaf[i] = kcalloc(entries, sizeof(u64 *), GFP_KERNEL); + tree->mid[i] = memremap(phys, dart->pgsize, MEMREMAP_WB); + if (!tree->mid_entry[i] || !tree->leaf[i] || !tree->mid[i]) + continue; + tree->entry[i] = pte; + for (j = 0; j < entries; j++) { + u64 mid = READ_ONCE(tree->mid[i][j]); + + if (!apple_dart_fw_table_pte(dart, mid, &phys) || + !apple_dart_fw_tree_table(dart, phys)) + continue; + tree->leaf[i][j] = memremap(phys, dart->pgsize, MEMREMAP_WB); + if (tree->leaf[i][j]) + tree->mid_entry[i][j] = mid; + } + } + return tree; +} + +/* What firmware's recorded four-level subtree translates @addr to, or 0. */ +static phys_addr_t apple_dart_fw_tree_phys(const struct apple_dart *dart, + const struct apple_dart_fw_tree *tree, + const u64 *live, u64 addr) +{ + u32 pgshift = ilog2(dart->pgsize); + u32 bits = ilog2(dart->pgsize / sizeof(u64)); + u64 mask = BIT_ULL(bits) - 1; + u64 slot = addr >> (pgshift + 2 * bits); + u64 mid = (addr >> (pgshift + bits)) & mask; + u64 page = (addr >> pgshift) & mask; + phys_addr_t phys; + int prot; + + if (!tree || !live || slot > mask || !tree->entry[slot] || + READ_ONCE(live[slot]) != tree->entry[slot] || + !tree->mid_entry[slot][mid] || + READ_ONCE(tree->mid[slot][mid]) != tree->mid_entry[slot][mid] || + !apple_dart_fw_leaf_decode(READ_ONCE(tree->leaf[slot][mid][page]), + addr, &phys, &prot)) + return 0; + return phys; +} + static int apple_dart_fw_invalidate(struct apple_dart_stream_map *stream_map, u64 lo, u64 hi); @@ -1066,6 +1237,7 @@ apple_dart_hw_map_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx) int sid; for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) { + struct apple_dart_fw_tree *tree = NULL; struct apple_dart_fw_root *fw; unsigned long flags; u32 ttbr; @@ -1112,10 +1284,14 @@ apple_dart_hw_map_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx) devm_memunmap(dart->dev, l1_tbl); return PTR_ERR(fw); } + if (!idx) + tree = apple_dart_snapshot_fw_tree(dart, sid, l1_tbl); spin_lock_irqsave(&dart->lock, flags); dart->locked_owned[sid][idx] = owned; dart->locked_ttbr[sid][idx] = l1_tbl; dart->locked_fw[sid][idx] = fw; + if (!idx) + dart->locked_fw_tree[sid] = tree; spin_unlock_irqrestore(&dart->lock, flags); } @@ -1137,6 +1313,7 @@ apple_dart_hw_unmap_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) { u64 *live = dart->locked_ttbr[sid][idx]; u64 *owned = dart->locked_owned[sid][idx]; + struct apple_dart_fw_tree *tree = NULL; struct apple_dart_fw_root *fw; unsigned long flags; bool first = false; @@ -1161,6 +1338,10 @@ apple_dart_hw_unmap_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx fw = dart->locked_fw[sid][idx]; dart->locked_fw[sid][idx] = NULL; dart->locked_ttbr[sid][idx] = NULL; + if (!idx) { + tree = dart->locked_fw_tree[sid]; + dart->locked_fw_tree[sid] = NULL; + } spin_unlock_irqrestore(&dart->lock, flags); /* Once per stream: the error is never cleared. */ if (first) @@ -1168,6 +1349,7 @@ apple_dart_hw_unmap_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx "stream %d: TLB invalidate failed retiring its root (%d); keeping the page tables of its domains until reboot\n", sid, err); apple_dart_free_fw_root(dart, fw); + apple_dart_free_fw_tree(dart, tree); devm_memunmap(dart->dev, live); devm_kfree(dart->dev, owned); dart->locked_owned[sid][idx] = NULL; @@ -2178,21 +2360,29 @@ apple_dart_inherited_phys(struct apple_dart_domain *domain, dma_addr_t address) size_t page = (address / dart->pgsize) % entries; size_t slot = address / dart->pgsize / entries; - if (!dart->locked || (!dart->fw_handoff && !dart->fw_mirror) || slot >= entries) + if (!dart->locked || (!dart->fw_handoff && !dart->fw_mirror)) return 0; spin_lock_irqsave(&dart->lock, flags); for (sid = 0; sid < dart->num_streams; sid++) { + const struct apple_dart_fw_tree *tree; struct apple_dart_fw_root *fw; - phys_addr_t phys; + phys_addr_t phys = 0; int prot; if (!apple_dart_domain_uses_fw(domain, i, map, sid)) continue; + tree = dart->locked_fw_tree[sid]; fw = dart->locked_fw[sid][0]; - if (!apple_dart_fw_slot_owned(fw, dart->locked_ttbr[sid][0], slot) || - !apple_dart_fw_leaf_decode(READ_ONCE(fw->leaf[slot][page]), - address, &phys, &prot) || - (result && result != phys)) { + if (tree) + phys = apple_dart_fw_tree_phys(dart, tree, + dart->locked_ttbr[sid][0], + address); + else if (slot >= entries || + !apple_dart_fw_slot_owned(fw, dart->locked_ttbr[sid][0], slot) || + !apple_dart_fw_leaf_decode(READ_ONCE(fw->leaf[slot][page]), + address, &phys, &prot)) + phys = 0; + if (!phys || (result && result != phys)) { spin_unlock_irqrestore(&dart->lock, flags); return 0; } @@ -2222,7 +2412,7 @@ static phys_addr_t apple_dart_iova_to_phys(struct iommu_domain *domain, if (phys) return phys; cfg = &io_pgtable_ops_to_pgtable(ops)->cfg; - if (cfg->apple_dart_cfg.n_levels != 3) + if (cfg->apple_dart_cfg.n_levels != 3 && cfg->apple_dart_cfg.n_levels != 4) return 0; /* Existing firmware translations need no replacement software mapping. */ return apple_dart_inherited_phys(dart_domain, address); @@ -3406,7 +3596,12 @@ static void apple_dart_get_fw_resv_regions(struct device *dev, struct apple_dart *dart = stream->dart; size_t entries = dart->pgsize / sizeof(u64); - if (!dart->fw_handoff || entries > DART_MAX_ROOT_ENTRIES) + /* + * Only the handoff and fw_tree reserve anything: leave the other + * fw_mirror DARTs (T6041, T8140, T8142) untouched, TCR reads included. + */ + if ((!dart->fw_handoff && !dart->fw_tree) || + entries > DART_MAX_ROOT_ENTRIES) continue; for_each_set_bit(sid, stream->sidmap, dart->num_streams) { for (idx = 0; idx < dart->hw->ttbr_count; idx++) { @@ -3430,6 +3625,13 @@ static void apple_dart_get_fw_resv_regions(struct device *dev, __set_bit(slot, slots); spin_unlock_irqrestore(&dart->lock, flags); + /* + * A three-level fw_mirror slot may repeat firmware's + * pages (apple_dart_check_fw_map()); a four-level one + * is firmware's alone, so keep the allocator out of it. + */ + if (!dart->fw_handoff && !(four && dart->fw_tree)) + continue; if (!four && check_add_overflow(window, ((u64)idx << (ilog2(dart->pgsize) + 2 * ilog2(entries))), &window)) @@ -4273,11 +4475,18 @@ static int apple_dart_probe(struct platform_device *pdev) dart->locked = apple_dart_is_locked(dart); /* Reserved table-page lifetime requires the matched three-level loader. */ dart->fw_handoff = apple_dart_fw_handoff_enabled(dart); - /* Preserve our retained-root path when no reserved-table handoff exists. */ + /* + * Preserve our retained-root path when no reserved-table handoff exists. + * T6050's boot loader leaves every display DART locked on four-level + * roots of its own (dart-dcpextN SID 23, dart-dispextN SIDs 0 and 16). + */ dart->fw_mirror = dart->locked && !dart->fw_handoff && (of_device_is_compatible(dev->of_node, "apple,t6041-dart") || + of_device_is_compatible(dev->of_node, "apple,t6050-dart") || of_device_is_compatible(dev->of_node, "apple,t8140-dart") || of_device_is_compatible(dev->of_node, "apple,t8142-dart")); + dart->fw_tree = dart->fw_mirror && + of_device_is_compatible(dev->of_node, "apple,t6050-dart"); if (of_machine_is_compatible("apple,j713") && of_machine_is_compatible("apple,t8132") && res->start == 0x394800000ULL && dart->hw->type == DART_T8110 && diff --git a/tools/asahi/apple-dart-fw-tests.py b/tools/asahi/apple-dart-fw-tests.py index d37ff84ba29ef..bf85fb3bfa2d6 100644 --- a/tools/asahi/apple-dart-fw-tests.py +++ b/tools/asahi/apple-dart-fw-tests.py @@ -4,8 +4,8 @@ from pathlib import Path import argparse, hashlib, json, re, subprocess ROOT=Path(__file__).resolve().parents[2] -NAMES=('apple_dart_fw_handoff_enabled','apple_dart_pte_to_paddr','apple_dart_fw_table_ram','apple_dart_fw_table_decode','apple_dart_fw_slot_owned','apple_dart_fw_leaf_decode','apple_dart_fw_mapping_matches','apple_dart_fw_page_matches','apple_dart_fw_slot_mirrors','apple_dart_publish_root','apple_dart_retire_root','apple_dart_free_fw_root','apple_dart_snapshot_fw_root','apple_dart_fw_slot_foreign','apple_dart_domain_uses_fw','apple_dart_check_fw_map','apple_dart_inherited_phys','apple_dart_fw_slot_range','apple_dart_get_fw_resv_regions','apple_dart_fw_invalidate','apple_dart_hw_unmap_locked_ttbr','apple_dart_retire_error','apple_dart_t8110_hw_invalidate_tlb','apple_dart_release_device','apple_dart_domain_free') -CASES=("legacy-map-match","legacy-map-mismatch","legacy-lookup","legacy-snapshot",'mirror','changed-leaf','wrong-permission','wrong-physical','malformed-leaf','vacant','changed-owner','retire','reserved-ram','unreserved','mmio','misalignment','address-width','root-encoding','leaf-as-table','snapshot','snapshot-malformed','snapshot-unreserved','snapshot-root-change','snapshot-map-failure','snapshot-allocation-failure','range-three','range-offset','range-width','range-overflow','range-underflow','range-four','range-four-last','map-firmware','map-hole','map-host','map-owned','map-unmatched','map-attached','map-wrong-physical','map-wrong-permission','map-changed-root','map-four','lookup-inherited','lookup-changed-leaf','lookup-changed-root','lookup-unmatched','reserve-firmware','reserve-teardown','reserve-unmatched','reserve-owned','invalidate-three','invalidate-four','invalidate-changed-root','invalidate-error','core-inherited','core-hole','core-changed-leaf','core-second-dart','snapshot-unmatched','snapshot-four','range-four-width','range-three-last','handoff-matched','handoff-missing','handoff-duplicate','handoff-zero','handoff-unlocked','handoff-other-soc','retire-free','retire-free-timeout','retire-release','retire-release-timeout','retire-rollback-timeout','retire-repeat-timeout') +NAMES=('apple_dart_fw_handoff_enabled','apple_dart_pte_to_paddr','apple_dart_fw_table_page','apple_dart_fw_table_ram','apple_dart_fw_table_pte','apple_dart_fw_table_decode','apple_dart_fw_tree_table','apple_dart_fw_slot_owned','apple_dart_fw_leaf_decode','apple_dart_fw_mapping_matches','apple_dart_fw_page_matches','apple_dart_fw_slot_mirrors','apple_dart_publish_root','apple_dart_retire_root','apple_dart_free_fw_root','apple_dart_snapshot_fw_root','apple_dart_free_fw_tree','apple_dart_fw_tree_phys','apple_dart_fw_slot_foreign','apple_dart_domain_uses_fw','apple_dart_check_fw_map','apple_dart_inherited_phys','apple_dart_fw_slot_range','apple_dart_get_fw_resv_regions','apple_dart_fw_invalidate','apple_dart_hw_unmap_locked_ttbr','apple_dart_retire_error','apple_dart_t8110_hw_invalidate_tlb','apple_dart_release_device','apple_dart_domain_free') +CASES=("legacy-map-match","legacy-map-mismatch","legacy-lookup","legacy-snapshot",'mirror','changed-leaf','wrong-permission','wrong-physical','malformed-leaf','vacant','changed-owner','retire','reserved-ram','unreserved','mmio','misalignment','address-width','root-encoding','leaf-as-table','snapshot','snapshot-malformed','snapshot-unreserved','snapshot-root-change','snapshot-map-failure','snapshot-allocation-failure','range-three','range-offset','range-width','range-overflow','range-underflow','range-four','range-four-last','map-firmware','map-hole','map-host','map-owned','map-unmatched','map-attached','map-wrong-physical','map-wrong-permission','map-changed-root','map-four','lookup-inherited','lookup-changed-leaf','lookup-changed-root','lookup-unmatched','reserve-firmware','reserve-teardown','reserve-unmatched','reserve-owned','invalidate-three','invalidate-four','invalidate-changed-root','invalidate-error','core-inherited','core-hole','core-changed-leaf','core-second-dart','snapshot-unmatched','snapshot-four','range-four-width','range-three-last','handoff-matched','handoff-missing','handoff-duplicate','handoff-zero','handoff-unlocked','handoff-other-soc','retire-free','retire-free-timeout','retire-release','retire-release-timeout','retire-rollback-timeout','retire-repeat-timeout','tree-lookup','tree-changed-root','tree-changed-mid','tree-unrecorded','tree-reserve','tree-mirror-only','subtree-free-partial','subtree-table-reserved','subtree-table-unclaimed','subtree-table-claimed','subtree-table-ram','subtree-table-misalignment') def function(s,name): match=re.search(r'^static[^;{}]*\b'+re.escape(name)+r'\([^;{}]*\)\s*\{',s,re.M) if not match:return None diff --git a/tools/asahi/apple-dart-fw/fixture.c b/tools/asahi/apple-dart-fw/fixture.c index f859d636ae369..14ef665a21c08 100644 --- a/tools/asahi/apple-dart-fw/fixture.c +++ b/tools/asahi/apple-dart-fw/fixture.c @@ -50,7 +50,7 @@ static unsigned lock_depth; static size_t find_next_bit(unsigned long*p,size_t n,size_t a){for(;alocked_ttbr[0][0]=NULL;retiring_dart->locked_owned[0][0]=NULL;free(retired_table);retired_table=NULL;}struct iommu_resv_region*r=®ions[region_count++];r->start=s;r->length=l;r->type=type;return r;} static void list_add_tail(struct list_head*l,struct list_head*h){h->count++;} struct apple_dart_fw_root {u64*entry;u64*accepted;u64**leaf;}; +struct apple_dart_fw_tree {u64*entry;u64**mid;u64**mid_entry;u64***leaf;}; #define DART_TCR(d,s) (s) static unsigned apple_dart_readl(struct apple_dart*d,unsigned reg){return d->tcr;} static u64 firmware_leaf[2048], ours_leaf[2048], root[2048]; @@ -80,6 +81,11 @@ static int ram_valid=1,reserved=1,map_failure,allocation_failure,change_root,map static int pfn_valid(unsigned long pfn){return ram_valid&&pfn>=4&&pfn<8;} static unsigned long pfn_to_page(unsigned long pfn){return pfn;} #define PageReserved(p) (reserved) +#define IORESOURCE_MEM 0x200 +#define IORES_DESC_NONE 0 +enum {REGION_DISJOINT,REGION_INTERSECTS,REGION_MIXED}; +static int claimed; +static int region_intersects(u64 s,size_t n,unsigned long f,unsigned long d){assert(n==SZ_16K&&f==IORESOURCE_MEM&&d==IORES_DESC_NONE);return claimed?REGION_INTERSECTS:REGION_DISJOINT;} static void*allocation(size_t n){if(allocation_failure)return NULL;return calloc(1,n);} #define kzalloc(n,g) allocation(n) #define kcalloc(n,z,g) allocation((n)*(z)) @@ -207,6 +213,22 @@ int main(int argc,char**argv){ else if(!strcmp(c,"lookup-unmatched")){d.fw_handoff=0;assert(!apple_dart_inherited_phys(&domain,0));} else assert(!strcmp(c,"lookup-inherited")); } + else if(!strncmp(c,"tree-",5)){ + /* fw_tree: firmware's recorded subtree of a four-level root (slot 0, mid 0) resolves; a changed root or mid + * entry, an unrecorded tree, and a fw_mirror DART without fw_tree resolve and reserve nothing. */ + static u64 mid_table[2048];u64*entry=calloc(2048,sizeof(u64)),**mid=calloc(2048,sizeof(u64*)),**mid_entry=calloc(2048,sizeof(u64*));u64***leaves3=calloc(2048,sizeof(u64**)); + assert(entry&&mid&&mid_entry&&leaves3);mid_entry[0]=calloc(2048,sizeof(u64));leaves3[0]=calloc(2048,sizeof(u64*));assert(mid_entry[0]&&leaves3[0]); + root[0]=0x1401;entry[0]=0x1401;mid[0]=mid_table;mid_table[0]=0x1801;mid_entry[0][0]=0x1801;leaves3[0][0]=firmware_leaf; + struct apple_dart_fw_tree tree={entry,mid,mid_entry,leaves3}; + d.fw_handoff=0;d.fw_mirror=1;d.fw_tree=strcmp(c,"tree-mirror-only")!=0;d.tcr=1;d.locked_fw[0][0]=NULL; + if(strcmp(c,"tree-unrecorded")&&strcmp(c,"tree-mirror-only"))d.locked_fw_tree[0]=&tree; + if(!strcmp(c,"tree-lookup"))assert(apple_dart_inherited_phys(&domain,0)==0x8000); + else if(!strcmp(c,"tree-changed-root")){root[0]=0x1001;assert(!apple_dart_inherited_phys(&domain,0));} + else if(!strcmp(c,"tree-changed-mid")){mid_table[0]=0x1c01;assert(!apple_dart_inherited_phys(&domain,0));} + else if(!strcmp(c,"tree-unrecorded"))assert(!apple_dart_inherited_phys(&domain,0)); + else if(!strcmp(c,"tree-reserve")){apple_dart_get_fw_resv_regions(&dev,&head);assert(head.count==1&®ions[0].start==0&®ions[0].length==(1ULL<<36)&®ions[0].type==IOMMU_RESV_RESERVED);} + else {assert(!strcmp(c,"tree-mirror-only"));assert(!apple_dart_inherited_phys(&domain,0));apple_dart_get_fw_resv_regions(&dev,&head);assert(!head.count);} + } else if(!strncmp(c,"reserve-",8)){ root[0]=0x1401;d.locked_window[0]=1ULL<<40; if(!strcmp(c,"reserve-teardown")){retired_table=calloc(2048,sizeof(u64));assert(retired_table);retired_table[0]=retired_table[5]=0x1401;d.locked_ttbr[0][0]=retired_table;release_on_alloc=1;retiring_dart=&d;} @@ -269,6 +291,17 @@ int main(int argc,char**argv){ else if(!strcmp(c,"vacant")){ours[0]=0;ours[1]=0x801;assert(!apple_dart_publish_root(&d,live,owned,ours,&fw,2));assert(live[0]==0x1401&&live[1]==0x801&&owned[1]==0x801);} else if(!strcmp(c,"changed-owner")){live[0]=0x1801;assert(apple_dart_publish_root(&d,live,owned,ours,&fw,2)==-EBUSY);assert(!owned[0]);} else if(!strcmp(c,"retire")){owned[1]=0x801;live[1]=0xc01;apple_dart_retire_root(live,owned,2);assert(live[0]==0x1401&&live[1]==0xc01&&!owned[1]);} + else if(!strcmp(c,"subtree-free-partial")){ + /* A failed allocation leaves any of the four arrays missing: freeing must not touch them. */ + struct apple_dart_fw_tree*t=calloc(1,sizeof(*t));assert(t);t->leaf=calloc(2048,sizeof(u64**));assert(t->leaf);t->leaf[0]=calloc(2048,sizeof(u64*));assert(t->leaf[0]);t->leaf[0][0]=firmware_leaf; + apple_dart_free_fw_tree(&d,t);assert(unmap_calls==1); + } + /* fw_tree tables: reserved RAM, or memory nothing claims; never Linux's RAM or a device's registers. */ + else if(!strcmp(c,"subtree-table-reserved")){claimed=1;assert(apple_dart_fw_tree_table(&d,0x14000));} + else if(!strcmp(c,"subtree-table-unclaimed")){ram_valid=0;assert(apple_dart_fw_tree_table(&d,0x14000));} + else if(!strcmp(c,"subtree-table-claimed")){ram_valid=0;claimed=1;assert(!apple_dart_fw_tree_table(&d,0x14000));} + else if(!strcmp(c,"subtree-table-ram")){reserved=0;claimed=1;assert(!apple_dart_fw_tree_table(&d,0x14000));} + else if(!strcmp(c,"subtree-table-misalignment")){ram_valid=0;assert(!apple_dart_fw_tree_table(&d,0x14001));} else if(!strcmp(c,"reserved-ram")){assert(apple_dart_fw_table_ram(&d,0x14000));} else if(!strcmp(c,"unreserved")){reserved=0;assert(!apple_dart_fw_table_ram(&d,0x14000));} else if(!strcmp(c,"mmio")){ram_valid=0;assert(!apple_dart_fw_table_ram(&d,0x14000));}