1from xnu import *
2
3######################################
4# Helper functions
5######################################
6def GetMemMappedPciCfgAddrFromRegistry():
7    """ Retrieve the base address of the memory mapped PCI config space. It is
8        found in registry entry AppleACPIPlatformExpert, property acpi-mmcfg-seg0.
9        Returns:
10            int base address of memory mapped PCI config space
11    """
12    kgm_pci_cfg_base_default = 0xe0000000
13    acpi_pe_obj = FindRegistryObjectRecurse(kern.globals.gRegistryRoot,
14        "AppleACPIPlatformExpert")
15    if acpi_pe_obj is None:
16        print "Could not find AppleACPIPlatformExpert in registry, \
17        using default base address for memory mapped PCI config space"
18        return kgm_pci_cfg_base_default
19    entry = kern.GetValueFromAddress(int(acpi_pe_obj), 'IOService *')
20    acpi_mmcfg_seg_prop = LookupKeyInPropTable(entry.fPropertyTable, "acpi-mmcfg-seg0")
21    if acpi_mmcfg_seg_prop is None:
22        print "Could not find acpi-mmcfg-seg0 property, \
23        using default base address for memory mapped PCI config space"
24        return kgm_pci_cfg_base_default
25    else:
26        return int(GetNumber(acpi_mmcfg_seg_prop))
27
28@static_var('kgm_pci_cfg_base', -1)
29def GetMemMappedPciCfgAddrBase():
30    """ Returns the base address of the memory mapped PCI config space. The address
31        is retrieved once from the registry, and is remembered for all subsequent
32        calls to this function
33        Returns:
34            int base address of memory mapped PCI config space
35    """
36    if GetMemMappedPciCfgAddrBase.kgm_pci_cfg_base == -1:
37        # Retrieve the base address from the registry if it hasn't been
38        # initialized yet
39        GetMemMappedPciCfgAddrBase.kgm_pci_cfg_base = GetMemMappedPciCfgAddrFromRegistry()
40    return GetMemMappedPciCfgAddrBase.kgm_pci_cfg_base
41
42def MakeMemMappedPciCfgAddr(bus, dev, func, offs):
43    """ Construct the memory address for the PCI config register specified by the
44        bus, device, function, and offset
45        Params:
46            bus, dev, func, offs: int - bus, device, function, and offset that specifies
47            the PCI config space register
48        Returns:
49            int - the physical memory address that maps to the PCI config space register
50    """
51    return GetMemMappedPciCfgAddrBase() | (bus << 20) | (dev << 15) | (func << 12) | offs
52
53def DoPciCfgRead(bits, bus, dev, func, offs):
54    """ Helper function that performs PCI config space read
55        Params:
56            bits: int - bit width of access: 8, 16, or 32 bits
57            bus, dev, func, offs: int - PCI config bus, device, function and offset
58        Returns:
59            int - the value read from PCI config space
60    """
61    phys_addr = MakeMemMappedPciCfgAddr(bus, dev, func, offs)
62    return ReadPhysInt(phys_addr, bits)
63
64def DoPciCfgWrite(bits, bus, dev, func, offs, val):
65    """ Helper function that performs PCI config space write
66        Params:
67            bits: int - bit width of access: 8, 16, or 32 bits
68            bus, dev, func, offs: int - PCI config bus, device, function and offset
69        Returns:
70            boolean - True upon success, False otherwise
71    """
72    phys_addr = MakeMemMappedPciCfgAddr(bus, dev, func, offs)
73    return WritePhysInt(phys_addr, val, bits)
74
75def ShowPciCfgBytes(bus, dev, func, offset):
76    """ Prints 16 bytes of PCI config space starting at specified offset
77        Params:
78            bus, dev, func, offset: int - bus, dev, function, and offset of the
79            PCI config space register
80    """
81    # Print mem-mapped address at beginning of each 16-byte line
82    phys_addr = MakeMemMappedPciCfgAddr(bus, dev, func, offset)
83    read_vals = [DoPciCfgRead(32, bus, dev, func, offset + byte)
84                    for byte in range(0, 16, 4)]
85    # It would be nicer to have a shorter format that we could loop
86    # over, but each call to print results in a newline which
87    # would prevent us from printing all 16 bytes on one line.
88    bytes_fmt = "{:08x}:" + "{:02x} " * 16
89    print bytes_fmt.format(
90        phys_addr,
91        read_vals[0] & 0xff, (read_vals[0] >> 8) & 0xff,
92        (read_vals[0] >> 16) & 0xff, (read_vals[0] >> 24) & 0xff,
93        read_vals[1] & 0xff, (read_vals[1] >> 8) & 0xff,
94        (read_vals[1] >> 16) & 0xff, (read_vals[1] >> 24) & 0xff,
95        read_vals[2] & 0xff, (read_vals[2] >> 8) & 0xff,
96        (read_vals[2] >> 16) & 0xff, (read_vals[2] >> 24) & 0xff,
97        read_vals[3] & 0xff, (read_vals[3] >> 8) & 0xff,
98        (read_vals[3] >> 16) & 0xff, (read_vals[3] >> 24) & 0xff)
99
100def DoPciCfgDump(bus, dev, func):
101    """ Dumps PCI config space of the PCI device specified by bus, dev, function
102        Params:
103            bus, dev, func: int - bus, dev, function of PCI config space to dump
104    """
105    # Check for a valid PCI device
106    vendor_id = DoPciCfgRead(16, bus, dev, func, 0)
107    if (vendor_id == 0xbad10ad) or not (vendor_id > 0 and vendor_id < 0xffff):
108        return
109    # Show the standard PCI config space
110    print "address: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"
111    print "--------------------------------------------------------"
112    for offset in range(0, 256, 16):
113        ShowPciCfgBytes(bus, dev, func, offset)
114    # Check for PCIE extended capability config space
115    if DoPciCfgRead(8, bus, dev, func, 256) < 0xff:
116        print " \n"
117        for offset in range(256, 4096, 16):
118            ShowPciCfgBytes(bus, dev, func, offset)
119
120def DoPciCfgScan(max_bus, dump):
121    """ Do a PCI config scan starting at bus 0 up to specified max bus
122        Params:
123            max_bus: int - maximum bus to scan
124            dump: bool - if True, dump the config space of each scanned device
125                         if False, print basic information of each scanned device
126    """
127    max_dev  = 32
128    max_func = 8
129    bdfs = ({'bus':bus, 'dev':dev, 'func':func}
130            for bus in range(max_bus)
131            for dev in range(max_dev)
132            for func in range(max_func))
133    fmt_string = "{:03x}:" * 3 + " " + \
134        "{:02x}" * 2 + "   " + \
135        "{:02x}" * 2 + "    {:02x} | " + \
136        "{:02x}" * 3
137    for bdf in bdfs:
138        bus = bdf['bus']
139        dev = bdf['dev']
140        func = bdf['func']
141        vend_dev_id = DoPciCfgRead(32, bus, dev, func, 0)
142        if not (vend_dev_id > 0 and vend_dev_id < 0xffffffff):
143            continue
144        if dump == False:
145            class_rev_id = DoPciCfgRead(32, bus, dev, func, 8)
146            print fmt_string.format(
147                bus, dev, func,
148                (vend_dev_id >> 8) & 0xff, vend_dev_id & 0xff,
149                (vend_dev_id >> 24) & 0xff, (vend_dev_id >> 16) & 0xff,
150                class_rev_id & 0xff, (class_rev_id >> 24) & 0xff,
151                (class_rev_id >> 16) & 0xff, (class_rev_id >> 8) & 0xff)
152        else:
153            print "{:03x}:{:03x}:{:03x}".format(bus, dev, func)
154            DoPciCfgDump(bus, dev, func)
155
156######################################
157# LLDB commands
158######################################
159@lldb_command('pci_cfg_read')
160def PciCfgRead(cmd_args=None):
161    """ Read PCI config space at the specified bus, device, function, and offset
162        Syntax: pci_cfg_read <bits> <bus> <device> <function> <offset>
163            bits: 8, 16, 32
164    """
165    if cmd_args == None or len(cmd_args) < 5:
166        print PciCfgRead.__doc__
167        return
168
169    bits = ArgumentStringToInt(cmd_args[0])
170    bus  = ArgumentStringToInt(cmd_args[1])
171    dev  = ArgumentStringToInt(cmd_args[2])
172    func = ArgumentStringToInt(cmd_args[3])
173    offs = ArgumentStringToInt(cmd_args[4])
174
175    read_val = DoPciCfgRead(bits, bus, dev, func, offs)
176    if read_val == 0xbad10ad:
177        print "ERROR: Failed to read PCI config space"
178        return
179
180    format_for_bits = {8:"{:#04x}", 16:"{:#06x}", 32:"{:#010x}"}
181    phys_addr = MakeMemMappedPciCfgAddr(bus, dev, func, offs)
182    fmt_string = "{:08x}: " + format_for_bits[bits]
183    print fmt_string.format(phys_addr, read_val)
184
185lldb_alias('pci_cfg_read8', 'pci_cfg_read 8')
186lldb_alias('pci_cfg_read16', 'pci_cfg_read 16')
187lldb_alias('pci_cfg_read32', 'pci_cfg_read 32')
188
189@lldb_command('pci_cfg_write')
190def PciCfgWrite(cmd_args=None):
191    """ Write PCI config space at the specified bus, device, function, and offset
192        Syntax: pci_cfg_write <bits> <bus> <device> <function> <offset> <write val>
193            bits: 8, 16, 32
194
195        Prints an error message if there was a problem
196        Prints nothing upon success.
197    """
198    if cmd_args == None or len(cmd_args) < 6:
199        print PciCfgWrite.__doc__
200        return
201
202    bits = ArgumentStringToInt(cmd_args[0])
203    bus  = ArgumentStringToInt(cmd_args[1])
204    dev  = ArgumentStringToInt(cmd_args[2])
205    func = ArgumentStringToInt(cmd_args[3])
206    offs = ArgumentStringToInt(cmd_args[4])
207    write_val = ArgumentStringToInt(cmd_args[5])
208
209    if DoPciCfgWrite(bits, bus, dev, func, offs, write_val) == False:
210        print "ERROR: Failed to write PCI config space"
211
212lldb_alias('pci_cfg_write8', 'pci_cfg_write 8')
213lldb_alias('pci_cfg_write16', 'pci_cfg_write 16')
214lldb_alias('pci_cfg_write32', 'pci_cfg_write 32')
215
216@lldb_command('pci_cfg_dump')
217def PciCfgDump(cmd_args=None):
218    """ Dump PCI config space for specified bus, device, and function
219        If an invalid/inaccessible PCI device is specified, nothing will
220        be printed out.
221        Syntax: pci_cfg_dump <bus> <dev> <fuction>
222    """
223    if cmd_args == None or len(cmd_args) < 3:
224        print PciCfgDump.__doc__
225        return
226
227    bus  = ArgumentStringToInt(cmd_args[0])
228    dev  = ArgumentStringToInt(cmd_args[1])
229    func = ArgumentStringToInt(cmd_args[2])
230
231    DoPciCfgDump(bus, dev, func)
232
233@lldb_command('pci_cfg_scan')
234def PciCfgScan(cmd_args=None):
235    """ Scan for pci devices. The maximum bus number to be scanned defaults to 8,
236        but can be specified as an argument
237        Syntax: pci_cfg_scan [max bus number]
238    """
239    if cmd_args == None or len(cmd_args) == 0:
240        max_bus = 8
241    elif len(cmd_args) == 1:
242        max_bus = ArgumentStringToInt(cmd_args[0])
243    else:
244        print PciCfgScan.__doc__
245        return
246
247    print "bus:dev:fcn: vendor device rev | class"
248    print "--------------------------------------"
249    DoPciCfgScan(max_bus, False)
250
251@lldb_command('pci_cfg_dump_all')
252def PciCfgDumpAll(cmd_args=None):
253    """ Dump config space for all scanned PCI devices. The maximum bus number to
254        be scanned defaults to 8, but can be specified as an argument
255        Syntax: pci_cfg_dump_all [max bus number]
256    """
257    if cmd_args == None or len(cmd_args) == 0:
258        max_bus = 8
259    elif len(cmd_args) == 1:
260        max_bus = ArgumentStringToInt(cmd_args[0])
261    else:
262        print PciCfgDumpAll.__doc__
263        return
264
265    DoPciCfgScan(max_bus, True)
266