1from __future__ import absolute_import, division, print_function 2 3from builtins import hex 4from builtins import range 5 6from xnu import * 7import xnudefines 8from kdp import * 9from utils import * 10import struct 11 12def ReadPhysInt(phys_addr, bitsize = 64, cpuval = None): 13 """ Read a physical memory data based on address. 14 params: 15 phys_addr : int - Physical address to read 16 bitsize : int - defines how many bytes to read. defaults to 64 bit 17 cpuval : None (optional) 18 returns: 19 int - int value read from memory. in case of failure 0xBAD10AD is returned. 20 """ 21 if "kdp" == GetConnectionProtocol(): 22 return KDPReadPhysMEM(phys_addr, bitsize) 23 24 #NO KDP. Attempt to use physical memory 25 paddr_in_kva = kern.PhysToKernelVirt(int(phys_addr)) 26 if paddr_in_kva : 27 if bitsize == 64 : 28 return kern.GetValueFromAddress(paddr_in_kva, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned() 29 if bitsize == 32 : 30 return kern.GetValueFromAddress(paddr_in_kva, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned() 31 if bitsize == 16 : 32 return kern.GetValueFromAddress(paddr_in_kva, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned() 33 if bitsize == 8 : 34 return kern.GetValueFromAddress(paddr_in_kva, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned() 35 return 0xBAD10AD 36 37@lldb_command('readphys') 38def ReadPhys(cmd_args = None): 39 """ Reads the specified untranslated address 40 The argument is interpreted as a physical address, and the 64-bit word 41 addressed is displayed. 42 usage: readphys <nbits> <address> 43 nbits: 8,16,32,64 44 address: 1234 or 0x1234 45 """ 46 if cmd_args == None or len(cmd_args) < 2: 47 print("Insufficient arguments.", ReadPhys.__doc__) 48 return False 49 else: 50 nbits = ArgumentStringToInt(cmd_args[0]) 51 phys_addr = ArgumentStringToInt(cmd_args[1]) 52 print("{0: <#x}".format(ReadPhysInt(phys_addr, nbits))) 53 return True 54 55lldb_alias('readphys8', 'readphys 8 ') 56lldb_alias('readphys16', 'readphys 16 ') 57lldb_alias('readphys32', 'readphys 32 ') 58lldb_alias('readphys64', 'readphys 64 ') 59 60def KDPReadPhysMEM(address, bits): 61 """ Setup the state for READPHYSMEM64 commands for reading data via kdp 62 params: 63 address : int - address where to read the data from 64 bits : int - number of bits in the intval (8/16/32/64) 65 returns: 66 int: read value from memory. 67 0xBAD10AD: if failed to read data. 68 """ 69 retval = 0xBAD10AD 70 if "kdp" != GetConnectionProtocol(): 71 print("Target is not connected over kdp. Nothing to do here.") 72 return retval 73 74 if "hwprobe" == KDPMode(): 75 # Send the proper KDP command and payload to the bare metal debug tool via a KDP server 76 addr_for_kdp = struct.unpack("<Q", struct.pack(">Q", address))[0] 77 byte_count = struct.unpack("<I", struct.pack(">I", bits // 8))[0] 78 packet = "{0:016x}{1:08x}{2:04x}".format(addr_for_kdp, byte_count, 0x0) 79 80 ret_obj = lldb.SBCommandReturnObject() 81 ci = lldb.debugger.GetCommandInterpreter() 82 ci.HandleCommand('process plugin packet send -c 25 -p {0}'.format(packet), ret_obj) 83 84 if ret_obj.Succeeded(): 85 value = ret_obj.GetOutput() 86 87 if bits == 64 : 88 pack_fmt = "<Q" 89 unpack_fmt = ">Q" 90 if bits == 32 : 91 pack_fmt = "<I" 92 unpack_fmt = ">I" 93 if bits == 16 : 94 pack_fmt = "<H" 95 unpack_fmt = ">H" 96 if bits == 8 : 97 pack_fmt = "<B" 98 unpack_fmt = ">B" 99 100 retval = struct.unpack(unpack_fmt, struct.pack(pack_fmt, int(value[-((bits // 4)+1):], 16)))[0] 101 102 else: 103 input_address = unsigned(addressof(kern.globals.manual_pkt.input)) 104 len_address = unsigned(addressof(kern.globals.manual_pkt.len)) 105 data_address = unsigned(addressof(kern.globals.manual_pkt.data)) 106 107 if not WriteInt32ToMemoryAddress(0, input_address): 108 return retval 109 110 kdp_pkt_size = GetType('kdp_readphysmem64_req_t').GetByteSize() 111 if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): 112 return retval 113 114 data_addr = int(addressof(kern.globals.manual_pkt)) 115 pkt = kern.GetValueFromAddress(data_addr, 'kdp_readphysmem64_req_t *') 116 117 header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READPHYSMEM64'), length=kdp_pkt_size) 118 119 if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and 120 WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and 121 WriteInt32ToMemoryAddress((bits // 8), int(addressof(pkt.nbytes))) and 122 WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu))) 123 ): 124 125 if WriteInt32ToMemoryAddress(1, input_address): 126 # now read data from the kdp packet 127 data_address = unsigned(addressof(kern.GetValueFromAddress(int(addressof(kern.globals.manual_pkt.data)), 'kdp_readphysmem64_reply_t *').data)) 128 if bits == 64 : 129 retval = kern.GetValueFromAddress(data_address, 'uint64_t *').GetSBValue().Dereference().GetValueAsUnsigned() 130 if bits == 32 : 131 retval = kern.GetValueFromAddress(data_address, 'uint32_t *').GetSBValue().Dereference().GetValueAsUnsigned() 132 if bits == 16 : 133 retval = kern.GetValueFromAddress(data_address, 'uint16_t *').GetSBValue().Dereference().GetValueAsUnsigned() 134 if bits == 8 : 135 retval = kern.GetValueFromAddress(data_address, 'uint8_t *').GetSBValue().Dereference().GetValueAsUnsigned() 136 137 return retval 138 139 140def KDPWritePhysMEM(address, intval, bits): 141 """ Setup the state for WRITEPHYSMEM64 commands for saving data in kdp 142 params: 143 address : int - address where to save the data 144 intval : int - integer value to be stored in memory 145 bits : int - number of bits in the intval (8/16/32/64) 146 returns: 147 boolean: True if the write succeeded. 148 """ 149 if "kdp" != GetConnectionProtocol(): 150 print("Target is not connected over kdp. Nothing to do here.") 151 return False 152 153 if "hwprobe" == KDPMode(): 154 # Send the proper KDP command and payload to the bare metal debug tool via a KDP server 155 addr_for_kdp = struct.unpack("<Q", struct.pack(">Q", address))[0] 156 byte_count = struct.unpack("<I", struct.pack(">I", bits // 8))[0] 157 158 if bits == 64 : 159 pack_fmt = ">Q" 160 unpack_fmt = "<Q" 161 if bits == 32 : 162 pack_fmt = ">I" 163 unpack_fmt = "<I" 164 if bits == 16 : 165 pack_fmt = ">H" 166 unpack_fmt = "<H" 167 if bits == 8 : 168 pack_fmt = ">B" 169 unpack_fmt = "<B" 170 171 data_val = struct.unpack(unpack_fmt, struct.pack(pack_fmt, intval))[0] 172 173 packet = "{0:016x}{1:08x}{2:04x}{3:016x}".format(addr_for_kdp, byte_count, 0x0, data_val) 174 175 ret_obj = lldb.SBCommandReturnObject() 176 ci = lldb.debugger.GetCommandInterpreter() 177 ci.HandleCommand('process plugin packet send -c 26 -p {0}'.format(packet), ret_obj) 178 179 if ret_obj.Succeeded(): 180 return True 181 else: 182 return False 183 184 else: 185 input_address = unsigned(addressof(kern.globals.manual_pkt.input)) 186 len_address = unsigned(addressof(kern.globals.manual_pkt.len)) 187 data_address = unsigned(addressof(kern.globals.manual_pkt.data)) 188 if not WriteInt32ToMemoryAddress(0, input_address): 189 return False 190 191 kdp_pkt_size = GetType('kdp_writephysmem64_req_t').GetByteSize() + (bits // 8) 192 if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): 193 return False 194 195 data_addr = int(addressof(kern.globals.manual_pkt)) 196 pkt = kern.GetValueFromAddress(data_addr, 'kdp_writephysmem64_req_t *') 197 198 header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEPHYSMEM64'), length=kdp_pkt_size) 199 200 if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and 201 WriteInt64ToMemoryAddress(address, int(addressof(pkt.address))) and 202 WriteInt32ToMemoryAddress(bits // 8, int(addressof(pkt.nbytes))) and 203 WriteInt16ToMemoryAddress(xnudefines.lcpu_self, int(addressof(pkt.lcpu))) 204 ): 205 206 if bits == 8: 207 if not WriteInt8ToMemoryAddress(intval, int(addressof(pkt.data))): 208 return False 209 if bits == 16: 210 if not WriteInt16ToMemoryAddress(intval, int(addressof(pkt.data))): 211 return False 212 if bits == 32: 213 if not WriteInt32ToMemoryAddress(intval, int(addressof(pkt.data))): 214 return False 215 if bits == 64: 216 if not WriteInt64ToMemoryAddress(intval, int(addressof(pkt.data))): 217 return False 218 if WriteInt32ToMemoryAddress(1, input_address): 219 return True 220 return False 221 222 223def WritePhysInt(phys_addr, int_val, bitsize = 64): 224 """ Write and integer value in a physical memory data based on address. 225 params: 226 phys_addr : int - Physical address to read 227 int_val : int - int value to write in memory 228 bitsize : int - defines how many bytes to read. defaults to 64 bit 229 returns: 230 bool - True if write was successful. 231 """ 232 if "kdp" == GetConnectionProtocol(): 233 if not KDPWritePhysMEM(phys_addr, int_val, bitsize): 234 print("Failed to write via KDP.") 235 return False 236 return True 237 #We are not connected via KDP. So do manual math and savings. 238 print("Failed: Write to physical memory is not supported for %s connection." % GetConnectionProtocol()) 239 return False 240 241@lldb_command('writephys') 242def WritePhys(cmd_args=None): 243 """ writes to the specified untranslated address 244 The argument is interpreted as a physical address, and the 64-bit word 245 addressed is displayed. 246 usage: writephys <nbits> <address> <value> 247 nbits: 8,16,32,64 248 address: 1234 or 0x1234 249 value: int value to be written 250 ex. (lldb)writephys 16 0x12345abcd 0x25 251 """ 252 if cmd_args == None or len(cmd_args) < 3: 253 print("Invalid arguments.", WritePhys.__doc__) 254 else: 255 nbits = ArgumentStringToInt(cmd_args[0]) 256 phys_addr = ArgumentStringToInt(cmd_args[1]) 257 int_value = ArgumentStringToInt(cmd_args[2]) 258 print(WritePhysInt(phys_addr, int_value, nbits)) 259 260 261lldb_alias('writephys8', 'writephys 8 ') 262lldb_alias('writephys16', 'writephys 16 ') 263lldb_alias('writephys32', 'writephys 32 ') 264lldb_alias('writephys64', 'writephys 64 ') 265 266 267def _PT_Step(paddr, index, verbose_level = vSCRIPT): 268 """ 269 Step to lower-level page table and print attributes 270 paddr: current page table entry physical address 271 index: current page table entry index (0..511) 272 verbose_level: vHUMAN: print nothing 273 vSCRIPT: print basic information 274 vDETAIL: print basic information and hex table dump 275 returns: (pt_paddr, pt_valid, pt_large) 276 pt_paddr: next level page table entry physical address 277 or null if invalid 278 pt_valid: 1 if $kgm_pt_paddr is valid, 0 if the walk 279 should be aborted 280 pt_large: 1 if kgm_pt_paddr is a page frame address 281 of a large page and not another page table entry 282 """ 283 entry_addr = paddr + (8 * index) 284 entry = ReadPhysInt(entry_addr, 64, xnudefines.lcpu_self ) 285 out_string = '' 286 if verbose_level >= vDETAIL: 287 for pte_loop in range(0, 512): 288 paddr_tmp = paddr + (8 * pte_loop) 289 out_string += "{0: <#020x}:\t {1: <#020x}\n".format(paddr_tmp, ReadPhysInt(paddr_tmp, 64, xnudefines.lcpu_self)) 290 paddr_mask = ~((0xfff<<52) | 0xfff) 291 paddr_large_mask = ~((0xfff<<52) | 0x1fffff) 292 pt_valid = False 293 pt_large = False 294 pt_paddr = 0 295 if verbose_level < vSCRIPT: 296 if entry & 0x1 : 297 pt_valid = True 298 pt_large = False 299 pt_paddr = entry & paddr_mask 300 if entry & (0x1 <<7): 301 pt_large = True 302 pt_paddr = entry & paddr_large_mask 303 else: 304 out_string+= "{0: <#020x}:\n\t{1:#020x}\n\t".format(entry_addr, entry) 305 if entry & 0x1: 306 out_string += " valid" 307 pt_paddr = entry & paddr_mask 308 pt_valid = True 309 else: 310 out_string += " invalid" 311 pt_paddr = 0 312 pt_valid = False 313 if entry & (0x1 << 62): 314 out_string += " compressed" 315 #Stop decoding other bits 316 entry = 0 317 if entry & (0x1 << 1): 318 out_string += " writable" 319 else: 320 out_string += " read-only" 321 322 if entry & (0x1 << 2): 323 out_string += " user" 324 else: 325 out_string += " supervisor" 326 327 if entry & (0x1 << 3): 328 out_string += " PWT" 329 330 if entry & (0x1 << 4): 331 out_string += " PCD" 332 333 if entry & (0x1 << 5): 334 out_string += " accessed" 335 336 if entry & (0x1 << 6): 337 out_string += " dirty" 338 339 if entry & (0x1 << 7): 340 out_string += " large" 341 pt_large = True 342 else: 343 pt_large = False 344 345 if entry & (0x1 << 8): 346 out_string += " global" 347 348 if entry & (0x3 << 9): 349 out_string += " avail:{0:x}".format((entry >> 9) & 0x3) 350 351 if entry & (0x1 << 63): 352 out_string += " noexec" 353 print(out_string) 354 return (pt_paddr, pt_valid, pt_large) 355 356def _PT_StepEPT(paddr, index, verbose_level = vSCRIPT): 357 """ 358 Step to lower-level page table and print attributes for EPT pmap 359 paddr: current page table entry physical address 360 index: current page table entry index (0..511) 361 verbose_level: vHUMAN: print nothing 362 vSCRIPT: print basic information 363 vDETAIL: print basic information and hex table dump 364 returns: (pt_paddr, pt_valid, pt_large) 365 pt_paddr: next level page table entry physical address 366 or null if invalid 367 pt_valid: 1 if $kgm_pt_paddr is valid, 0 if the walk 368 should be aborted 369 pt_large: 1 if kgm_pt_paddr is a page frame address 370 of a large page and not another page table entry 371 """ 372 entry_addr = paddr + (8 * index) 373 entry = ReadPhysInt(entry_addr, 64, xnudefines.lcpu_self ) 374 out_string = '' 375 if verbose_level >= vDETAIL: 376 for pte_loop in range(0, 512): 377 paddr_tmp = paddr + (8 * pte_loop) 378 out_string += "{0: <#020x}:\t {1: <#020x}\n".format(paddr_tmp, ReadPhysInt(paddr_tmp, 64, xnudefines.lcpu_self)) 379 paddr_mask = ~((0xfff<<52) | 0xfff) 380 paddr_large_mask = ~((0xfff<<52) | 0x1fffff) 381 pt_valid = False 382 pt_large = False 383 pt_paddr = 0 384 if verbose_level < vSCRIPT: 385 if entry & 0x7 : 386 pt_valid = True 387 pt_large = False 388 pt_paddr = entry & paddr_mask 389 if entry & (0x1 <<7): 390 pt_large = True 391 pt_paddr = entry & paddr_large_mask 392 else: 393 out_string+= "{0: <#020x}:\n\t{1:#020x}\n\t".format(entry_addr, entry) 394 if entry & 0x7: 395 out_string += "valid" 396 pt_paddr = entry & paddr_mask 397 pt_valid = True 398 else: 399 out_string += "invalid" 400 pt_paddr = 0 401 pt_valid = False 402 if entry & (0x1 << 62): 403 out_string += " compressed" 404 #Stop decoding other bits 405 entry = 0 406 if entry & 0x1: 407 out_string += " readable" 408 else: 409 out_string += " no read" 410 if entry & (0x1 << 1): 411 out_string += " writable" 412 else: 413 out_string += " no write" 414 415 if entry & (0x1 << 2): 416 out_string += " executable" 417 else: 418 out_string += " no exec" 419 420 ctype = entry & 0x38 421 if ctype == 0x30: 422 out_string += " cache-WB" 423 elif ctype == 0x28: 424 out_string += " cache-WP" 425 elif ctype == 0x20: 426 out_string += " cache-WT" 427 elif ctype == 0x8: 428 out_string += " cache-WC" 429 else: 430 out_string += " cache-NC" 431 432 if (entry & 0x40) == 0x40: 433 out_string += " Ignore-PTA" 434 435 if (entry & 0x100) == 0x100: 436 out_string += " accessed" 437 438 if (entry & 0x200) == 0x200: 439 out_string += " dirty" 440 441 if entry & (0x1 << 7): 442 out_string += " large" 443 pt_large = True 444 else: 445 pt_large = False 446 print(out_string) 447 return (pt_paddr, pt_valid, pt_large) 448 449def _PmapL4Walk(pmap_addr_val,vaddr, ept_pmap, verbose_level = vSCRIPT): 450 """ Walk the l4 pmap entry. 451 params: pmap_addr_val - core.value representing kernel data of type pmap_addr_t 452 vaddr : int - virtual address to walk 453 """ 454 pt_paddr = unsigned(pmap_addr_val) 455 pt_valid = (unsigned(pmap_addr_val) != 0) 456 pt_large = 0 457 pframe_offset = 0 458 if pt_valid: 459 # Lookup bits 47:39 of linear address in PML4T 460 pt_index = (vaddr >> 39) & 0x1ff 461 pframe_offset = vaddr & 0x7fffffffff 462 if verbose_level > vHUMAN : 463 print("pml4 (index {0:d}):".format(pt_index)) 464 if not(ept_pmap): 465 (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) 466 else: 467 (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) 468 if pt_valid: 469 # Lookup bits 38:30 of the linear address in PDPT 470 pt_index = (vaddr >> 30) & 0x1ff 471 pframe_offset = vaddr & 0x3fffffff 472 if verbose_level > vHUMAN: 473 print("pdpt (index {0:d}):".format(pt_index)) 474 if not(ept_pmap): 475 (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) 476 else: 477 (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) 478 if pt_valid and not pt_large: 479 #Lookup bits 29:21 of the linear address in PDPT 480 pt_index = (vaddr >> 21) & 0x1ff 481 pframe_offset = vaddr & 0x1fffff 482 if verbose_level > vHUMAN: 483 print("pdt (index {0:d}):".format(pt_index)) 484 if not(ept_pmap): 485 (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) 486 else: 487 (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) 488 if pt_valid and not pt_large: 489 #Lookup bits 20:21 of linear address in PT 490 pt_index = (vaddr >> 12) & 0x1ff 491 pframe_offset = vaddr & 0xfff 492 if verbose_level > vHUMAN: 493 print("pt (index {0:d}):".format(pt_index)) 494 if not(ept_pmap): 495 (pt_paddr, pt_valid, pt_large) = _PT_Step(pt_paddr, pt_index, verbose_level) 496 else: 497 (pt_paddr, pt_valid, pt_large) = _PT_StepEPT(pt_paddr, pt_index, verbose_level) 498 paddr = 0 499 paddr_isvalid = False 500 if pt_valid: 501 paddr = pt_paddr + pframe_offset 502 paddr_isvalid = True 503 504 if verbose_level > vHUMAN: 505 if paddr_isvalid: 506 pvalue = ReadPhysInt(paddr, 32, xnudefines.lcpu_self) 507 print("phys {0: <#020x}: {1: <#020x}".format(paddr, pvalue)) 508 else: 509 print("no translation") 510 511 return paddr 512 513def PmapWalkX86_64(pmapval, vaddr, verbose_level = vSCRIPT): 514 """ 515 params: pmapval - core.value representing pmap_t in kernel 516 vaddr: int - int representing virtual address to walk 517 """ 518 if pmapval.pm_cr3 != 0: 519 if verbose_level > vHUMAN: 520 print("Using normal Intel PMAP from pm_cr3\n") 521 return _PmapL4Walk(pmapval.pm_cr3, vaddr, 0, config['verbosity']) 522 else: 523 if verbose_level > vHUMAN: 524 print("Using EPT pmap from pm_eptp\n") 525 return _PmapL4Walk(pmapval.pm_eptp, vaddr, 1, config['verbosity']) 526 527def assert_64bit(val): 528 assert(val < 2**64) 529 530ARM64_TTE_SIZE = 8 531ARM64_TTE_SHIFT = 3 532ARM64_VMADDR_BITS = 48 533 534def PmapBlockOffsetMaskARM64(page_size, level): 535 assert level >= 0 and level <= 3 536 ttentries = (page_size // ARM64_TTE_SIZE) 537 return page_size * (ttentries ** (3 - level)) - 1 538 539def PmapBlockBaseMaskARM64(page_size, level): 540 assert level >= 0 and level <= 3 541 return ((1 << ARM64_VMADDR_BITS) - 1) & ~PmapBlockOffsetMaskARM64(page_size, level) 542 543def PmapDecodeTTEARM64(tte, level, stage2 = False, is_iommu_tte = False): 544 """ Display the bits of an ARM64 translation table or page table entry 545 in human-readable form. 546 tte: integer value of the TTE/PTE 547 level: translation table level. Valid values are 1, 2, or 3. 548 is_iommu_tte: True if the TTE is from an IOMMU's page table, False otherwise. 549 """ 550 assert(isinstance(level, numbers.Integral)) 551 assert_64bit(tte) 552 553 if tte & 0x1 == 0x0: 554 print("Invalid.") 555 return 556 557 if (tte & 0x2 == 0x2) and (level != 0x3): 558 print("Type = Table pointer.") 559 print("Table addr = {:#x}.".format(tte & 0xfffffffff000)) 560 561 if not stage2: 562 print("PXN = {:#x}.".format((tte >> 59) & 0x1)) 563 print("XN = {:#x}.".format((tte >> 60) & 0x1)) 564 print("AP = {:#x}.".format((tte >> 61) & 0x3)) 565 print("NS = {:#x}.".format(tte >> 63)) 566 else: 567 print("Type = Block.") 568 569 if stage2: 570 print("S2 MemAttr = {:#x}.".format((tte >> 2) & 0xf)) 571 else: 572 attr_index = (tte >> 2) & 0x7 573 attr_string = { 0: 'WRITEBACK', 1: 'WRITECOMB', 2: 'WRITETHRU', 574 3: 'CACHE DISABLE', 4: 'INNERWRITEBACK', 5: 'POSTED', 575 6: 'POSTED_REORDERED', 7: 'POSTED_COMBINED_REORDERED' } 576 577 # Only show the string version of the AttrIdx for CPU mappings since 578 # these values don't apply to IOMMU mappings. 579 if is_iommu_tte: 580 print("AttrIdx = {:#x}.".format(attr_index)) 581 else: 582 print("AttrIdx = {:#x} ({:s}).".format(attr_index, attr_string[attr_index])) 583 print("NS = {:#x}.".format((tte >> 5) & 0x1)) 584 585 if stage2: 586 print("S2AP = {:#x}.".format((tte >> 6) & 0x3)) 587 else: 588 print("AP = {:#x}.".format((tte >> 6) & 0x3)) 589 590 print("SH = {:#x}.".format((tte >> 8) & 0x3)) 591 print("AF = {:#x}.".format((tte >> 10) & 0x1)) 592 593 if not stage2: 594 print("nG = {:#x}.".format((tte >> 11) & 0x1)) 595 596 print("HINT = {:#x}.".format((tte >> 52) & 0x1)) 597 598 if stage2: 599 print("S2XN = {:#x}.".format((tte >> 53) & 0x3)) 600 else: 601 print("PXN = {:#x}.".format((tte >> 53) & 0x1)) 602 print("XN = {:#x}.".format((tte >> 54) & 0x1)) 603 604 print("SW Use = {:#x}.".format((tte >> 55) & 0xf)) 605 606 return 607 608def PmapTTnIndexARM64(vaddr, pmap_pt_attr): 609 pta_max_level = unsigned(pmap_pt_attr.pta_max_level) 610 611 tt_index = [] 612 for i in range(pta_max_level + 1): 613 tt_index.append((vaddr & unsigned(pmap_pt_attr.pta_level_info[i].index_mask)) \ 614 >> unsigned(pmap_pt_attr.pta_level_info[i].shift)) 615 616 return tt_index 617 618def PmapWalkARM64(pmap_pt_attr, root_tte, vaddr, verbose_level = vHUMAN): 619 assert(type(vaddr) in (int, int)) 620 assert_64bit(vaddr) 621 assert_64bit(root_tte) 622 623 # Obtain pmap attributes 624 page_size = pmap_pt_attr.pta_page_size 625 page_offset_mask = (page_size - 1) 626 page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask) 627 tt_index = PmapTTnIndexARM64(vaddr, pmap_pt_attr) 628 stage2 = bool(pmap_pt_attr.stage2 if hasattr(pmap_pt_attr, 'stage2') else False) 629 630 # The pmap starts at a page table level that is defined by register 631 # values; the root level can be obtained from the attributes structure 632 level = unsigned(pmap_pt_attr.pta_root_level) 633 634 root_tt_index = tt_index[level] 635 root_pgtable_num_ttes = (unsigned(pmap_pt_attr.pta_level_info[level].index_mask) >> \ 636 unsigned(pmap_pt_attr.pta_level_info[level].shift)) + 1 637 tte = int(unsigned(root_tte[root_tt_index])) 638 639 # Walk the page tables 640 paddr = -1 641 max_level = unsigned(pmap_pt_attr.pta_max_level) 642 is_valid = True 643 is_leaf = False 644 645 while (level <= max_level): 646 if verbose_level >= vSCRIPT: 647 print("L{} entry: {:#x}".format(level, tte)) 648 if verbose_level >= vDETAIL: 649 PmapDecodeTTEARM64(tte, level, stage2) 650 651 if tte & 0x1 == 0x0: 652 if verbose_level >= vHUMAN: 653 print("L{} entry invalid: {:#x}\n".format(level, tte)) 654 655 is_valid = False 656 break 657 658 # Handle leaf entry 659 if tte & 0x2 == 0x0 or level == max_level: 660 base_mask = page_base_mask if level == max_level else PmapBlockBaseMaskARM64(page_size, level) 661 offset_mask = page_offset_mask if level == max_level else PmapBlockOffsetMaskARM64(page_size, level) 662 paddr = tte & base_mask 663 paddr = paddr | (vaddr & offset_mask) 664 665 if level != max_level: 666 print("phys: {:#x}".format(paddr)) 667 668 is_leaf = True 669 break 670 else: 671 # Handle page table entry 672 next_phys = (tte & page_base_mask) + (ARM64_TTE_SIZE * tt_index[level + 1]) 673 assert(isinstance(next_phys, numbers.Integral)) 674 675 next_virt = kern.PhysToKernelVirt(next_phys) 676 assert(isinstance(next_virt, numbers.Integral)) 677 678 if verbose_level >= vDETAIL: 679 print("L{} physical address: {:#x}. L{} virtual address: {:#x}".format(level + 1, next_phys, level + 1, next_virt)) 680 681 ttep = kern.GetValueFromAddress(next_virt, "tt_entry_t*") 682 tte = int(unsigned(dereference(ttep))) 683 assert(isinstance(tte, numbers.Integral)) 684 685 # We've parsed one level, so go to the next level 686 assert(level <= 3) 687 level = level + 1 688 689 690 if verbose_level >= vHUMAN: 691 if paddr: 692 print("Translation of {:#x} is {:#x}.".format(vaddr, paddr)) 693 else: 694 print("(no translation)") 695 696 return paddr 697 698def PmapWalk(pmap, vaddr, verbose_level = vHUMAN): 699 if kern.arch == 'x86_64': 700 return PmapWalkX86_64(pmap, vaddr, verbose_level) 701 elif kern.arch.startswith('arm64'): 702 # Obtain pmap attributes from pmap structure 703 pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr 704 return PmapWalkARM64(pmap_pt_attr, pmap.tte, vaddr, verbose_level) 705 else: 706 raise NotImplementedError("PmapWalk does not support {0}".format(kern.arch)) 707 708@lldb_command('pmap_walk') 709def PmapWalkHelper(cmd_args=None): 710 """ Perform a page-table walk in <pmap> for <virtual_address>. 711 Syntax: (lldb) pmap_walk <pmap> <virtual_address> [-v] [-e] 712 Multiple -v's can be specified for increased verbosity 713 """ 714 if cmd_args == None or len(cmd_args) < 2: 715 raise ArgumentError("Too few arguments to pmap_walk.") 716 717 pmap = kern.GetValueAsType(cmd_args[0], 'pmap_t') 718 addr = ArgumentStringToInt(cmd_args[1]) 719 PmapWalk(pmap, addr, config['verbosity']) 720 return 721 722def GetMemoryAttributesFromUser(requested_type): 723 pmap_attr_dict = { 724 '4k' : kern.globals.pmap_pt_attr_4k, 725 '16k' : kern.globals.pmap_pt_attr_16k, 726 '16k_s2' : kern.globals.pmap_pt_attr_16k_stage2 if hasattr(kern.globals, 'pmap_pt_attr_16k_stage2') else None, 727 } 728 729 requested_type = requested_type.lower() 730 if requested_type not in pmap_attr_dict: 731 return None 732 733 return pmap_attr_dict[requested_type] 734 735@lldb_command('ttep_walk') 736def TTEPWalkPHelper(cmd_args=None): 737 """ Perform a page-table walk in <root_ttep> for <virtual_address>. 738 Syntax: (lldb) ttep_walk <root_ttep> <virtual_address> [4k|16k|16k_s2] [-v] [-e] 739 Multiple -v's can be specified for increased verbosity 740 """ 741 if cmd_args == None or len(cmd_args) < 2: 742 raise ArgumentError("Too few arguments to ttep_walk.") 743 744 if not kern.arch.startswith('arm64'): 745 raise NotImplementedError("ttep_walk does not support {0}".format(kern.arch)) 746 747 tte = kern.GetValueFromAddress(kern.PhysToKernelVirt(ArgumentStringToInt(cmd_args[0])), 'unsigned long *') 748 addr = ArgumentStringToInt(cmd_args[1]) 749 750 pmap_pt_attr = kern.globals.native_pt_attr if len(cmd_args) < 3 else GetMemoryAttributesFromUser(cmd_args[2]) 751 if pmap_pt_attr is None: 752 raise ArgumentError("Invalid translation attribute type.") 753 754 return PmapWalkARM64(pmap_pt_attr, tte, addr, config['verbosity']) 755 756@lldb_command('decode_tte') 757def DecodeTTE(cmd_args=None): 758 """ Decode the bits in the TTE/PTE value specified <tte_val> for translation level <level> and stage [s1|s2] 759 Syntax: (lldb) decode_tte <tte_val> <level> [s1|s2] 760 """ 761 if cmd_args == None or len(cmd_args) < 2: 762 raise ArgumentError("Too few arguments to decode_tte.") 763 if len(cmd_args) > 2 and cmd_args[2] not in ["s1", "s2"]: 764 raise ArgumentError("{} is not a valid stage of translation.".format(cmd_args[2])) 765 if kern.arch.startswith('arm64'): 766 stage2 = True if len(cmd_args) > 2 and cmd_args[2] == "s2" else False 767 PmapDecodeTTEARM64(ArgumentStringToInt(cmd_args[0]), ArgumentStringToInt(cmd_args[1]), stage2) 768 else: 769 raise NotImplementedError("decode_tte does not support {0}".format(kern.arch)) 770 771 772PVH_HIGH_FLAGS_ARM64 = (1 << 62) | (1 << 61) | (1 << 60) | (1 << 59) | (1 << 58) | (1 << 57) | (1 << 56) | (1 << 55) 773PVH_HIGH_FLAGS_ARM32 = (1 << 31) 774 775def PVDumpPTE(pvep, ptep, verbose_level = vHUMAN): 776 """ Dump information about a single mapping retrieved by the pv_head_table. 777 778 pvep: Either a pointer to the PVE object if the PVH entry is PVH_TYPE_PVEP, 779 or None if type PVH_TYPE_PTEP. 780 ptep: For type PVH_TYPE_PTEP this should just be the raw PVH entry with 781 the high flags already set (the type bits don't need to be cleared). 782 For type PVH_TYPE_PVEP this will be the value retrieved from the 783 pve_ptep[] array. 784 """ 785 if kern.arch.startswith('arm64'): 786 iommu_flag = 0x4 787 iommu_table_flag = 1 << 63 788 else: 789 iommu_flag = 0 790 iommu_table_flag = 0 791 792 # AltAcct status is only stored in the ptep for PVH_TYPE_PVEP entries. 793 if pvep is not None and (ptep & 0x1): 794 # Note: It's not possible for IOMMU mappings to be marked as alt acct so 795 # setting this string is mutually exclusive with setting the IOMMU strings. 796 pte_str = ' (alt acct)' 797 else: 798 pte_str = '' 799 800 if pvep is not None: 801 pve_str = 'PVEP {:#x}, '.format(pvep) 802 else: 803 pve_str = '' 804 805 # For PVH_TYPE_PTEP, this clears out the type bits. For PVH_TYPE_PVEP, this 806 # either does nothing or clears out the AltAcct bit. 807 ptep = ptep & ~0x3 808 809 # When printing with extra verbosity, print an extra newline that describes 810 # who owns the mapping. 811 extra_str = '' 812 813 if ptep & iommu_flag: 814 # The mapping is an IOMMU Mapping 815 ptep = ptep & ~iommu_flag 816 817 # Due to LLDB automatically setting all the high bits of pointers, when 818 # ptep is retrieved from the pve_ptep[] array, LLDB will automatically set 819 # the iommu_table_flag, which means this check only works for PVH entries 820 # of type PVH_TYPE_PTEP (since those PTEPs come directly from the PVH 821 # entry which has the right casting applied to avoid this issue). 822 # 823 # Why don't we just do the same casting for pve_ptep[] you ask? Well not 824 # for a lack of trying, that's for sure. If you can figure out how to 825 # cast that array correctly, then be my guest. 826 if ptep & iommu_table_flag: 827 pte_str = ' (IOMMU table), entry' 828 829 ptd = GetPtDesc(KVToPhysARM(ptep)) 830 iommu = dereference(ptd.iommu) 831 else: 832 # Instead of dumping the PTE (since we don't have that), dump the 833 # descriptor object used by the IOMMU state (t8020dart/nvme_ppl/etc). 834 # 835 # This works because later on when the "ptep" is dereferenced as a 836 # PTE pointer (uint64_t pointer), the descriptor pointer will be 837 # dumped as that's the first 64-bit value in the IOMMU state object. 838 pte_str = ' (IOMMU state), descriptor' 839 ptep = ptep | iommu_table_flag 840 iommu = dereference(kern.GetValueFromAddress(ptep, 'ppl_iommu_state *')) 841 842 # For IOMMU mappings, dump who owns the mapping as the extra string. 843 extra_str = 'Mapped by {:s}'.format(dereference(iommu.desc).name) 844 if unsigned(iommu.name) != 0: 845 extra_str += '/{:s}'.format(iommu.name) 846 extra_str += ' (iommu state: {:x})'.format(addressof(iommu)) 847 else: 848 # The mapping is a CPU Mapping 849 pte_str += ', entry' 850 ptd = GetPtDesc(KVToPhysARM(ptep)) 851 if ptd.pmap == kern.globals.kernel_pmap: 852 extra_str = "Mapped by kernel task (kernel_pmap: {:#x})".format(ptd.pmap) 853 elif verbose_level >= vDETAIL: 854 task = TaskForPmapHelper(ptd.pmap) 855 extra_str = "Mapped by user task (pmap: {:#x}, task: {:s})".format(ptd.pmap, "{:#x}".format(task) if task is not None else "<unknown>") 856 try: 857 print("{:s}PTEP {:#x}{:s}: {:#x}".format(pve_str, ptep, pte_str, dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) 858 except: 859 print("{:s}PTEP {:#x}{:s}: <unavailable>".format(pve_str, ptep, pte_str)) 860 861 if verbose_level >= vDETAIL: 862 print(" |-- {:s}".format(extra_str)) 863 864def PVWalkARM(pai, verbose_level = vHUMAN): 865 """ Walk a physical-to-virtual reverse mapping list maintained by the arm pmap. 866 867 pai: physical address index (PAI) corresponding to the pv_head_table 868 entry to walk. 869 verbose_level: Set to vSCRIPT or higher to print extra info around the 870 the pv_head_table/pp_attr_table flags and to dump the 871 pt_desc_t object if the type is a PTD. 872 """ 873 # LLDB will automatically try to make pointer values dereferencable by 874 # setting the upper bits if they aren't set. We need to parse the flags 875 # stored in the upper bits later, so cast the pv_head_table to an array of 876 # integers to get around this "feature". We'll add the upper bits back 877 # manually before deref'ing anything. 878 pv_head_table = cast(kern.GetGlobalVariable('pv_head_table'), "uintptr_t*") 879 pvh_raw = unsigned(pv_head_table[pai]) 880 pvh = pvh_raw 881 pvh_type = pvh & 0x3 882 883 print("PVH raw value: {:#x}".format(pvh_raw)) 884 if kern.arch.startswith('arm64'): 885 pvh = pvh | PVH_HIGH_FLAGS_ARM64 886 else: 887 pvh = pvh | PVH_HIGH_FLAGS_ARM32 888 889 if pvh_type == 0: 890 print("PVH type: NULL") 891 elif pvh_type == 3: 892 print("PVH type: page-table descriptor ({:#x})".format(pvh & ~0x3)) 893 elif pvh_type == 2: 894 print("PVH type: single PTE") 895 PVDumpPTE(None, pvh, verbose_level) 896 elif pvh_type == 1: 897 pvep = pvh & ~0x3 898 print("PVH type: PTE list") 899 pve_ptep_idx = 0 900 while pvep != 0: 901 pve = kern.GetValueFromAddress(pvep, "pv_entry_t *") 902 903 if pve.pve_ptep[pve_ptep_idx] != 0: 904 PVDumpPTE(pvep, pve.pve_ptep[pve_ptep_idx], verbose_level) 905 906 pve_ptep_idx += 1 907 if pve_ptep_idx == 2: 908 pve_ptep_idx = 0 909 pvep = unsigned(pve.pve_next) 910 911 if verbose_level >= vDETAIL: 912 if (pvh_type == 1) or (pvh_type == 2): 913 # Dump pv_head_table flags when there's a valid mapping. 914 pvh_flags = [] 915 916 if pvh_raw & (1 << 62): 917 pvh_flags.append("CPU") 918 if pvh_raw & (1 << 60): 919 pvh_flags.append("EXEC") 920 if pvh_raw & (1 << 59): 921 pvh_flags.append("LOCKDOWN_KC") 922 if pvh_raw & (1 << 58): 923 pvh_flags.append("HASHED") 924 if pvh_raw & (1 << 57): 925 pvh_flags.append("LOCKDOWN_CS") 926 if pvh_raw & (1 << 56): 927 pvh_flags.append("LOCKDOWN_RO") 928 if kern.arch.startswith('arm64') and pvh_raw & (1 << 61): 929 pvh_flags.append("LOCK") 930 931 print("PVH Flags: {}".format(pvh_flags)) 932 933 # Always dump pp_attr_table flags (these can be updated even if there aren't mappings). 934 ppattr = unsigned(kern.globals.pp_attr_table[pai]) 935 print("PPATTR raw value: {:#x}".format(ppattr)) 936 937 ppattr_flags = ["WIMG ({:#x})".format(ppattr & 0x3F)] 938 if ppattr & 0x40: 939 ppattr_flags.append("REFERENCED") 940 if ppattr & 0x80: 941 ppattr_flags.append("MODIFIED") 942 if ppattr & 0x100: 943 ppattr_flags.append("INTERNAL") 944 if ppattr & 0x200: 945 ppattr_flags.append("REUSABLE") 946 if ppattr & 0x400: 947 ppattr_flags.append("ALTACCT") 948 if ppattr & 0x800: 949 ppattr_flags.append("NOENCRYPT") 950 if ppattr & 0x1000: 951 ppattr_flags.append("REFFAULT") 952 if ppattr & 0x2000: 953 ppattr_flags.append("MODFAULT") 954 if ppattr & 0x4000: 955 ppattr_flags.append("MONITOR") 956 if ppattr & 0x8000: 957 ppattr_flags.append("NO_MONITOR") 958 959 print("PPATTR Flags: {}".format(ppattr_flags)) 960 961 if pvh_type == 3: 962 def RunLldbCmdHelper(command): 963 """Helper for dumping an LLDB command right before executing it 964 and printing the results. 965 command: The LLDB command (as a string) to run. 966 967 Example input: "p/x kernel_pmap". 968 """ 969 print("\nExecuting: {:s}\n{:s}".format(command, lldb_run_command(command))) 970 # Dump the page table descriptor object 971 ptd = kern.GetValueFromAddress(pvh & ~0x3, 'pt_desc_t *') 972 RunLldbCmdHelper("p/x *(pt_desc_t*)" + hex(ptd)) 973 974 # Depending on the system, more than one ptd_info can be associated 975 # with a single PTD. Only dump the first PTD info and assume the 976 # user knows to dump the rest if they're on one of those systems. 977 RunLldbCmdHelper("p/x ((pt_desc_t*)" + hex(ptd) + ")->ptd_info[0]") 978 979@lldb_command('pv_walk') 980def PVWalk(cmd_args=None): 981 """ Show mappings for <physical_address | PAI> tracked in the PV list. 982 Syntax: (lldb) pv_walk <physical_address | PAI> [-vv] 983 984 Extra verbosity will pretty print the pv_head_table/pp_attr_table flags 985 as well as dump the page table descriptor (PTD) struct if the entry is a 986 PTD. 987 """ 988 if cmd_args == None or len(cmd_args) < 1: 989 raise ArgumentError("Too few arguments to pv_walk.") 990 if not kern.arch.startswith('arm'): 991 raise NotImplementedError("pv_walk does not support {0}".format(kern.arch)) 992 993 pa = kern.GetValueFromAddress(cmd_args[0], 'unsigned long') 994 995 # If the input is already a PAI, this function will return the input unchanged. 996 # This function also ensures that the physical address is kernel-managed. 997 pai = ConvertPhysAddrToPai(pa) 998 999 PVWalkARM(pai, config['verbosity']) 1000 1001@lldb_command('kvtophys') 1002def KVToPhys(cmd_args=None): 1003 """ Translate a kernel virtual address to the corresponding physical address. 1004 Assumes the virtual address falls within the kernel static region. 1005 Syntax: (lldb) kvtophys <kernel virtual address> 1006 """ 1007 if cmd_args == None or len(cmd_args) < 1: 1008 raise ArgumentError("Too few arguments to kvtophys.") 1009 if kern.arch.startswith('arm'): 1010 print("{:#x}".format(KVToPhysARM(int(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long')))))) 1011 elif kern.arch == 'x86_64': 1012 print("{:#x}".format(int(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'))) - unsigned(kern.globals.physmap_base))) 1013 1014@lldb_command('phystokv') 1015def PhysToKV(cmd_args=None): 1016 """ Translate a physical address to the corresponding static kernel virtual address. 1017 Assumes the physical address corresponds to managed DRAM. 1018 Syntax: (lldb) phystokv <physical address> 1019 """ 1020 if cmd_args == None or len(cmd_args) < 1: 1021 raise ArgumentError("Too few arguments to phystokv.") 1022 print("{:#x}".format(kern.PhysToKernelVirt(int(unsigned(kern.GetValueFromAddress(cmd_args[0], 'unsigned long')))))) 1023 1024def KVToPhysARM(addr): 1025 try: 1026 ptov_table = kern.globals.ptov_table 1027 for i in range(0, kern.globals.ptov_index): 1028 if (addr >= int(unsigned(ptov_table[i].va))) and (addr < (int(unsigned(ptov_table[i].va)) + int(unsigned(ptov_table[i].len)))): 1029 return (addr - int(unsigned(ptov_table[i].va)) + int(unsigned(ptov_table[i].pa))) 1030 except Exception as exc: 1031 raise ValueError("VA {:#x} not found in physical region lookup table".format(addr)) 1032 return (addr - unsigned(kern.globals.gVirtBase) + unsigned(kern.globals.gPhysBase)) 1033 1034 1035def GetPtDesc(paddr): 1036 pn = (paddr - unsigned(kern.globals.vm_first_phys)) // kern.globals.page_size 1037 pvh = unsigned(kern.globals.pv_head_table[pn]) 1038 if kern.arch.startswith('arm64'): 1039 pvh = pvh | PVH_HIGH_FLAGS_ARM64 1040 else: 1041 pvh = pvh | PVH_HIGH_FLAGS_ARM32 1042 pvh_type = pvh & 0x3 1043 if pvh_type != 0x3: 1044 raise ValueError("PV head {:#x} does not correspond to a page-table descriptor".format(pvh)) 1045 ptd = kern.GetValueFromAddress(pvh & ~0x3, 'pt_desc_t *') 1046 return ptd 1047 1048 1049def ShowPTEARM(pte, page_size, stage2 = False): 1050 """ Display vital information about an ARM page table entry 1051 pte: kernel virtual address of the PTE. Should be L3 PTE. May also work with L2 TTEs for certain devices. 1052 """ 1053 def GetPageTableInfo(ptd, pt_index, paddr): 1054 try: 1055 refcnt = ptd.ptd_info[pt_index].refcnt 1056 if refcnt == 0x4000: 1057 level = 2 1058 else: 1059 level = 3 1060 # PTDs used to describe IOMMU pages always have a refcnt of 0x8000/0x8001. 1061 is_iommu_pte = (refcnt & 0x8000) == 0x8000 1062 if is_iommu_pte: 1063 iommu_desc_name = '{:s}'.format(dereference(dereference(ptd.iommu).desc).name) 1064 if unsigned(dereference(ptd.iommu).name) != 0: 1065 iommu_desc_name += '/{:s}'.format(dereference(ptd.iommu).name) 1066 info_str = "iommu state: {:#x} ({:s})".format(ptd.iommu, iommu_desc_name) 1067 else: 1068 info_str = None 1069 return (int(unsigned(refcnt)), level, info_str) 1070 except Exception as exc: 1071 raise ValueError("Unable to retrieve PTD refcnt") 1072 pte_paddr = KVToPhysARM(pte) 1073 ptd = GetPtDesc(pte_paddr) 1074 pt_index = (pte % kern.globals.page_size) // page_size 1075 refcnt, level, info_str = GetPageTableInfo(ptd, pt_index, pte_paddr) 1076 wiredcnt = ptd.ptd_info[pt_index].wiredcnt 1077 va = ptd.va[pt_index] 1078 print("descriptor: {:#x} (refcnt: {:#x}, wiredcnt: {:#x}, va: {:#x})".format(ptd, refcnt, wiredcnt, va)) 1079 1080 # The pmap/iommu field is a union, so only print the correct one. 1081 if info_str is not None: 1082 print(info_str) 1083 else: 1084 if ptd.pmap == kern.globals.kernel_pmap: 1085 pmap_str = "(kernel_pmap)" 1086 else: 1087 task = TaskForPmapHelper(ptd.pmap) 1088 pmap_str = "(User Task: {:s})".format("{:#x}".format(task) if task is not None else "<unknown>") 1089 print("pmap: {:#x} {:s}".format(ptd.pmap, pmap_str)) 1090 nttes = page_size // 8 1091 granule = page_size * (nttes ** (4 - level)) 1092 pte_pgoff = pte % page_size 1093 pte_pgoff = pte_pgoff // 8 1094 print("maps {}: {:#x}".format("IPA" if stage2 else "VA", int(unsigned(ptd.va[pt_index])) + (pte_pgoff * granule))) 1095 pteval = int(unsigned(dereference(kern.GetValueFromAddress(unsigned(pte), 'pt_entry_t *')))) 1096 print("value: {:#x}".format(pteval)) 1097 print("level: {:d}".format(level)) 1098 PmapDecodeTTEARM64(pteval, level, stage2) 1099 1100@lldb_command('showpte') 1101def ShowPTE(cmd_args=None): 1102 """ Display vital information about the page table entry at VA <pte> 1103 Syntax: (lldb) showpte <pte_va> [4k|16k|16k_s2] 1104 """ 1105 if cmd_args == None or len(cmd_args) < 1: 1106 raise ArgumentError("Too few arguments to showpte.") 1107 1108 if kern.arch.startswith('arm64'): 1109 pmap_pt_attr = kern.globals.native_pt_attr if len(cmd_args) < 2 else GetMemoryAttributesFromUser(cmd_args[1]) 1110 if pmap_pt_attr is None: 1111 raise ArgumentError("Invalid translation attribute type.") 1112 1113 stage2 = bool(pmap_pt_attr.stage2 if hasattr(pmap_pt_attr, 'stage2') else False) 1114 ShowPTEARM(kern.GetValueFromAddress(cmd_args[0], 'unsigned long'), pmap_pt_attr.pta_page_size, stage2) 1115 else: 1116 raise NotImplementedError("showpte does not support {0}".format(kern.arch)) 1117 1118def FindMappingAtLevelARM64(pmap, tt, nttes, level, va, action): 1119 """ Perform the specified action for all valid mappings in an ARM64 translation table 1120 pmap: owner of the translation table 1121 tt: translation table or page table 1122 nttes: number of entries in tt 1123 level: translation table level, 1 2 or 3 1124 action: callback for each valid TTE 1125 """ 1126 # Obtain pmap attributes 1127 pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr 1128 page_size = pmap_pt_attr.pta_page_size 1129 page_offset_mask = (page_size - 1) 1130 page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask) 1131 max_level = unsigned(pmap_pt_attr.pta_max_level) 1132 1133 for i in range(nttes): 1134 try: 1135 tte = tt[i] 1136 if tte & 0x1 == 0x0: 1137 continue 1138 1139 tt_next = None 1140 paddr = unsigned(tte) & unsigned(page_base_mask) 1141 1142 # Handle leaf entry 1143 if tte & 0x2 == 0x0 or level == max_level: 1144 type = 'block' if level < max_level else 'entry' 1145 granule = PmapBlockOffsetMaskARM64(page_size, level) + 1 1146 else: 1147 # Handle page table entry 1148 type = 'table' 1149 granule = page_size 1150 tt_next = kern.GetValueFromAddress(kern.PhysToKernelVirt(paddr), 'tt_entry_t *') 1151 1152 mapped_va = int(unsigned(va)) + ((PmapBlockOffsetMaskARM64(page_size, level) + 1) * i) 1153 if action(pmap, level, type, addressof(tt[i]), paddr, mapped_va, granule): 1154 if tt_next is not None: 1155 FindMappingAtLevelARM64(pmap, tt_next, granule // ARM64_TTE_SIZE, level + 1, mapped_va, action) 1156 1157 except Exception as exc: 1158 print("Unable to access tte {:#x}".format(unsigned(addressof(tt[i])))) 1159 1160def ScanPageTables(action, targetPmap=None): 1161 """ Perform the specified action for all valid mappings in all page tables, 1162 optionally restricted to a single pmap. 1163 pmap: pmap whose page table should be scanned. If None, all pmaps on system will be scanned. 1164 """ 1165 print("Scanning all available translation tables. This may take a long time...") 1166 def ScanPmap(pmap, action): 1167 if kern.arch.startswith('arm64'): 1168 # Obtain pmap attributes 1169 pmap_pt_attr = pmap.pmap_pt_attr if hasattr(pmap, 'pmap_pt_attr') else kern.globals.native_pt_attr 1170 granule = pmap_pt_attr.pta_page_size 1171 level = unsigned(pmap_pt_attr.pta_root_level) 1172 root_pgtable_num_ttes = (unsigned(pmap_pt_attr.pta_level_info[level].index_mask) >> \ 1173 unsigned(pmap_pt_attr.pta_level_info[level].shift)) + 1 1174 1175 if action(pmap, pmap_pt_attr.pta_root_level, 'root', pmap.tte, unsigned(pmap.ttep), pmap.min, granule): 1176 if kern.arch.startswith('arm64'): 1177 FindMappingAtLevelARM64(pmap, pmap.tte, root_pgtable_num_ttes, level, pmap.min, action) 1178 1179 if targetPmap is not None: 1180 ScanPmap(kern.GetValueFromAddress(targetPmap, 'pmap_t'), action) 1181 else: 1182 for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'): 1183 ScanPmap(pmap, action) 1184 1185@lldb_command('showallmappings') 1186def ShowAllMappings(cmd_args=None): 1187 """ Find and display all available mappings on the system for 1188 <physical_address>. Optionally only searches the pmap 1189 specified by [<pmap>] 1190 Syntax: (lldb) showallmappings <physical_address> [<pmap>] 1191 WARNING: this macro can take a long time (up to 30min.) to complete! 1192 """ 1193 if cmd_args == None or len(cmd_args) < 1: 1194 raise ArgumentError("Too few arguments to showallmappings.") 1195 if not kern.arch.startswith('arm'): 1196 raise NotImplementedError("showallmappings does not support {0}".format(kern.arch)) 1197 pa = kern.GetValueFromAddress(cmd_args[0], 'unsigned long') 1198 targetPmap = None 1199 if len(cmd_args) > 1: 1200 targetPmap = cmd_args[1] 1201 def printMatchedMapping(pmap, level, type, tte, paddr, va, granule): 1202 if paddr <= pa < (paddr + granule): 1203 print("pmap: {:#x}: L{:d} {:s} at {:#x}: [{:#x}, {:#x}), maps va {:#x}".format(pmap, level, type, unsigned(tte), paddr, paddr + granule, va)) 1204 return True 1205 ScanPageTables(printMatchedMapping, targetPmap) 1206 1207@lldb_command('showptusage') 1208def ShowPTUsage(cmd_args=None): 1209 """ Display a summary of pagetable allocations for a given pmap. 1210 Syntax: (lldb) showptusage [<pmap>] 1211 WARNING: this macro can take a long time (> 1hr) to complete! 1212 """ 1213 if not kern.arch.startswith('arm'): 1214 raise NotImplementedError("showptusage does not support {0}".format(kern.arch)) 1215 targetPmap = None 1216 if len(cmd_args) > 0: 1217 targetPmap = cmd_args[0] 1218 lastPmap = [None] 1219 numTables = [0] 1220 numUnnested = [0] 1221 numPmaps = [0] 1222 def printValidTTE(pmap, level, type, tte, paddr, va, granule): 1223 unnested = "" 1224 nested_region_addr = int(unsigned(pmap.nested_region_addr)) 1225 nested_region_end = nested_region_addr + int(unsigned(pmap.nested_region_size)) 1226 if lastPmap[0] is None or (pmap != lastPmap[0]): 1227 lastPmap[0] = pmap 1228 numPmaps[0] = numPmaps[0] + 1 1229 print ("pmap {:#x}:".format(pmap)) 1230 if type == 'root': 1231 return True 1232 if (level == 2) and (va >= nested_region_addr) and (va < nested_region_end): 1233 ptd = GetPtDesc(paddr) 1234 if ptd.pmap != pmap: 1235 return False 1236 else: 1237 numUnnested[0] = numUnnested[0] + 1 1238 unnested = " (likely unnested)" 1239 numTables[0] = numTables[0] + 1 1240 print((" " * 4 * int(level)) + "L{:d} entry at {:#x}, maps {:#x}".format(level, unsigned(tte), va) + unnested) 1241 if level == 2: 1242 return False 1243 else: 1244 return True 1245 ScanPageTables(printValidTTE, targetPmap) 1246 print("{:d} table(s), {:d} of them likely unnested, in {:d} pmap(s)".format(numTables[0], numUnnested[0], numPmaps[0])) 1247 1248def checkPVList(pmap, level, type, tte, paddr, va, granule): 1249 """ Checks an ARM physical-to-virtual mapping list for consistency errors. 1250 pmap: owner of the translation table 1251 level: translation table level. PV lists will only be checked for L2 (arm32) or L3 (arm64) tables. 1252 type: unused 1253 tte: KVA of PTE to check for presence in PV list. If None, presence check will be skipped. 1254 paddr: physical address whose PV list should be checked. Need not be page-aligned. 1255 granule: unused 1256 """ 1257 vm_first_phys = unsigned(kern.globals.vm_first_phys) 1258 vm_last_phys = unsigned(kern.globals.vm_last_phys) 1259 page_size = kern.globals.page_size 1260 if kern.arch.startswith('arm64'): 1261 page_offset_mask = (page_size - 1) 1262 page_base_mask = ((1 << ARM64_VMADDR_BITS) - 1) & (~page_offset_mask) 1263 paddr = paddr & page_base_mask 1264 max_level = 3 1265 pvh_set_bits = PVH_HIGH_FLAGS_ARM64 1266 if level < max_level or paddr < vm_first_phys or paddr >= vm_last_phys: 1267 return True 1268 pn = (paddr - vm_first_phys) // page_size 1269 pvh = unsigned(kern.globals.pv_head_table[pn]) | pvh_set_bits 1270 pvh_type = pvh & 0x3 1271 if pmap is not None: 1272 pmap_str = "pmap: {:#x}: ".format(pmap) 1273 else: 1274 pmap_str = '' 1275 if tte is not None: 1276 tte_str = "pte {:#x} ({:#x}): ".format(unsigned(tte), paddr) 1277 else: 1278 tte_str = "paddr {:#x}: ".format(paddr) 1279 if pvh_type == 0 or pvh_type == 3: 1280 print("{:s}{:s}unexpected PVH type {:d}".format(pmap_str, tte_str, pvh_type)) 1281 elif pvh_type == 2: 1282 ptep = pvh & ~0x3 1283 if tte is not None and ptep != unsigned(tte): 1284 print("{:s}{:s}PVH mismatch ({:#x})".format(pmap_str, tte_str, ptep)) 1285 try: 1286 pte = int(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask 1287 if (pte != paddr): 1288 print("{:s}{:s}PVH {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte)) 1289 except Exception as exc: 1290 print("{:s}{:s}Unable to read PVH {:#x}".format(pmap_str, tte_str, ptep)) 1291 elif pvh_type == 1: 1292 pvep = pvh & ~0x3 1293 tte_match = False 1294 pve_ptep_idx = 0 1295 while pvep != 0: 1296 pve = kern.GetValueFromAddress(pvep, "pv_entry_t *") 1297 ptep = unsigned(pve.pve_ptep[pve_ptep_idx]) & ~0x3 1298 pve_ptep_idx += 1 1299 if pve_ptep_idx == 2: 1300 pve_ptep_idx = 0 1301 pvep = unsigned(pve.pve_next) 1302 if ptep == 0: 1303 continue 1304 if tte is not None and ptep == unsigned(tte): 1305 tte_match = True 1306 try: 1307 pte = int(unsigned(dereference(kern.GetValueFromAddress(ptep, 'pt_entry_t *')))) & page_base_mask 1308 if (pte != paddr): 1309 print("{:s}{:s}PVE {:#x} maps wrong page ({:#x}) ".format(pmap_str, tte_str, ptep, pte)) 1310 except Exception as exc: 1311 print("{:s}{:s}Unable to read PVE {:#x}".format(pmap_str, tte_str, ptep)) 1312 if tte is not None and not tte_match: 1313 print("{:s}{:s}{:s}not found in PV list".format(pmap_str, tte_str, paddr)) 1314 return True 1315 1316@lldb_command('pv_check', 'P') 1317def PVCheck(cmd_args=None, cmd_options={}): 1318 """ Check the physical-to-virtual mapping for a given PTE or physical address 1319 Syntax: (lldb) pv_check <addr> [-p] 1320 -P : Interpret <addr> as a physical address rather than a PTE 1321 """ 1322 if cmd_args == None or len(cmd_args) < 1: 1323 raise ArgumentError("Too few arguments to pv_check.") 1324 if kern.arch.startswith('arm64'): 1325 level = 3 1326 else: 1327 raise NotImplementedError("pv_check does not support {0}".format(kern.arch)) 1328 if "-P" in cmd_options: 1329 pte = None 1330 pa = int(unsigned(kern.GetValueFromAddress(cmd_args[0], "unsigned long"))) 1331 else: 1332 pte = kern.GetValueFromAddress(cmd_args[0], 'pt_entry_t *') 1333 pa = int(unsigned(dereference(pte))) 1334 checkPVList(None, level, None, pte, pa, 0, None) 1335 1336@lldb_command('check_pmaps') 1337def CheckPmapIntegrity(cmd_args=None): 1338 """ Performs a system-wide integrity check of all PTEs and associated PV lists. 1339 Optionally only checks the pmap specified by [<pmap>] 1340 Syntax: (lldb) check_pmaps [<pmap>] 1341 WARNING: this macro can take a HUGE amount of time (several hours) if you do not 1342 specify [pmap] to limit it to a single pmap. It will also give false positives 1343 for kernel_pmap, as we do not create PV entries for static kernel mappings on ARM. 1344 Use of this macro without the [<pmap>] argument is heavily discouraged. 1345 """ 1346 if not kern.arch.startswith('arm'): 1347 raise NotImplementedError("check_pmaps does not support {0}".format(kern.arch)) 1348 targetPmap = None 1349 if len(cmd_args) > 0: 1350 targetPmap = cmd_args[0] 1351 ScanPageTables(checkPVList, targetPmap) 1352 1353@lldb_command('pmapsforledger') 1354def PmapsForLedger(cmd_args=None): 1355 """ Find and display all pmaps currently using <ledger>. 1356 Syntax: (lldb) pmapsforledger <ledger> 1357 """ 1358 if cmd_args == None or len(cmd_args) < 1: 1359 raise ArgumentError("Too few arguments to pmapsforledger.") 1360 if not kern.arch.startswith('arm'): 1361 raise NotImplementedError("pmapsforledger does not support {0}".format(kern.arch)) 1362 ledger = kern.GetValueFromAddress(cmd_args[0], 'ledger_t') 1363 for pmap in IterateQueue(kern.globals.map_pmap_list, 'pmap_t', 'pmaps'): 1364 if pmap.ledger == ledger: 1365 print("pmap: {:#x}".format(pmap)) 1366 1367 1368def IsValidPai(pai): 1369 """ Given an unsigned value, detect whether that value is a valid physical 1370 address index (PAI). It does this by first computing the last possible 1371 PAI and comparing the input to that. 1372 1373 All contemporary SoCs reserve the bottom part of the address space, so 1374 there shouldn't be any valid physical addresses between zero and the 1375 last PAI either. 1376 """ 1377 page_size = unsigned(kern.globals.page_size) 1378 vm_first_phys = unsigned(kern.globals.vm_first_phys) 1379 vm_last_phys = unsigned(kern.globals.vm_last_phys) 1380 1381 last_pai = (vm_last_phys - vm_first_phys) // page_size 1382 if (pai < 0) or (pai >= last_pai): 1383 return False 1384 1385 return True 1386 1387def ConvertPaiToPhysAddr(pai): 1388 """ Convert the given Physical Address Index (PAI) into a physical address. 1389 1390 If the input isn't a valid PAI (it's most likely already a physical 1391 address), then just return back the input unchanged. 1392 """ 1393 pa = pai 1394 1395 # If the value is a valid PAI, then convert it into a physical address. 1396 if IsValidPai(pai): 1397 pa = (pai * unsigned(kern.globals.page_size)) + unsigned(kern.globals.vm_first_phys) 1398 1399 return pa 1400 1401def ConvertPhysAddrToPai(pa): 1402 """ Convert the given physical address into a Physical Address Index (PAI). 1403 1404 If the input is already a valid PAI, then just return back the input 1405 unchanged. 1406 """ 1407 vm_first_phys = unsigned(kern.globals.vm_first_phys) 1408 vm_last_phys = unsigned(kern.globals.vm_last_phys) 1409 pai = pa 1410 1411 if not IsValidPai(pa) and (pa < vm_first_phys or pa >= vm_last_phys): 1412 raise ArgumentError("{:#x} is neither a valid PAI nor a kernel-managed address: [{:#x}, {:#x})".format(pa, vm_first_phys, vm_last_phys)) 1413 elif not IsValidPai(pa): 1414 # If the value isn't already a valid PAI, then convert it into one. 1415 pai = (pa - vm_first_phys) // unsigned(kern.globals.page_size) 1416 1417 return pai 1418 1419@lldb_command('pmappaindex') 1420def PmapPaIndex(cmd_args=None): 1421 """ Display both a physical address and physical address index (PAI) when 1422 provided with only one of those values. 1423 1424 Syntax: (lldb) pmappaindex <physical address | PAI> 1425 1426 NOTE: This macro will throw an exception if the input isn't a valid PAI 1427 and is also not a kernel-managed physical address. 1428 """ 1429 if (cmd_args == None) or (len(cmd_args) < 1): 1430 raise ArgumentError("Too few arguments to pmappaindex.") 1431 1432 if not kern.arch.startswith('arm'): 1433 raise NotImplementedError("pmappaindex is only supported on ARM devices.") 1434 1435 value = kern.GetValueFromAddress(cmd_args[0], 'unsigned long') 1436 pai = value 1437 phys_addr = value 1438 1439 if IsValidPai(value): 1440 # Input is a PAI, calculate the physical address. 1441 phys_addr = ConvertPaiToPhysAddr(value) 1442 else: 1443 # Input is a physical address, calculate the PAI 1444 pai = ConvertPhysAddrToPai(value) 1445 1446 print("Physical Address: {:#x}".format(phys_addr)) 1447 print("PAI: {:d}".format(pai)) 1448