1# Copyright (C) 2021-2022 Intel Corporation.
2#
3# SPDX-License-Identifier: BSD-3-Clause
4#
5
6import sys
7import mmap
8import logging
9from math import floor, ceil
10
11from .exception import *
12
13class Object:
14    def get(self):
15        raise NotImplementedError(self.__class__.__name__)
16
17    def set(self, obj):
18        raise NotImplementedError(self.__class__.__name__)
19
20    def to_buffer(self):
21        raise NotImplementedError(self.__class__.__name__)
22
23    def to_decimal_string(self):
24        raise NotImplementedError(self.__class__.__name__)
25
26    def to_hex_string(self):
27        raise NotImplementedError(self.__class__.__name__)
28
29    def to_integer(self):
30        raise NotImplementedError(self.__class__.__name__)
31
32    def to_string(self):
33        raise NotImplementedError(self.__class__.__name__)
34
35    def get_obj(self):
36        return self
37
38class UninitializedObject(Object):
39    def to_string(self):
40        return "Uninitialized Object"
41
42class BufferBase(Object):
43    @staticmethod
44    def bitmask(to, frm):
45        return ((1 << (to + 1)) - 1) - ((1 << frm) - 1)
46
47    def __init__(self, length):
48        self.__length = length
49        self.__fields = {}    # name -> (offset, bitwidth, access_width)
50
51    def read(self, byte_idx, bit_width):
52        return NotImplementedError(self.__class__.__name__)
53
54    def write(self, byte_idx, value, bit_width):
55        return NotImplementedError(self.__class__.__name__)
56
57    def create_field(self, name, offset, bitwidth, access_width):
58        self.__fields[name] = (offset, bitwidth, access_width)
59
60    def field_bitwidth(self, name):
61        return self.__fields[name][1]
62
63    def read_field(self, name):
64        offset, bitwidth, access_width = self.__fields[name]
65        acc = 0
66        acc_bit_count = 0
67        bit_idx = offset
68        bit_remaining = bitwidth
69
70        assert offset + bitwidth <= self.__length * 8, \
71            f"Buffer overflow: attempt to access field {name} at bit {offset + bitwidth} while the buffer has only {len(self.__data) * 8} bits"
72
73        # Bits out of byte boundary
74        if bit_idx % access_width > 0:
75            # byte_idx shall be (access_width // 8)-byte aligned
76            byte_idx = (bit_idx // access_width) * (access_width // 8)
77            bit_count = (access_width - bit_idx % access_width)
78            if bit_count > bit_remaining:
79                bit_count = bit_remaining
80
81            mask = self.bitmask(bit_idx % access_width + bit_count - 1, bit_idx % access_width)
82            acc = (self.read(byte_idx, access_width) & mask) >> (bit_idx % access_width)
83            acc_bit_count += bit_count
84            bit_idx += bit_count
85            bit_remaining -= bit_count
86
87        while bit_remaining > 0:
88            byte_idx = bit_idx // 8
89            bit_count = min(access_width, bit_remaining)
90
91            mask = self.bitmask(bit_count - 1, 0)
92            acc |= (self.read(byte_idx, access_width) & mask) << acc_bit_count
93            acc_bit_count += bit_count
94            bit_idx += bit_count
95            bit_remaining -= bit_count
96
97        return acc
98
99    def write_field(self, name, value):
100        offset, bitwidth, access_width = self.__fields[name]
101        bit_idx = offset
102        bit_remaining = bitwidth
103
104        assert offset + bitwidth <= self.__length * 8, \
105            f"Buffer overflow: attempt to access field {name} at bit {offset + bitwidth} while the buffer has only {len(self.__data) * 8} bits"
106
107        # Bits out of access_width boundary
108        if bit_idx % access_width > 0:
109            byte_idx = (bit_idx // access_width) * (access_width // 8)
110            bit_count = (access_width - bit_idx % access_width)
111            if bit_count > bit_remaining:
112                bit_count = bit_remaining
113
114            mask_of_write = self.bitmask(bit_idx % access_width + bit_count - 1, bit_idx % access_width)
115            mask_of_keep = ((1 << access_width) - 1) - mask_of_write
116            v = (value & ((1 << bit_count) - 1)) << (bit_idx % access_width)
117            self.write(byte_idx, (v & mask_of_write) | (self.read(byte_idx, access_width) & mask_of_keep), access_width)
118
119            value >>= bit_count
120            bit_idx += bit_count
121            bit_remaining -= bit_count
122
123        while bit_remaining > 0:
124            byte_idx = bit_idx // 8
125            bit_count = min(access_width, bit_remaining)
126
127            mask_of_write = self.bitmask(bit_count - 1, 0)
128            mask_of_keep = ((1 << access_width) - 1) - mask_of_write
129            v = (value & ((1 << bit_count) - 1))
130            self.write(byte_idx, (v & mask_of_write) | (self.read(byte_idx, access_width) & mask_of_keep), access_width)
131
132            value >>= bit_count
133            bit_idx += bit_count
134            bit_remaining -= bit_count
135
136    def to_buffer(self):
137        return self
138
139class Buffer(BufferBase):
140    def __init__(self, data):
141        assert len(data) > 0
142        super().__init__(len(data))
143        self.__data = bytearray(data)
144
145    @property
146    def data(self):
147        return bytes(self.__data)
148
149    def read(self, byte_idx, bit_width):
150        acc = 0
151        byte_width = min(bit_width // 8, len(self.__data) - byte_idx)
152        return int.from_bytes(self.__data[byte_idx : (byte_idx + byte_width)], sys.byteorder)
153
154    def write(self, byte_idx, value, bit_width):
155        byte_width = min(bit_width // 8, len(self.__data) - byte_idx)
156        self.__data[byte_idx : (byte_idx + byte_width)] = value.to_bytes(byte_width, sys.byteorder)
157
158    def get(self):
159        return self.__data
160
161    def set(self, value):
162        data = value.to_buffer().get()
163        copy_length = min(len(data), len(self.__data))
164        self.__data[:copy_length] = data[:copy_length]
165
166    def to_hex_string(self):
167        result = ",".join(map(lambda x:hex(x)[2:], self.__data))
168        return String(result)
169
170    def to_integer(self):
171        acc = 0
172        i = min(len(self.__data), 8) - 1
173        while i >= 0:
174            acc <<= 8
175            acc |= self.__data[i]
176            i -= 1
177        return Integer(acc)
178
179class StreamIOBuffer(BufferBase):
180    def __init__(self, stream, base, length):
181        super().__init__(length)
182        self.__stream = stream
183        self.__base = base
184
185    def read(self, byte_idx, bit_width):
186        byte_width = bit_width // 8
187        self.__stream.seek(self.__base + byte_idx)
188        data = self.__stream.read(byte_width)
189        return int.from_bytes(data, sys.byteorder)
190
191    def write(self, byte_idx, value, bit_width):
192        byte_width = bit_width // 8
193        self.__stream.seek(self.__base + byte_idx)
194        self.__stream.write(value.to_bytes(byte_width, sys.byteorder))
195
196class IndexedIOBuffer(BufferBase):
197    def __init__(self, index_register, data_register):
198        # FIXME: Get the real size of an indexed I/O region
199        super().__init__(256)
200        self.__index_register = index_register
201        self.__data_register = data_register
202
203    def read(self, byte_idx, bit_width):
204        assert bit_width == 8, f"Indexed I/O buffers can only be read one byte at a time"
205        self.__index_register.set(Integer(byte_idx, 8))
206        return self.__data_register.get()
207
208    def write(self, byte_idx, value, bit_width):
209        # Do not allow writes to indexed I/O buffer
210        assert False, "Cannot write to indexed I/O buffers"
211
212class BufferField(Object):
213    def __init__(self, buf, field):
214        self.__buf = buf
215        self.__field = field
216
217    def get(self):
218        return self.__buf.read_field(self.__field)
219
220    def set(self, obj):
221        self.__buf.write_field(self.__field, obj.get())
222
223    def set_writable(self):
224        self.__buf.set_field_writable(self.__field)
225
226    def to_integer(self):
227        return Integer(self.get())
228
229    def to_buffer(self):
230        bitwidth = self.__buf.field_bitwidth(self.__field)
231        return Buffer(self.get().to_bytes((bitwidth + 7) // 8, sys.byteorder))
232
233    def to_string(self):
234        return f"BufferField({self.__field})"
235
236    def to_hex_string(self):
237        return String(hex(self.get())[2:])
238
239# DebugObject
240
241class Device(Object):
242    def __init__(self, sym):
243        self.__sym = sym
244
245    def get_sym(self):
246        return self.__sym
247
248# Event
249
250class FieldUnit(BufferField):
251    def to_string(self):
252        return "Field"
253
254class Integer(Object):
255    def __init__(self, value, width=64):
256        self.__value = value
257        self.__width = width
258
259    def get(self):
260        return self.__value
261
262    def set(self, obj):
263        self.__value = obj.get()
264
265    def to_buffer(self):
266        assert self.__width % 8 == 0
267        data = bytearray()
268        i = 0
269        v = self.__value
270        while i < self.__width:
271            data.append(v & 0xff)
272            v >>= 8
273            i += 8
274        return Buffer(data)
275
276    def to_decimal_string(self):
277        return String(str(self.__value))
278
279    def to_hex_string(self):
280        return String(hex(self.__value)[2:])
281
282    def to_integer(self):
283        return self
284
285class Method(Object):
286    def __init__(self, tree):
287        self.tree = tree
288        self.name = tree.children[1].value
289        self.body = tree.children[3]
290
291class PredefinedMethod(Object):
292    def __init__(self, fn):
293        self.fn = fn
294
295# Mutex
296
297class ObjectReference(Object):
298    def __init__(self, obj, index=None):
299        self.__obj = obj
300        self.__index = index
301
302    def get(self):
303        if self.__index is not None:
304            if isinstance(self.__obj, Package):
305                return self.__obj.elements[self.__index]
306            elif isinstance(self.__obj, Buffer):
307                name = f"byte_{hex(self.__index)[2:]}"
308                self.__obj.create_field(name, self.__index * 8, 8, 8)
309                return BufferField(self.__obj, name)
310            else:
311                raise NotImplementedError(self.__obj.__class__.__name__)
312        else:
313            return self.__obj
314
315    def set(self, obj, index=None):
316        self.__obj = obj
317        self.__index = index
318
319class OperationRegion(Object):
320    devmem = None
321    devport = None
322    opened_indexed_regions = {}
323
324    @classmethod
325    def open_system_memory(cls, name, offset, length):
326        if not cls.devmem:
327            cls.devmem = open("/dev/mem", "rb", buffering=0)
328
329        logging.debug(f"Open system memory space {name}: [{hex(offset)}, {hex(offset + length - 1)}]")
330        offset_page_aligned = (offset >> 12) << 12
331        length_page_aligned = ceil(((offset & 0xFFF) + length) / 0x1000) * 0x1000
332        try:
333            mm = mmap.mmap(cls.devmem.fileno(), length_page_aligned, flags=mmap.MAP_PRIVATE, prot=mmap.PROT_READ, offset=offset_page_aligned)
334        except PermissionError as e:
335            logging.debug(f"Do not have permission to access [{hex(offset_page_aligned)}, {hex(offset_page_aligned + length_page_aligned)}] by /dev/mem.")
336            logging.debug(f"You may need to add `iomem=relaxed` to the Linux kernel command line in your bootloader configuration file.")
337            raise
338        iobuf = StreamIOBuffer(mm, offset & 0xFFF, length)
339        return OperationRegion(iobuf)
340
341    @classmethod
342    def open_system_io(cls, name, offset, length):
343        if not cls.devport:
344            cls.devport = open("/dev/port", "w+b", buffering=0)
345
346        logging.debug(f"Open system I/O space {name}: [{hex(offset)}, {hex(offset + length - 1)}]")
347        iobuf = StreamIOBuffer(cls.devport, offset, length)
348        return OperationRegion(iobuf)
349
350    @classmethod
351    def open_pci_configuration_space(cls, bus_number, device_adr, offset, length):
352        assert offset <= 0xFF and (offset + length) <= 0x100
353        # Assume bus is 0 for now
354        bus = bus_number
355        device = device_adr >> 16
356        function = device_adr & 0xFF
357        sysfs_path = "/sys/devices/pci0000:%02x/0000:%02x:%02x.%d/config" % (bus, bus, device, function)
358        try:
359            f = open(sysfs_path, "rb", buffering=0)
360            iobuf = StreamIOBuffer(f, offset, length)
361            return OperationRegion(iobuf)
362        except FileNotFoundError:
363            logging.debug(f"Cannot read the configuration space of %02x:%02x.%d from {sysfs_path}. Assume the PCI device does not exist." % (bus, device, function))
364            data = bytearray([0xff]) * length
365            buf = Buffer(data)
366            return OperationRegion(buf)
367
368    @classmethod
369    def open_indexed_region(cls, index_register, data_register):
370        logging.debug(f"Open I/O region indexed by index register {index_register.to_string()} and data register {data_register.to_string()}.")
371        k = (str(index_register), str(data_register))
372        if k not in cls.opened_indexed_regions.keys():
373            iobuf = IndexedIOBuffer(index_register, data_register)
374            region = OperationRegion(iobuf)
375            cls.opened_indexed_regions[k] = region
376
377            # Mark the index register as writable
378            index_register.set_writable()
379
380            return region
381        else:
382            return cls.opened_indexed_regions[k]
383
384    def __init__(self, iobuf):
385        self.__iobuf = iobuf
386        self.__writable_fields = set()
387
388    def create_field(self, name, offset, bitwidth, access_width):
389        self.__iobuf.create_field(name, offset, bitwidth, access_width)
390
391    def read_field(self, name):
392        return self.__iobuf.read_field(name)
393
394    def field_bitwidth(self, name):
395        return self.__iobuf.field_bitwidth(name)
396
397    def write_field(self, name, value):
398        # Do not allow writes to stream I/O buffer unless the base is explicitly marked as writable
399        if name in self.__writable_fields:
400            self.__iobuf.write_field(name, value)
401        else:
402            if isinstance(value, int):
403                logging.debug(f"Skip writing 0x{value:0X} to I/O field {name}")
404            else:
405                logging.debug(f"Skip writing {value} to I/O field {name}")
406
407    def set_field_writable(self, name):
408        self.__writable_fields.add(name)
409
410    def to_string(self):
411        return "Operation Region"
412
413class Package(Object):
414    def __init__(self, elements):
415        self.__elements = elements
416
417    @property
418    def elements(self):
419        return self.__elements
420
421    def to_string(self):
422        return "Package"
423
424class PowerResource(Object):
425    def __init__(self, name):
426        self.name = name
427
428# Processor
429
430class RawDataBuffer(Object):
431    def __init__(self, data):
432        self.__data = data
433
434    def get(self):
435        return self.__data
436
437class String(Object):
438    def __init__(self, s):
439        self.__s = s
440
441    def get(self):
442        return self.__s
443
444    def set(self, obj):
445        self.__s = obj.get()
446
447    def to_decimal_string(self):
448        return self
449
450    def to_hex_string(self):
451        return self
452
453    def to_integer(self):
454        return Integer(int(self.__s, base=16))
455
456    def to_string(self):
457        return self
458
459# ThermalZone
460