Files
UniXNU/scripts/check-smmu4k-permissions.py
2026-10-19 18:49:31 +08:00

60 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""Original LPAE permission oracle, including GMU privileged executable memory."""
import hashlib,json,re,subprocess
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1];cache=ROOT/'.cache/adreno-source';kit=ROOT/'kits/ArmSmmu4K'
out=ROOT/'build/arm-smmu-4k-permissions';out.mkdir(exist_ok=True);(out/'report.json').unlink(missing_ok=True)
pins=json.loads((ROOT/'scripts/data/adreno-inputs.json').read_text())
def sha(p):return hashlib.sha256(p.read_bytes()).hexdigest()
names=['drivers/iommu/io-pgtable-arm.c','include/linux/io-pgtable.h','include/linux/iommu.h',
'drivers/gpu/drm/msm/msm_gem.c','drivers/gpu/drm/msm/adreno/a6xx_gmu.c']
for n in names:
if sha(cache/n)!=pins['linux']['files'][n]['sha256']:raise SystemExit('Pinned permission source differs '+n)
source=(cache/names[0]).read_text();header=(cache/names[1]).read_text()
def block(source,sig):
start=source.index(sig);at=source.index('{',start)+1;depth=1
while depth:
if source[at]=='{':depth+=1
elif source[at]=='}':depth-=1
at+=1
return source[start:at]
function=block(source,'static arm_lpae_iopte arm_lpae_prot_to_pte(')
definitions={}
for text in [source,header,(cache/names[2]).read_text()]:
for m in re.finditer(r'^\s*#define\s+(\w+)(\([^\n]*?\))?\s+((?:[^\n]*\\\n)*[^\n]*)',text,re.M):definitions[m[1]]=m[0]
selected={}
def include(name):
if name in selected:return
selected[name]=definitions[name]
for dep in set(re.findall(r'(?:ARM_LPAE_|IOMMU_|IO_PGTABLE_QUIRK_)[A-Za-z0-9_]+',definitions[name]))-{name}:include(dep)
for name in set(re.findall(r'(?:ARM_LPAE_|IOMMU_|IO_PGTABLE_QUIRK_)[A-Za-z0-9_]+',function)):include(name)
prelude='''#include <stdint.h>
typedef uint64_t arm_lpae_iopte;
#define BIT(n) (1u<<(n))
'''+block(header,'enum io_pgtable_fmt {')+';\n'+'\n'.join(selected.values())+'''
struct arm_lpae_io_pgtable {struct {enum io_pgtable_fmt fmt;struct {unsigned quirks;} cfg;} iop;};
'''
oracle=out/'original-permissions.c';oracle.write_text(prelude+function+'''
uint64_t source_permission_pte(unsigned permissions,unsigned cache) {
struct arm_lpae_io_pgtable data={.iop={.fmt=ARM_64_LPAE_S1}};
int prot=IOMMU_READ;
if(permissions&2)prot|=IOMMU_WRITE;
if(!(permissions&4))prot|=IOMMU_NOEXEC;
if(permissions&8)prot|=IOMMU_PRIV;
if(cache)prot|=IOMMU_CACHE;
return arm_lpae_prot_to_pte(&data,prot);
}
''')
gem=block((cache/names[3]).read_text(),'int msm_gem_prot(')
gmu=block((cache/names[4]).read_text(),'static int a6xx_gmu_memory_alloc(')
if 'MSM_BO_MAP_PRIV' not in gmu or 'MSM_BO_MAP_PRIV' not in gem or 'IOMMU_PRIV' not in gem or 'IOMMU_NOEXEC' in gem:
raise SystemExit('Pinned GMU/MSM mapping policy changed')
sources=[kit/'ArmSmmu4K.c',kit/'test_permissions.c'];test=out/'test-permissions'
subprocess.run(['xcrun','clang','-std=c11','-O2','-Wall','-Wextra','-Werror','-fsanitize=address,undefined',*sources,oracle,'-o',test],check=True,timeout=60)
r=subprocess.run([test],check=True,capture_output=True,text=True,timeout=30);print(r.stdout,end='')
if '544 lower/upper' not in r.stdout:raise SystemExit('Permission case count changed')
(out/'host.log').write_text(r.stdout)
(out/'report.json').write_text(json.dumps(dict(passed=True,linux_revision=pins['linux']['commit'],pinned_sources={n:sha(cache/n) for n in names},
sources={str(p.relative_to(ROOT)):sha(p) for p in sources+[kit/'ArmSmmu4K.h',Path(__file__)]},oracle_sha256=sha(oracle),
profiles=544,leaves=1088,host_asan_ubsan=True,cpu_permissions_modified=False,active_hardware_mapping_qualified=False),indent=2)+'\n')