With these annotations, mypy no longer report any issues in strict mode:
$ mypy --strict chipsec/helper/linuxnative/cpuid.py
Success: no issues found in 1 source file
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
Make the machine code `bytes` instead of `list`. This makes it no longer
necessary to cast it to `c_ubyte * size` before using it.
While at it, add `mmap.MAP_PRIVATE` flag to the mapping, as it is not
needed to create a `MAP_SHARED` mapping for the code (which is the
default flag).
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
Now that chipsec is no longer compatible with Python2, this import is no
longer useful.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
There is no point in doing a lazy-import with
`chipsec.helper.linuxnative.cpuid`, as the `cpuid` function is almost
always called when starting chipsec, to display the banner.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
When using chipsec.chipset Python module from an unprivileged user, the
Python interpreter crashes with a segmentation fault on an `outl`
instruction. This is because `iopl(3)` failed and the "legacy PCI"
implementation tried to write a value to an I/O port without being
allowed to.
Fix this by checking the return value of `iopl`.
While at it, enable using `error` in the C library, to properly report
the `EPERM` error.
While working on this code, it appeared that:
- `self.addr` was written twice in `PORTS`' constructor: once for the
`inl` implementation and another one for `outl`.
- Everytime `PORTS` or `LEGACY_PCI` objects were created, some memory
was allocated (with `mmap.mmap`) to run the helpers and this memory
was never freed.
- The global variables `IN_PORT` and `OUT_PORT` were defined as lists
but always used as bytes.
Fix these issues by spliting `self.addr` into two variables, by
introducing a class property which re-use previous `PORTS` instances
(making this class a singleton) and by making `IN_PORT` and `OUT_PORT`
directly `bytes`.
While at it, change the names to the usual camel case convention
(`PORTS` -> `Ports` and `LEGACY_PCI` -> `LegacyPci`) and add type hints.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
On AMD, the "PCH" is the LPC device on PCI 0:14.3. According to "PPR for
AMD Family 17h Model 18h B1. 55570-B1 Rev 3.16 - Apr 14, 2021."
available on
https://www.amd.com/en/support/tech-docs/processor-programming-reference-ppr-for-amd-family-17h-model-18h-revision-b1
the low byte at D14F3x008 (bus 0 device 14 function 3 offset 8) is the
RevisionID of this device.
Using 0:1F.0 to read the RID in `Chipset.detect_platform` is likely to
be a bug from commit 2691f50bae ("Detect PCH based on platform vendor
ID"), which introduced AMD support in this function. Fix this by using
`bus, dev, fun` to read `pch_rid`.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
This global variable is no longer used after commit 4b9ab99839
("Change the way helper loading is done"). Update the documentation too.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
`uefi_search.py` calls `is_printable` with bytes, in:
m = re.compile(bytes(rule['regexp'], 'utf-8')).search(efi.Image)
if m:
match_result |= MATCH_REGEXP
_str = m.group(0)
hexver = binascii.hexlify(_str)
printver = f" ('{_str}')" if defines.is_printable(_str) else ''
... because `_str` has actually type `bytes`, not `str`.
When `is_printable` is called with bytes, it always return `False`,
because in
set(seq).issubset(set(string.printable))
`set(seq)` is a set of integers and `set(string.printable)` is a set of
strings.
Fix this by always converting the parameter to string, using
`bytestostring`. This is not the most efficient way of doing this (a
more efficient would be `set(seq).issubset(set(string.printable.encode()))`
with some caching of the second set) but it is simple and makes caller
less likely to use the function in an unsupported way.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
Reading 64-bit MMIO registers does not work with Linux native helper.
For example:
$ ./chipsec_util.py --helper linuxnativehelper txt state
...
TXT Public Key Hash: 9c78f0d8000000002f47761c00000000164a66a90000000092e3144f00000000
This value comes from four 64-bit MMIO registers (`TXT_PUBLIC_KEY_0` to
`TXT_PUBLIC_KEY_3`). This is caused by `LinuxNativeHelper.read_mmio_reg`
assuming the size cannot be larger than 4. And it also assumes that the
physical address is always aligned on a 4-byte boundary.
Reading a value as a "32-bit integer" can be important for MMIO
registers, so do not change this case, when the physical address is
aligned. In practice:
- If `size == 1`, the result can be directly read from the mapping.
- Otherwise, if the address is aligned,
`region_mv.cast(defines.SIZE2FORMAT[size])` can be used to trigger an
atomic read of the requested size.
- In the unaligned case, the bytes are read from the memory view,
possibly one by one, and `defines.unpack1` is used to glue them back
to an integer.
Implement a similar logic in `write_mmio_reg` too. While at it, as the
`return 0` did not make much sense in this function, remove it.
With this commit:
$ ./chipsec_util.py --helper linuxnativehelper txt state
...
TXT Public Key Hash: 9c78f0d853de854a2f47761c72b86a11164a66a984c1aad792e3144fb71c2d11
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
Python 3.5 introduced `bytes.hex` to represent bytes in hexadecimal:
https://docs.python.org/3.5/library/stdtypes.html#bytes.hex
This function directly returns a string, contrary to `binascii.hexlify`
which returns bytes. Using `binascii.hexlify` without transforming the
output to string actually produced buggy output. For example:
$ ./chipsec_util.py spidesc spi_rom.bin
...
+ 0x0000 Reserved : 0xb'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'
This `b'` is not desired. Using `bytes.hex` fixes this issue and makes
the code simpler.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
`Chipset.set_control` calls:
self.write_register_field(reg, field, control_value, cpu_thread)
But `write_register_field` is defined as:
def write_register_field(self, reg_name, field_name, field_value, preserve_field_position=False, cpu_thread=0):
So variable `cpu_thread` is given to parameter `preserve_field_position`
instead of parameter `cpu_thread`.
Fix this by using `cpu_thread=cpu_thread` syntax in the call.
This bug was found while adding type hints to `chipset`. Mypy reported:
Argument 4 to "write_register_field" of "Chipset" has incompatible type "int"; expected "bool"
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
This function was never used. Moreover it does not work with numbers
with an odd number of hexdigits because `binascii.unhexlify(f'{value:x}')`
fails with:
>>> hex_to_text(1)
binascii.Error: Odd-length string
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
`OsHelper.get_available_helpers` is returning a list of helper names in
the order they are stored in the dict `self.avail_helpers`. This order
seems to come from the order in which `os.listdir(helper_dir)` reads the
content of the directory.
To make this order more predictible and meaningful, sort the list in
`get_available_helpers`. This function was only called when generating
the documentation ofi option `--helper` and this makes it more
reproducible too.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
Using `{buf:8X}` does not work in `LinuxNativeHelper.write_msr`: this
raises an expection
TypeError: unsupported format string passed to bytes.__format__
Use `{buf.hex()}` instead to format the bytes in the error message.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
In tests, `read_phys_mem` could return strings instead of bytes, which
prevents detecting issues with `chipsec_util.py gdt` and
`chipsec_util.py idt`.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
`print_buffer_bytes` can directly handle bytes, instead of converting
them to a string.
While at it, replace buggy calls to `print_buffer(buffer)` (when
`buffer` uses type `bytes`) with `print_buffer_bytes(buffer)`.
This fixes `./chipsec_util.py idt 0`. Before it failed with:
[CHIPSEC] Dumping IDT of 8 CPU threads
[cpu0] Physical Address: 0x000000036639E000
[cpu0] # of entries : 256
[cpu0] Contents (4 entries):
Traceback (most recent call last):
File "./chipsec_util.py", line 210, in <module>
sys.exit(main())
File "./chipsec_util.py", line 205, in main
return chipsecMain.main()
File "./chipsec_util.py", line 190, in main
comm.run()
File "chipsec/utilcmd/desc_cmd.py", line 84, in run
self.cs.msr.IDT_all(4)
File "chipsec/hal/msr.py", line 166, in IDT_all
self.IDT(tid, num_entries)
File "chipsec/hal/msr.py", line 158, in IDT
return self.dump_Descriptor_Table(cpu_thread_id, DESCRIPTOR_TABLE_CODE_IDTR, num_entries)
File "chipsec/hal/msr.py", line 144, in dump_Descriptor_Table
print_buffer(dt)
File "chipsec/logger.py", line 493, in print_buffer
prt_str = bytes2string(arr, length)
File "chipsec/logger.py", line 466, in bytes2string
num_string += [f'{ord(c):02X} ']
TypeError: ord() expected string of length 1, but int found
There was another bug in `hal/msr.py` where `ord(dt[...])` was used
instead of `dt`.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
Using a list of unicode characters instead of bytes is strange in Python
3. Replace these lists with bytes, which is the usual type to hold
binary data.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
In `SPI.write_spi`, When `len(buf) % 4 != 0`, a dword value to be
written is computed with `ord(buf[...])`. This does not work, as in
Python 3, `buf[...]` directly gives an integer.
Fix this issue by removing the call to `ord`.
Fixes: https://github.com/chipsec/chipsec/issues/1775
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
`oshelper.UnimplementedAPIError` is actually imported from
`chipsec.exceptions`. Importing it directly fixes warnings reported by
Mypy such as:
chipsec/hal/spi.py:201: error: Module has no attribute "UnimplementedAPIError"
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
If the vendor ID or the device ID returned by `detect_platform()` is not
known in `chipset_dictionary`, accessing
`self.chipset_dictionary[vid][did]` raises a `KeyError` exception. To
prevent this, use the variables `vid_found` and `did_found` before
accessing `self.chipset_dictionary[vid][did]`.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>
Currently, `chipsec_util.py spidesc spi_rom.bin` displays:
Master Read/Write Access to Flash Regions
--------------------------------------------------------
Region | CPU | ME
--------------------------------------------------------
f{r:-2d} {spi.SPI_REGION_NAMES[r]:20s} | |
The `f` was not positioned correctly in the formatted string.
Signed-off-by: Nicolas Iooss <nicolas.iooss_git@polytechnique.org>