offensive-security / kali-arm-build-scripts

Kali Linux ARM build scripts
874 stars 374 forks source link

GPIO not working on Kali Linux RaspberryPi 3 64bit 2018.4a #144

Closed dingyifei closed 5 years ago

dingyifei commented 5 years ago

The RPi.GPIO is not working on Kali Linux RaspberryPi 3 64bit 2018.4a I install the image download from https://www.offensive-security.com/kali-linux-arm-images/ and when I try to import RPi.GPIO on python 3.7.1 and python3.6, I receive the following error:

root@kali:~# python3
Python 3.6.7 (default, Oct 21 2018, 08:08:16) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import RPi.GPIO
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/RPi/GPIO/__init__.py", line 23, in <module>
    from RPi._GPIO import *
RuntimeError: This module can only be run on a Raspberry Pi!
>>> 

Already tried reinstall RPi.GPIO I tried install wiringpi and when I use gpio readall I get the following error:

root@kali:~# gpio readall
Oops: Unable to determine board revision from /proc/cpuinfo
 -> No "Hardware" line
 ->  You'd best google the error to find out why.

Here are the cat /proc/cpuinfo

root@kali:~# cat /proc/cpuinfo
processor       : 0
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4

processor       : 1
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4

processor       : 2
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4

processor       : 3
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4
steev commented 5 years ago

Did you compile it yourself? Afaik, the packages have binaries that aren’t 64bit

dingyifei commented 5 years ago

I followed the procedure in http://wiringpi.com/download-and-install/ compiled WiringPi by clone and run the build script. I didn't compile the rpi.gpio-common... isn't rpi.gpio a python program? The RPi.GPIO isn't pre-installed in the image, I ran apt install rpi.gpio and installed it manually.

dingyifei commented 5 years ago

The rpi.gpio-common, python3-rpi.gpio, and python-rpi.gpio are all arm64 package base on apt search rpi.gpio result

digitallyelite commented 5 years ago

ah you're right - it looks like the 64bit package doesn't actually pass the check for RPI for some reason.

Can you give me the output of /proc/cpuinfo on yours?

Keep in mind, the RPi Foundation does not support running the RPi in 64bit mode at all, so we're gonna have to do a little leg work. This will likely have to be passed on to a debian bug as we (in kali) don't modify the package from Debian at all, but lets do the troubleshooting before we do so.

dingyifei commented 5 years ago

The raspberry pi I'm using is Raspberry Pi 3 B+ (the one with higher clock speed cpu and metal shielding on the processor) I tested the kali-linux-2018.4-rpi3-nexmon.img and it is working fine Here are the cat /proc/cpuinfo output:

root@kali:~# cat /proc/cpuinfo
processor       : 0
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4

processor       : 1
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4

processor       : 2
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4

processor       : 3
BogoMIPS        : 38.40
Features        : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant     : 0x0
CPU part        : 0xd03
CPU revision    : 4
digitallyelite commented 5 years ago

Right - Basically what we're looking at is https://sourceforge.net/p/raspberry-gpio-python/code/ci/default/tree/source/c_gpio.c - and seeing if we can massage it to see that we're an RPi - since I'm guessing we lack the Hardware: line in 64bit that is in 32bit.

dingyifei commented 5 years ago

Seems like the 64 bit build don't have some cpu features and the model name I get some completely different cpuinfo from the 32 bit kali cat /proc/cpuinfo:

model name  : ARMv7 Processor rev 4 (v7l)
BogoMIPS    : 38.40
Features    : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part    : 0xd03
CPU revision    : 4

processor   : 1
model name  : ARMv7 Processor rev 4 (v7l)
BogoMIPS    : 38.40
Features    : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part    : 0xd03
CPU revision    : 4

processor   : 2
model name  : ARMv7 Processor rev 4 (v7l)
BogoMIPS    : 38.40
Features    : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part    : 0xd03
CPU revision    : 4

processor   : 3
model name  : ARMv7 Processor rev 4 (v7l)
BogoMIPS    : 38.40
Features    : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part    : 0xd03
CPU revision    : 4

Hardware    : BCM2835
Revision    : a020d3
Serial      : 000000004be218c8
digitallyelite commented 5 years ago

Right, in 32bit, it has the Hardware/Revision/Serial stanza.

And the rpi.gpio package looks for that to tell if it's on an RPi or not.

As far as I know, there's no way to have the Hardware/Revision/Serial stanza in arm64, at least, not without someone patching the kernel to add it.

dingyifei commented 5 years ago

Im looking at the source code form https://pypi.org/project/RPi.GPIO/ maybe just set the value in it to BCM2835?

digitallyelite commented 5 years ago

you could hack it locally to always return true, yes, but unfortunately, that won't work for a debian package, since it can be installed on any (arm64) system.

dingyifei commented 5 years ago

I get it to work LOL The following are the modified \RPi.GPIO-0.6.5\source\cpuinfo.c

/*
Copyright (c) 2012-2016 Ben Croston

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cpuinfo.h"

int get_rpi_info(rpi_info *info)
{
    FILE *fp;
    char buffer[1024];
    char hardware[1024];
    char revision[1024];
    char *rev;
    int found = 0;
    int len;

    char fake_revision[1024] = "a020d3";
    char fake_hardware[1024] = "BCM2835";
    found = 1;

    if ((fp = fopen("/proc/cpuinfo", "r")) == NULL)
        return -1;
    while (!feof(fp) && fgets(buffer, sizeof(buffer), fp))
    {
        sscanf(buffer, "Hardware    : %s", fake_hardware);
        if (strcmp(fake_hardware, "BCM2708") == 0 ||
            strcmp(fake_hardware, "BCM2709") == 0 ||
            strcmp(fake_hardware, "BCM2835") == 0 ||
            strcmp(fake_hardware, "BCM2836") == 0 ||
            strcmp(fake_hardware, "BCM2837") == 0)
        {
            found = 1;
        }
        sscanf(buffer, "Revision    : %s", fake_revision);
    }

    fclose(fp);

    if (!found)
        return -1;

    if ((len = strlen(fake_revision)) == 0)
        return -1;

    if (len >= 6 && strtol((char[]){fake_revision[len - 6], 0}, NULL, 16) & 8)
    {
        // new scheme
        //info->rev = revision[len-1]-'0';
        strcpy(info->revision, fake_revision);
        switch (fake_revision[len - 2])
        {
        case '0':
            info->type = "Model A";
            info->p1_revision = 2;
            break;
        case '1':
            info->type = "Model B";
            info->p1_revision = 2;
            break;
        case '2':
            info->type = "Model A+";
            info->p1_revision = 3;
            break;
        case '3':
            info->type = "Model B+";
            info->p1_revision = 3;
            break;
        case '4':
            info->type = "Pi 2 Model B";
            info->p1_revision = 3;
            break;
        case '5':
            info->type = "Alpha";
            info->p1_revision = 3;
            break;
        case '6':
            info->type = "Compute";
            info->p1_revision = 0;
            break;
        case '8':
            info->type = "Pi 3 Model B";
            info->p1_revision = 3;
            break;
        case '9':
            info->type = "Zero";
            info->p1_revision = 3;
            break;
        default:
            info->type = "Unknown";
            info->p1_revision = 3;
            break;
        }
        switch (fake_revision[len - 4])
        {
        case '0':
            info->processor = "BCM2835";
            break;
        case '1':
            info->processor = "BCM2836";
            break;
        case '2':
            info->processor = "BCM2837";
            break;
        default:
            info->processor = "Unknown";
            break;
        }
        switch (fake_revision[len - 5])
        {
        case '0':
            info->manufacturer = "Sony";
            break;
        case '1':
            info->manufacturer = "Egoman";
            break;
        case '2':
            info->manufacturer = "Embest";
            break;
        case '4':
            info->manufacturer = "Embest";
            break;
        default:
            info->manufacturer = "Unknown";
            break;
        }
        switch (strtol((char[]){fake_revision[len - 6], 0}, NULL, 16) & 7)
        {
        case 0:
            info->ram = "256M";
            break;
        case 1:
            info->ram = "512M";
            break;
        case 2:
            info->ram = "1024M";
            break;
        default:
            info->ram = "Unknown";
            break;
        }
    }
    else
    {
        // old scheme
        info->ram = "Unknown";
        info->manufacturer = "Unknown";
        info->processor = "Unknown";
        info->type = "Unknown";
        strcpy(info->revision, fake_revision);

        // get last four characters (ignore preceeding 1000 for overvolt)
        if (len > 4)
            rev = (char *)&fake_revision + len - 4;
        else
            rev = fake_revision;

        if ((strcmp(rev, "0002") == 0) ||
            (strcmp(rev, "0003") == 0))
        {
            info->type = "Model B";
            info->p1_revision = 1;
            info->ram = "256M";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "0004") == 0)
        {
            info->type = "Model B";
            info->p1_revision = 2;
            info->ram = "256M";
            info->manufacturer = "Sony";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "0005") == 0)
        {
            info->type = "Model B";
            info->p1_revision = 2;
            info->ram = "256M";
            info->manufacturer = "Qisda";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "0006") == 0)
        {
            info->type = "Model B";
            info->p1_revision = 2;
            info->ram = "256M";
            info->manufacturer = "Egoman";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "0007") == 0)
        {
            info->type = "Model A";
            info->p1_revision = 2;
            info->ram = "256M";
            info->manufacturer = "Egoman";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "0008") == 0)
        {
            info->type = "Model A";
            info->p1_revision = 2;
            info->ram = "256M";
            info->manufacturer = "Sony";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "0009") == 0)
        {
            info->type = "Model A";
            info->p1_revision = 2;
            info->ram = "256M";
            info->manufacturer = "Qisda";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "000d") == 0)
        {
            info->type = "Model B";
            info->p1_revision = 2;
            info->ram = "512M";
            info->manufacturer = "Egoman";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "000e") == 0)
        {
            info->type = "Model B";
            info->p1_revision = 2;
            info->ram = "512M";
            info->manufacturer = "Sony";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "000f") == 0)
        {
            info->type = "Model B";
            info->p1_revision = 2;
            info->ram = "512M";
            info->manufacturer = "Qisda";
            info->processor = "BCM2835";
        }
        else if ((strcmp(rev, "0011") == 0) ||
                 (strcmp(rev, "0014") == 0))
        {
            info->type = "Compute Module";
            info->p1_revision = 0;
            info->ram = "512M";
            info->processor = "BCM2835";
        }
        else if (strcmp(rev, "0012") == 0)
        {
            info->type = "Model A+";
            info->p1_revision = 3;
            info->ram = "256M";
            info->processor = "BCM2835";
        }
        else if ((strcmp(rev, "0010") == 0) ||
                 (strcmp(rev, "0013") == 0))
        {
            info->type = "Model B+";
            info->p1_revision = 3;
            info->ram = "512M";
            info->processor = "BCM2835";
        }
        else
        { // don't know - assume revision 3 p1 connector
            info->p1_revision = 3;
        }
    }
    return 0;
}

/*

32 bits
NEW                   23: will be 1 for the new scheme, 0 for the old scheme
MEMSIZE             20: 0=256M 1=512M 2=1G
MANUFACTURER  16: 0=SONY 1=EGOMAN
PROCESSOR         12: 0=2835 1=2836
TYPE                   04: 0=MODELA 1=MODELB 2=MODELA+ 3=MODELB+ 4=Pi2 MODEL B 5=ALPHA 6=CM
REV                     00: 0=REV0 1=REV1 2=REV2

pi2 = 1<<23 | 2<<20 | 1<<12 | 4<<4 = 0xa01040

--------------------

SRRR MMMM PPPP TTTT TTTT VVVV

S scheme (0=old, 1=new)
R RAM (0=256, 1=512, 2=1024)
M manufacturer (0='SONY',1='EGOMAN',2='EMBEST',3='UNKNOWN',4='EMBEST')
P processor (0=2835, 1=2836 2=2837)
T type (0='A', 1='B', 2='A+', 3='B+', 4='Pi 2 B', 5='Alpha', 6='Compute Module')
V revision (0-15)

*/

Also I had modified the /gpiozero/pins/local.py

from __future__ import (
    unicode_literals,
    absolute_import,
    print_function,
    division,
    )
str = type('')

import io
import warnings
from collections import defaultdict
from threading import Lock

try:
    from spidev import SpiDev
except ImportError:
    SpiDev = None

from . import SPI
from .pi import PiFactory, PiPin, SPI_HARDWARE_PINS
from .spi import SPISoftwareBus
from ..devices import Device, SharedMixin
from ..output_devices import OutputDevice
from ..exc import DeviceClosed, PinUnknownPi, SPIInvalidClockMode

class LocalPiFactory(PiFactory):
    """
    Abstract base class representing pins attached locally to a Pi. This forms
    the base class for local-only pin interfaces
    (:class:`~gpiozero.pins.rpigpio.RPiGPIOPin`,
    :class:`~gpiozero.pins.rpio.RPIOPin`, and
    :class:`~gpiozero.pins.native.NativePin`).
    """
    pins = {}
    _reservations = defaultdict(list)
    _res_lock = Lock()

    def __init__(self):
        super(LocalPiFactory, self).__init__()
        self.spi_classes = {
            ('hardware', 'exclusive'): LocalPiHardwareSPI,
            ('hardware', 'shared'):    LocalPiHardwareSPIShared,
            ('software', 'exclusive'): LocalPiSoftwareSPI,
            ('software', 'shared'):    LocalPiSoftwareSPIShared,
            }
        # Override the reservations and pins dict to be this class' attributes.
        # This is a bit of a dirty hack, but ensures that anyone evil enough to
        # mix pin implementations doesn't try and control the same pin with
        # different backends
        self.pins = LocalPiFactory.pins
        self._reservations = LocalPiFactory._reservations
        self._res_lock = LocalPiFactory._res_lock

    def _get_revision(self):
        # Cache the result as we can reasonably assume it won't change during
        # runtime (this is LocalPin after all; descendents that deal with
        # remote Pis should inherit from Pin instead)
        with io.open('/proc/cpuinfo', 'r') as f:
            for line in f:
                line = "Revision    : a020d3"
                revision = line.split(':')[1].strip().lower()
                overvolted = revision.startswith('100')
                if overvolted:
                    revision = revision[-4:]
                return revision
        raise PinUnknownPi('unable to locate Pi revision in /proc/cpuinfo')

class LocalPiPin(PiPin):
    """
    Abstract base class representing a multi-function GPIO pin attached to the
    local Raspberry Pi.
    """
    pass

class LocalPiHardwareSPI(SPI, Device):
    def __init__(self, factory, port, device):
        self._port = port
        self._device = device
        self._interface = None
        if SpiDev is None:
            raise ImportError('failed to import spidev')
        super(LocalPiHardwareSPI, self).__init__()
        pins = SPI_HARDWARE_PINS[port]
        self.pin_factory.reserve_pins(
            self,
            pins['clock'],
            pins['mosi'],
            pins['miso'],
            pins['select'][device]
            )
        self._interface = SpiDev()
        self._interface.open(port, device)
        self._interface.max_speed_hz = 500000

    def close(self):
        if getattr(self, '_interface', None):
            self._interface.close()
        self._interface = None
        self.pin_factory.release_all(self)
        super(LocalPiHardwareSPI, self).close()

    @property
    def closed(self):
        return self._interface is None

    def __repr__(self):
        try:
            self._check_open()
            return 'SPI(port=%d, device=%d)' % (self._port, self._device)
        except DeviceClosed:
            return 'SPI(closed)'

    def transfer(self, data):
        """
        Writes data (a list of integer words where each word is assumed to have
        :attr:`bits_per_word` bits or less) to the SPI interface, and reads an
        equivalent number of words, returning them as a list of integers.
        """
        return self._interface.xfer2(data)

    def _get_clock_mode(self):
        return self._interface.mode

    def _set_clock_mode(self, value):
        self._interface.mode = value

    def _get_lsb_first(self):
        return self._interface.lsbfirst

    def _set_lsb_first(self, value):
        self._interface.lsbfirst = bool(value)

    def _get_select_high(self):
        return self._interface.cshigh

    def _set_select_high(self, value):
        self._interface.cshigh = bool(value)

    def _get_bits_per_word(self):
        return self._interface.bits_per_word

    def _set_bits_per_word(self, value):
        self._interface.bits_per_word = value

class LocalPiSoftwareSPI(SPI, OutputDevice):
    def __init__(self, factory, clock_pin, mosi_pin, miso_pin, select_pin):
        self._bus = None
        super(LocalPiSoftwareSPI, self).__init__(select_pin, active_high=False)
        try:
            self._clock_phase = False
            self._lsb_first = False
            self._bits_per_word = 8
            self._bus = SPISoftwareBus(clock_pin, mosi_pin, miso_pin)
        except:
            self.close()
            raise

    def _conflicts_with(self, other):
        # XXX Need to refine this
        return not (
            isinstance(other, LocalPiSoftwareSPI) and
            (self.pin.number != other.pin.number)
            )

    def close(self):
        if getattr(self, '_bus', None):
            self._bus.close()
        self._bus = None
        super(LocalPiSoftwareSPI, self).close()

    @property
    def closed(self):
        return self._bus is None

    def __repr__(self):
        try:
            self._check_open()
            return 'SPI(clock_pin=%d, mosi_pin=%d, miso_pin=%d, select_pin=%d)' % (
                self._bus.clock.pin.number,
                self._bus.mosi.pin.number,
                self._bus.miso.pin.number,
                self.pin.number)
        except DeviceClosed:
            return 'SPI(closed)'

    def transfer(self, data):
        with self._bus.lock:
            self.on()
            try:
                return self._bus.transfer(
                    data, self._clock_phase, self._lsb_first, self._bits_per_word)
            finally:
                self.off()

    def _get_clock_mode(self):
        with self._bus.lock:
            return (not self._bus.clock.active_high) << 1 | self._clock_phase

    def _set_clock_mode(self, value):
        if not (0 <= value < 4):
            raise SPIInvalidClockMode("%d is not a valid clock mode" % value)
        with self._bus.lock:
            self._bus.clock.active_high = not (value & 2)
            self._clock_phase = bool(value & 1)

    def _get_lsb_first(self):
        return self._lsb_first

    def _set_lsb_first(self, value):
        self._lsb_first = bool(value)

    def _get_bits_per_word(self):
        return self._bits_per_word

    def _set_bits_per_word(self, value):
        if value < 1:
            raise ValueError('bits_per_word must be positive')
        self._bits_per_word = int(value)

    def _get_select_high(self):
        return self.active_high

    def _set_select_high(self, value):
        with self._bus.lock:
            self.active_high = value
            self.off()

class LocalPiHardwareSPIShared(SharedMixin, LocalPiHardwareSPI):
    @classmethod
    def _shared_key(cls, factory, port, device):
        return (port, device)

class LocalPiSoftwareSPIShared(SharedMixin, LocalPiSoftwareSPI):
    @classmethod
    def _shared_key(cls, factory, clock_pin, mosi_pin, miso_pin, select_pin):
        return (select_pin,)