Add second TSC speed regular expression
[muen/mugenhwcfg.git] / src / creator.py
1 #   Copyright (C) 2015 Chen Chin Jieh <chinjieh@gmail.com>
2 #   Copyright (C) 2015 Reto Buerki <reet@codelabs.ch>
3 #   Copyright (C) 2015 Adrian-Ken Rueegsegger <ken@codelabs.ch>
4 #
5 #   This file is part of mugenhwcfg.
6 #
7 #   mugenhwcfg is free software: you can redistribute it and/or modify
8 #   it under the terms of the GNU General Public License as published by
9 #   the Free Software Foundation, either version 3 of the License, or
10 #   (at your option) any later version.
11 #
12 #   mugenhwcfg is distributed in the hope that it will be useful,
13 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #   GNU General Public License for more details.
16 #
17 #   You should have received a copy of the GNU General Public License
18 #   along with mugenhwcfg.  If not, see <http://www.gnu.org/licenses/>.
19
20
21 # Module that handles creation of the XML document
22 import dmar
23 import fadt
24 import parseutil
25 import customExceptions
26 import util
27 import paths
28 import os
29 import re
30 import message
31 import devicecap
32 import extractor
33 import subprocess
34 import shutil
35 import mmap
36 import struct
37 from collections import namedtuple, OrderedDict, deque
38 import schemadata
39
40 Address = namedtuple("Address", "start end")
41 SerialDevice = namedtuple("SerialDevice", "name start end irq")
42 PciDevice = namedtuple(
43     "PciDevice", "name classcode vendorId deviceId revisionId descr")
44
45 PAGE_SIZE = "0x1000"  # 4KB
46 PAGE_MIN_SIZE = PAGE_SIZE
47 MEM_ALLOCATABLE_MINSIZE = 0x100000  # 1 MB
48 PROCESSOR_SPEED_KEYWORDS = ["GHz", "MHz"]
49 RESOURCE_START_IDX = 1
50 WELL_KNOWN_IOPORTS = {
51     Address("3f8", "3ff"): "com_1",
52     Address("2f8", "2ff"): "com_2",
53     Address("3e8", "3ef"): "com_3",
54     Address("2e8", "2ef"): "com_4"
55 }
56
57
58 class ProcessorCreator():
59
60     def createElem(self, cpuinfopath, msrpaths, dmesgpath):
61         print "> Creating element: processor"
62
63         VMX_OFFSET = 0x485
64         VMX_BITSIZE = 5
65         rate = self.getVmxTimerRate(msrpaths, VMX_OFFSET, VMX_BITSIZE)
66
67         processor = schemadata.schema.processorType(
68             cpuCores=self.get_cpu_cores(cpuinfopath),
69             speed=round(self.getSpeed(dmesgpath)),
70             vmxTimerRate=rate)
71
72         print "Element created: processor"
73         return processor
74
75     def get_cpu_cores(self, cpuinfopath):
76         return int(parseutil.parseData_Sep(
77             extractor.extractData(cpuinfopath), "cpu cores", ":"))
78
79     def getSpeed(self, dmesgpath):
80         "Gets speed value from dmesg"
81         regs = [re.compile(r'.*Refined TSC clocksource calibration: '
82                            '(?P<speed>.*)'),
83                 re.compile(r'.*tsc: Detected (?P<speed>.*) processor')]
84
85         result = 0
86         try:
87             data = extractor.extractData(dmesgpath)
88         except IOError:
89             errstr = ("Could not read file: %s\n" % dmesgpath +
90                       "Processor speed not found.")
91             message.addError(errstr)
92
93         for regex in regs:
94             match = regex.search(data)
95             if match is not None:
96                 result = util.getSpeedValue(match.group("speed"),
97                                             PROCESSOR_SPEED_KEYWORDS)
98                 break
99
100         if not result:
101             message.addError("Could not find TSC speed in: %s\n" % dmesgpath)
102
103         return result
104
105     def getVmxTimerRate(self, msrpaths, offset, vmxbitsize):
106         # check for MSR
107         vmxTimerRate = 0
108
109         MSRfound = False
110         for path in msrpaths:
111             try:
112                 # Try to find MSR file
113                 vmxTimerRate = self.getVmxFromMSR(path, offset, vmxbitsize)
114             except IOError:
115                 pass
116             else:
117                 MSRfound = True
118                 break
119         if not MSRfound:
120             # MSRFileNotFound
121             errormsg = "VMX timer rate could not be extracted from:\n"
122             for path in msrpaths:
123                 errormsg += ("%s\n" % path)
124
125             errormsg += ("Try 'modprobe msr' to load kernel module and try "
126                          "again.")
127             message.addError(errormsg)
128
129         return vmxTimerRate
130
131     def getVmxFromMSR(self, msrpath, offset, vmxbitsize):
132         "Gets VmxTimerRate value from a given msr path"
133         # Try to find MSR file
134         byte = extractor.extractBinaryData(msrpath, offset, 1)
135         # Raises IOError here if not found
136         vmxbits = 0
137         # Get bits from VMX_BITS_START to VMX_BITS_END
138         for bitnum in range(0, vmxbitsize):
139             vmxbits += util.getBit(int(byte, 16), bitnum) << bitnum
140         vmxTimerRate = int(vmxbits)
141         return vmxTimerRate
142
143
144 class MemoryCreator():
145
146     def createElem(self, memmappath):
147         print "> Creating element: memory"
148
149         memory = schemadata.schema.physicalMemoryType()
150         # Get list of memoryBlocks available
151         memoryBlockList = self.getMemoryBlocks(memmappath)
152         for memoryBlock in memoryBlockList:
153             memory.append(memoryBlock)
154
155         for region in dmar.get_reserved_mem_regions():
156             memory.append(self.generate_reserved_mem_region(region))
157
158         print "Element created: memory"
159         return memory
160
161     def getMemoryBlocks(self, path):
162         memoryBlockList = []
163
164         def walkError(excep):
165             message.addError("Could not access memory block data: " +
166                              str(excep), False)
167
168         memdirs = []
169         for root, subdirs, files in os.walk(path, onerror=walkError):
170             if not subdirs:  # at end of paths
171                 memdirs.append(root)
172
173         memdirs.sort(key=lambda x: int(os.path.basename(x)))
174         # Sort paths found by numerical order
175         for root in memdirs:
176             endfile = root + "/" + "end"
177             typefile = root + "/" + "type"
178             startfile = root + "/" + "start"
179             try:
180                 memoryBlock = self.generateMemoryBlock(endfile,
181                                                        typefile,
182                                                        startfile)
183             except IOError:
184                 message.addError("Could not retrieve complete memory data",
185                                  False)
186             else:
187                 # Adds newly created memoryBlock element to memoryBlockList
188                 memoryBlockList.append(memoryBlock)
189
190         # Filter out memoryBlocks that do not meet requirements
191         memoryBlockList = self.filterMemoryBlocks(memoryBlockList)
192
193         return memoryBlockList
194
195     def filterMemoryBlocks(self, memoryBlockList):
196         "Removes reserved and empty memory blocks and returns result"
197         result = []
198         print "Filtering memory blocks..."
199         filterlist = [mem for mem in memoryBlockList
200                       if mem.name == "reserved" or mem.size == "16#0000#"]
201         result = util.removeListsFromList(memoryBlockList, filterlist)
202         return result
203
204     def generateMemoryBlock(self, endfile, typefile, startfile):
205         name = extractor.extractData(typefile)
206         physical_addr = extractor.extractData(startfile)
207
208         memsize = util.sizeOf(extractor.extractData(endfile),
209                               extractor.extractData(startfile))
210         # Round memsize down to multiple of PAGE_SIZE.
211         # Note: Blocks with size below one page will be rounded down to
212         # zero.
213         if int(memsize, 16) % int(PAGE_SIZE, 16) != 0:
214             memrounded = util.hexRoundToMultiple(
215                 memsize, PAGE_SIZE, rounddown=True)
216             print "Mem size %s for memoryBlock %s rounded down to: %s" % (
217                 memsize, physical_addr, memrounded)
218             memsize = memrounded
219
220         allocatable = str(self.isAllocatable(name, physical_addr)).lower()
221
222         memoryBlock = schemadata.schema.memoryBlockType(
223             name=name, physicalAddress=util.toWord64(physical_addr),
224             size=util.toWord64(memsize),
225             allocatable=allocatable)
226
227         return memoryBlock
228
229     def isAllocatable(self, name, physical_addr):
230         if (name == "System RAM" and
231            int(physical_addr, 16) >= MEM_ALLOCATABLE_MINSIZE):
232             return True
233         else:
234             return False
235
236     def generate_reserved_mem_region(self, region):
237         return schemadata.schema.reservedMemRegionType(
238             name=region.name, physicalAddress=util.toWord64(region.start),
239             size=util.toWord64(util.sizeOf(region.end, region.start)))
240
241
242 class DevicesCreator():
243
244     def createElem(self):
245         print "> Creating element: devices"
246         devices = schemadata.schema.devicesType(
247             pciConfigAddress=util.toWord64(
248                 self.getPciConfigAddress(paths.IOMEM)))
249
250         # Add system board
251         print "> Extracting system board information..."
252         devices.append(SystemboardDeviceCreator().create_element())
253
254         # Add IOMMUs
255         print "> Extracting IOMMU device information..."
256         for dev in IommuDevicesCreator().createElems(
257                 paths.DMAR, paths.TEMP, paths.DEVMEM):
258             devices.append(dev)
259
260         # Add Serial Devices
261         print "> Extracting Serial device information..."
262         for dev in SerialDevicesCreator().createElems(paths.SERIALS):
263             devices.append(dev)
264
265         # Add Pci Devices
266         print "> Extracting PCI device information..."
267         for dev in PciDevicesCreator().createElems(paths.DEVICES,
268                                                    paths.IOMMUGRPS):
269             devices.append(dev)
270
271         print "Element created: devices"
272
273         return devices
274
275     def getPciConfigAddress(self, path):
276         pciconfigaddr = ""
277         key = "PCI MMCONFIG"
278         try:
279             iomemdata = extractor.extractData(path)
280             keyline = parseutil.findLines(iomemdata, key)[0]
281             pciconfigaddr = keyline.split("-")[0].lstrip()
282
283         except (customExceptions.KeyNotFound, IOError):
284             message.addWarning(
285                 "Could not obtain pciConfigAddress from %s." % path)
286
287         return pciconfigaddr
288
289
290 class PciDevicesCreator():
291
292     "Helper class of DevicesCreator"
293
294     def __init__(self):
295         self.devicenames = {}  # devicepath = name
296
297     def createElems(self, devicesdir, iommugrpdir):
298         pcidevicelist = []
299         devicepaths = []
300         devicecapmgr = None
301         devicespecs = {}
302         print "Finding PCI devices..."
303         # Get device absolute paths from symbolic links in paths.DEVICES
304         devicepaths = util.getLinks(devicesdir, self.isDeviceName)
305         print "Checking Dependencies..."
306         devicecapmgr = self.init_DevicecapManager(devicepaths)
307         devicespecs = self.getDeviceSpecs(devicepaths, paths.PCIIDS)
308         print "Examining PCI devices..."
309         filteredpaths = self.filterDevicePaths(devicepaths, devicecapmgr)
310         print("Extracting device information from %d PCI devices " %
311               len(filteredpaths) + "(excluding PCI bridges and non "
312               "PCI-Express devices behind bridges)...")
313         if (not os.path.isdir(iommugrpdir) or not os.listdir(iommugrpdir)):
314             message.addWarning("IOMMU groups not available in directory '" +
315                                iommugrpdir + "', reboot with "
316                                "'intel_iommu=on' kernel parameter.")
317             iommugrpdir = None
318
319         for devicepath in filteredpaths:
320             pcidevicelist.append(self.createDeviceFromPath(
321                 devicepath,
322                 devicecapmgr,
323                 devicespecs[devicepath],
324                 iommugrpdir))
325
326         return pcidevicelist
327
328     def init_DevicecapManager(self, devicepaths):
329         "Initialises the DeviceCapManager"
330
331         print "Getting device capabilities..."
332         devicecapmgr = devicecap.DevicecapManager()
333         try:
334             devicecapmgr.extractCapabilities(devicepaths)
335         except customExceptions.DeviceCapabilitiesNotRead:
336             message.addError("Not enough permissions to access capabilities "
337                              "of devices. It is advised to run the tool again "
338                              "with the proper permissions.", False)
339         return devicecapmgr
340
341     def filterDevicePaths(self, devicePaths, devicecapmgr):
342         "Returns filtered list of paths of devices"
343         bridgePaths = []
344         pciExpressPaths = []
345         bridgedDevicePaths = []
346         nonPciExpressPaths = []
347         resultPaths = []
348         for devicepath in devicePaths:
349             if self.isPciExpress(devicepath, devicecapmgr):
350                 pciExpressPaths.append(devicepath)
351
352             if self.isBridge(devicepath):
353                 bridgePaths.append(devicepath)
354                 for root, subdirs, files in os.walk(devicepath):
355                     for subdir in subdirs:
356                         if self.isDeviceName(subdir):
357                             bridgedDevicePaths.append(
358                                 os.path.join(root, subdir))
359
360         for bridgedDevice in bridgedDevicePaths:
361             if self.isPciExpress(bridgedDevice, devicecapmgr) is False:
362                 nonPciExpressPaths.append(bridgedDevice)
363
364         print "PCI Devices found: %d\n------------------" % len(devicePaths)
365         print "> PCI Bridges:", len(bridgePaths)
366         for item in bridgePaths:
367             print "  ", os.path.basename(item)
368
369         print "> Devices behind bridges:", len(bridgedDevicePaths)
370         for item in bridgedDevicePaths:
371             print "  ", os.path.basename(item)
372
373         print "> PCI Express Devices:", len(pciExpressPaths)
374         for item in pciExpressPaths:
375             print "  ", os.path.basename(item)
376
377         resultPaths = util.removeListsFromList(devicePaths,
378                                                bridgePaths,
379                                                nonPciExpressPaths)
380         return resultPaths
381
382     def isBridge(self, devicepath):
383         isBridge = False
384         PCI_BRIDGE = "0x0604"
385         if extractor.extractData(os.path.join(devicepath,
386                                               "class"))[0:6] == PCI_BRIDGE:
387             isBridge = True
388
389         return isBridge
390
391     def isPciExpress(self, devicepath, devicecapmgr):
392         isPciExpress = False
393         PCI_EXPRESS = "0x10"
394
395         if PCI_EXPRESS in devicecapmgr.getCapList(devicepath):
396             isPciExpress = True
397
398         return isPciExpress
399
400     def isDeviceName(self, value):
401         "Checks for format: ####:##:##.#"
402         splitcolon = value.split(':')
403         result = True
404
405         try:
406             if len(splitcolon) != 3:
407                 result = False
408
409             if '.' not in splitcolon[2]:
410                 result = False
411
412             if len(splitcolon[0]) != 4:  # Host bus no. length
413                 result = False
414
415             if len(splitcolon[1]) != 2:  # Bus no. length
416                 result = False
417
418             if len(splitcolon[2].split('.')[0]) != 2:  # Device no. length
419                 result = False
420
421             if len(splitcolon[2].split('.')[1]) != 1:  # Function no. length
422                 result = False
423         except IndexError:
424             result = False
425
426         return result
427
428     def getDeviceBus(self, devicestr):
429         return devicestr.split(':')[1]
430
431     def getDeviceNo(self, devicestr):
432         return (devicestr.split(':')[2]).split('.')[0]
433
434     def getDeviceFunction(self, devicestr):
435         return (devicestr.split(':')[2]).split('.')[1]
436
437     def getDeviceSpecs(self, devicepaths, pciids):
438         specs = OrderedDict()
439         occurrences = {}
440
441         try:
442             # Init PciIdsParser, throws customExceptions.PciIdsFileNotFound
443             # if fail
444             pciIdsParser = parseutil.PciIdsParser(pciids)
445         except customExceptions.PciIdsFileNotFound:
446             message.addError("pci.ids file could not be located in tool "
447                              "directory: %s. " % paths.CURRENTDIR +
448                              "Device "
449                              "names could not be obtained. Please ensure that "
450                              "the file is in the directory.",
451                              False)
452         else:
453
454             for devicepath in devicepaths:
455                 classcode = extractor.extractData(
456                     os.path.join(devicepath, "class"))[2:6]
457                 vendor = extractor.extractData(
458                     os.path.join(devicepath, "vendor"))[2:6]
459                 device = extractor.extractData(
460                     os.path.join(devicepath, "device"))[2:6]
461                 revision = extractor.extractBinaryData(
462                     os.path.join(devicepath, "config"), 8, 1)[2:4]
463
464                 # Construct device name using class name
465                 # (e.g. usb_controller_1).
466                 device_name = self.toClassName(classcode, pciIdsParser)
467                 cur_count = occurrences.get(device_name, 1)
468                 occurrences[device_name] = cur_count + 1
469
470                 descr = ""
471                 try:
472                     descr = pciIdsParser.getVendorName(
473                         vendor) + " " + pciIdsParser.getDeviceName(vendor,
474                                                                    device)
475                 except:
476                     descr = ""
477
478                 specs[devicepath] = PciDevice(
479                     name=device_name + "_%d" % cur_count,
480                     classcode=util.wrap16(classcode),
481                     vendorId=util.wrap16(vendor),
482                     deviceId=util.wrap16(device),
483                     revisionId=util.wrap16(revision),
484                     descr=descr)
485
486         return specs
487
488     def toClassName(self, classcode, pciIdsParser):
489         "Convert given class code to name"
490         classname = classcode
491         try:
492             classname = pciIdsParser.getClassName(classname)
493             classname = util.spacesToUnderscores(classname.lower())
494         except (customExceptions.PciIdsFailedSearch,
495                 customExceptions.PciIdsSubclassNotFound):
496             message.addWarning(("Unable to resolve device class " + classcode +
497                                 ". Please update pci.ids (-u) and try again"))
498         return classname
499
500     def getPci(self, devicepath, devicecapmgr, devicespec, iommugrpdir):
501         pcistr = os.path.basename(devicepath)
502         pci = schemadata.schema.pciType(
503             bus=util.wrap16(self.getDeviceBus(pcistr)),
504             device=util.wrap16(self.getDeviceNo(pcistr)),
505             function=self.getDeviceFunction(pcistr),
506             msi=self.getPci_MSI(devicepath, devicecapmgr)).append(
507                 schemadata.schema.deviceIdentificationType(
508                     classcode=devicespec.classcode,
509                     vendorId=devicespec.vendorId,
510                     deviceId=devicespec.deviceId,
511                     revisionId=devicespec.revisionId))
512
513         if iommugrpdir:
514             for root, dirs, files in os.walk(iommugrpdir):
515                 if pcistr in dirs:
516                     tokens = root.split("/")
517                     pci.append(schemadata.schema.iommuGroupType
518                                (id=tokens[len(tokens) - 2]))
519
520         return pci
521
522     def getPci_MSI(self, devicepath, devicecapmgr):
523         msi = "false"
524         if devicecap.CAP_MSI in devicecapmgr.getCapList(devicepath):
525             if devicecapmgr.getCapValue(devicepath, devicecap.CAP_MSI).enable:
526                 msi = "true"
527         if devicecap.CAP_MSIX in devicecapmgr.getCapList(devicepath):
528             if devicecapmgr.getCapValue(devicepath, devicecap.CAP_MSIX).enable:
529                 msi = "true"
530
531         return msi
532
533     def getLegacyIrq(self, devicepath):
534         irqs = []
535         try:
536             irqNo = extractor.extractData(os.path.join(devicepath, "irq"))
537         except IOError:
538             message.addError("Could not obtain irq number for device: %s" %
539                              os.path.basename(devicepath), False)
540         else:
541             if irqNo is not "0":
542                 irq = schemadata.schema.irqType(
543                     name="irq%d" % RESOURCE_START_IDX, number=irqNo)
544                 irqs.append(irq)
545
546         return irqs
547
548     def getMsiIrqs(self, devicepath):
549         irqs = []
550         path = os.path.join(devicepath, "msi_irqs")
551
552         if os.path.exists(path):
553             for c, file in enumerate(sorted(os.listdir(path)),
554                                      start=RESOURCE_START_IDX):
555                 irq = schemadata.schema.irqType(name="irq%d" % c, number=file)
556                 irqs.append(irq)
557
558         return irqs
559
560     def getDeviceMemory(self, devicepath, minsize=PAGE_MIN_SIZE):
561         devmemblocks = []
562         try:
563             resourceData = extractor.extractData(
564                 os.path.join(devicepath, "resource"))
565         except IOError:
566             message.addError("Could not obtain memory information for device: "
567                              "%s" % os.path.basename(devicepath), False)
568         else:
569             memcount = RESOURCE_START_IDX
570             for line in resourceData.splitlines():
571                 tokens = line.split(' ')
572                 if tokens[2][-3] == '2':  # if line represents a memory block
573
574                     memory = schemadata.schema.deviceMemoryType(
575                         name="mem%d" % memcount,
576                         physicalAddress=util.toWord64(tokens[0]),
577                         caching="UC")
578                     # Rounds memsize up to minsize
579                     memsize = util.sizeOf(tokens[1], tokens[0])
580                     if int(memsize, 16) < int(minsize, 16):
581                         memrounded = util.hexFloor(memsize, minsize)
582                         print("Mem size %s for device %s rounded to: %s" %
583                               (memsize,
584                                os.path.basename(devicepath),
585                                memrounded))
586                         memsize = memrounded
587
588                     memory.size = util.toWord64(memsize)
589                     devmemblocks.append(memory)
590                     memcount += 1
591
592         return devmemblocks
593
594     def getIoports(self, devicepath):
595         ioports = []
596
597         try:
598             resourceData = extractor.extractData(
599                 os.path.join(devicepath, "resource"))
600         except IOError:
601             message.addError("Could not obtain ioport information for device: "
602                              "%s" % os.path.basename(devicepath), False)
603         else:
604             ioportcount = RESOURCE_START_IDX
605             for line in resourceData.splitlines():
606                 tokens = line.split(' ')
607                 if tokens[2][-3] == '1':  # if line represents ioport info
608
609                     ioPort = schemadata.schema.ioPortType(
610                         name="ioport%d" % ioportcount,
611                         start=util.toWord64(tokens[0]),
612                         end=util.toWord64(tokens[1]))
613                     ioportcount += 1
614                     ioports.append(ioPort)
615
616         return ioports
617
618     def getIrqs(self, devicepath):
619         irqs = []
620
621         if os.path.exists(os.path.join(devicepath, "msi_irqs")):
622             irqs = self.getMsiIrqs(devicepath)
623         else:
624             irqs = self.getLegacyIrq(devicepath)
625
626         return irqs
627
628     def createDeviceFromPath(self, devicepath, devicecapmgr, devicespec,
629                              iommugrp):
630         device = schemadata.schema.deviceType(name=devicespec.name)
631         if devicespec.descr:
632             device.description = devicespec.descr
633
634         device.pci = self.getPci(devicepath, devicecapmgr, devicespec,
635                                  iommugrp)
636
637         # irq
638         for irq in self.getIrqs(devicepath):
639             device.append(irq)
640
641         # memory, includes expansion roms
642         for devmemblock in self.getDeviceMemory(devicepath):
643             device.append(devmemblock)
644
645         # ioports
646         for ioport in self.getIoports(devicepath):
647             device.append(ioport)
648
649         # reserved memory region if present
650         pcistr = os.path.basename(devicepath)
651         rmrr = dmar.get_referenced_rmrr(self.getDeviceBus(pcistr),
652                                         self.getDeviceNo(pcistr),
653                                         self.getDeviceFunction(pcistr))
654         if rmrr:
655             device.append(schemadata.schema.namedRefType(ref=rmrr))
656
657         return device
658
659
660 class SerialDevicesCreator():
661
662     "Helper class of DevicesCreator"
663
664     def __init__(self):
665         self.currentdev = 1
666
667     def createElems(self, serialdevicepath):
668         "Return serial devices found in given path"
669         serialdevices = []
670         for fname in sorted(os.listdir(serialdevicepath)):
671             devpath = os.path.join(serialdevicepath, fname)
672             resources = devpath + "/resources"
673             if os.path.isdir(devpath) and os.path.exists(resources):
674                 devresources = extractor.extractData(resources)
675                 device = self.parseSerialDeviceResources(devresources)
676                 if device is not None:
677                     print "  Name: " + device.name + ", Ports: " \
678                         + device.start + "-" + device.end \
679                         + (", IRQ: " + device.irq if device.irq is not None
680                            else "")
681                     xmlDev = schemadata.schema.deviceType(name=device.name)
682
683                     if device.irq is not None:
684                         xmlDev.append(schemadata.schema.irqType(
685                             name="irq1", number=device.irq))
686
687                     xmlDev.append(schemadata.schema.ioPortType(
688                         name="ioport1",
689                         start=util.toWord64(device.start),
690                         end=util.toWord64(device.end)))
691
692                     serialdevices.append(xmlDev)
693
694         return serialdevices
695
696     def parseSerialDeviceResources(self, resources):
697         "Parses serial device resources"
698         device = None
699         try:
700             addrs = parseutil.parseData_Sep(resources, "io", " ")
701             irq = None
702             try:
703                 irq = parseutil.parseData_Sep(resources, "irq", " ")
704             except customExceptions.KeyNotFound:
705                 pass
706         except customExceptions.KeyNotFound:
707             message.addMessage("Error parsing serial device information")
708         else:
709             start = addrs.partition("-")[0]
710             end = addrs.partition("-")[2]
711             ports = Address(start[2:], end[2:])
712             name = ""
713             if ports in WELL_KNOWN_IOPORTS:
714                 name = WELL_KNOWN_IOPORTS[ports]
715             else:
716                 name = "serial_port_%d" % self.currentdev
717                 self.currentdev = self.currentdev + 1
718
719             device = SerialDevice(name, start, end, irq)
720
721         return device
722
723
724 class IommuDevicesCreator():
725
726     def createElems(self, dmarpath, temppath, devmempath):
727         elemlist = []
728
729         # Create Iommu devices
730         for i, addr in enumerate(dmar.get_iommu_addrs(), 1):
731             elemlist.append(
732                 self.createDeviceFromAddr(devmempath,
733                                           addr,
734                                           "iommu_" + str(i)))
735         return elemlist
736
737     def getIommuRegisterValue(self,
738                               address,
739                               devmem,
740                               regsize):
741         "Return IOMMU register value at given address and specified size"
742         try:
743             return int(extractor.extractBinaryData(devmem,
744                                                    address,
745                                                    regsize),
746                        16)
747         except IOError, e:
748             message.addError("Could not access file: %s - %s" %
749                              (devmem, hex(address)), False)
750             return -1
751
752     def getIommuAGAW(self,
753                      iommuaddr,
754                      devmem,
755                      capoffset,
756                      capbytesize,
757                      agawbitstart):
758         "Gets the AGAW name from a given iommuaddr, at the capability offset"
759         capreg = self.getIommuRegisterValue(iommuaddr + capoffset,
760                                             devmem,
761                                             capbytesize)
762
763         if capreg == -1:
764             return "agaw"
765
766         AGAW_39_BITNO = 1
767         AGAW_48_BITNO = 2
768         AGAW_39_NAME = "39"
769         AGAW_48_NAME = "48"
770
771         try:
772             agaw = (capreg >> agawbitstart) & 0x1F  # See 5 bits
773             if util.getBit(agaw, AGAW_39_BITNO):
774                 return AGAW_39_NAME
775             elif util.getBit(agaw, AGAW_48_BITNO):
776                 return AGAW_48_NAME
777         except:
778             message.addError("AGAW Capability could not be found for "
779                              "IOMMU device at: %s" % iommuaddr, False)
780         return "agaw"
781
782     def createDeviceFromAddr(self,
783                              devmem,
784                              iommuaddr,
785                              iommuname):
786         "Generates a device element from a given iommu address"
787
788         CAP_REG_OFFSET = 0x08
789         CAP_REG_BYTE_SIZE = 8
790
791         EXTCAP_REG_OFFSET = 0x10
792         EXTCAP_REG_BYTE_SIZE = 8
793
794         AGAW_BIT_START = 8
795
796         IOMMU_SIZE = "1000"
797
798         device = schemadata.schema.deviceType(name=iommuname)
799
800         device.append(schemadata.schema.deviceMemoryType(
801             name="mmio",
802             caching="UC",
803             physicalAddress=util.toWord64(iommuaddr),
804             size=util.toWord64(IOMMU_SIZE)))
805
806         capabilities = schemadata.schema.capabilitiesType()
807         capabilities.append(schemadata.schema.capabilityType(name="iommu"))
808
809         # agaw
810         capabilities.append(schemadata.schema.capabilityType(
811             self.getIommuAGAW(int(iommuaddr, 16),
812                               devmem,
813                               CAP_REG_OFFSET,
814                               CAP_REG_BYTE_SIZE,
815                               AGAW_BIT_START), name="agaw"))
816
817         # fro, see Intel VT-d spec sections 10.4.2 and 10.4.14
818         capabilities.append(schemadata.schema.capabilityType(
819             str(((self.getIommuRegisterValue(
820                 int(iommuaddr, 16) + CAP_REG_OFFSET,
821                 devmem,
822                 CAP_REG_BYTE_SIZE) >> 24) & 0x3ff) * 16),
823             name="fr_offset"))
824
825         # iro, see Intel VT-d spec sections 10.4.3 and 10.4.8.1
826         capabilities.append(schemadata.schema.capabilityType(
827             str(((self.getIommuRegisterValue(
828                 int(iommuaddr, 16) + EXTCAP_REG_OFFSET,
829                 devmem,
830                 EXTCAP_REG_BYTE_SIZE) >> 8) & 0x3ff) * 16 + 8),
831             name="iotlb_invalidate_offset"))
832
833         device.append(capabilities)
834
835         return device
836
837
838 class SystemboardDeviceCreator():
839
840     def create_element(self):
841         dev = schemadata.schema.deviceType(name="system_board")
842
843         port = 0xcf8
844         for i in range(1, 9):
845             dev.append(schemadata.schema.ioPortType(
846                 name="ioport" + str(i),
847                 start=util.toWord64(hex(port)),
848                 end=util.toWord64(hex(port))))
849             port += 1
850
851         shutdown_port = fadt.get_shutdown_port()
852         dev.append(schemadata.schema.ioPortType(
853             name="pm1a_cnt",
854             start=util.toWord64(shutdown_port),
855             end=util.toWord64(shutdown_port)))
856
857         caps = schemadata.schema.capabilitiesType()
858         caps.append(schemadata.schema.capabilityType(name="systemboard"))
859         # First byte in ACPI DSDT _S5 object
860         caps.append(schemadata.schema.capabilityType(
861             7168, name="pm1a_cnt_slp_typ"))
862         dev.append(caps)
863
864         return dev
865
866
867 def genDmesg(temppath, name):
868     util.makefolder(temppath)
869     target = os.path.join(temppath, name)
870     print "Generating dmesg output to: %s" % target
871     with open(target, "w") as f:
872         subprocess.check_call(["dmesg"], stdout=f)
873     return target
874
875
876 def createElements():
877     "Creates the element tree and returns top element"
878
879     # Choose binding module to use
880     schemadata.selectSchema("hardware_config")
881
882     # Initialise dmesg
883     dmesg = genDmesg(paths.TEMP, "dmesg_tmp")
884
885     # Parse DMAR
886     dmar.parse_table(paths.DMAR, paths.TEMP)
887
888     # Parse FADT
889     fadt.parse_table(paths.FADT, paths.TEMP)
890
891     # Create Elements
892     hardware = schemadata.schema.hardwareType()
893     hardware.append(ProcessorCreator().createElem(paths.CPUINFO, paths.MSR,
894                                                   dmesg))
895     hardware.append(MemoryCreator().createElem(paths.MEMMAP))
896     hardware.append(DevicesCreator().createElem())
897
898     return hardware