[Discussion] Expanding wiki for Raspberry Pi GPIO access lazarus / 32-Linux ---------------------------------------------------------------------- ccrause · 2023-09-10 09:34 I recently starting playing with GPIO pins on Raspberry Pi.  This prompted studying the relevant wiki entry for this topic.  This is an excellent source of information, which I think can be improved further by a high level discussion on the different methods available, pro's and cons and so forth.  Before pasting my poorly informed opinion in the wiki, I would like to gather the opinions of expert users in this field on my proposed additions. Below some points for discussion: Native vs. library Native requires no extra libraries (other than already included by the OS distribution).  The main advantage is no external dependencies, therefore easy to deploy.Native may look complicated because of the low level details that may be required. A well designed library can implement the boiler plate with tried and tested code, leaving the user with a clean and simple interface to work through. Underlying access method Direct hardware register access - fast, require root access, bypass kernel access control. Not portable between different hardware.sysfs (deprecated since v4.8 but still available) - Simple file based calls, have drawbacks e.g. can only access a single pin per call, potential race conditions etc [1].Shell calls - using sysfs but using shell commands to read/write data via files.  Good for scripting, but extra indirection for programming.chardev (v1, deprecated but still available) - Open handle to hardware chip, then interact with pins via IOCTL calls. Can access multiple pins in a single request, set open source/open drain mode and bias of pin (pullup, pulldown) on pins [2].chardev (v2) - Fix padding between 32 bit user space and 64 bit kernel space. Merge some functionality into one call [2]. Any criticism, builds, corrections or additions are welcomed. [1] https://blog.lxsang.me/post/id/33 [2] https://elinux.org/images/c/cb/Linux_GPIO- Evolution_and_Current_State_of_the_User_API.pdf ---------------------------------------------------------------------- MarkMLl · 2023-09-10 10:47 To reinforce what you've said about direct hardware access, I suspect that there might be portability issues if anything other than Raspbian (or whatever it's called these days) is used, not to mention other "not quite RPi" boards even if they are superficially OS-compatible. Also you /can/ use GPIO devices on a standard PC with suitable expansion hardware. Apropos the chardev stuff and in particular v2 and with a nod to the thread I cited a few days ago, I need to revisit that at some point but it's not going to be for at least a few weeks. MarkMLl ---------------------------------------------------------------------- ccrause · 2023-09-23 06:33 Quote from: ccrause on September 10, 2023, 11:34:32 amUnderlying access method Direct hardware register access - fast, require root access, bypass kernel access control. Not portable between different hardware.This isn't entirely true.  Root access is required if accessing the GPIO memory via /dev/mem but any user that is part of the gpio group can access it through /dev/gpiomem. Access control is handled by the mmap call, with a resolution of a memory page (typically 4096 bytes).  This means that access control is coarse grained and can only be used to share or reserve access to all the GPIO registers, not individual pins. Are there any more subtleties to mention? Edit: spelling ---------------------------------------------------------------------- MarkMLl · 2023-09-23 07:07 Quote from: ccrause on September 23, 2023, 08:33:57 amThis isn't entirely true.  Root access is required if accessing the GPIO memory via /dev/mem That isn't entirely true either :-) You can- and should- use POSIX capabilities in preference to granting a program carte blanche access. Unfortunately Lazarus doesn't make it easy to set that, I think I submitted a patch back in the Mantis days that improved the situation but GitLab seems to have forgotten all about me. MarkMLl ---------------------------------------------------------------------- ccrause · 2023-09-23 11:23 Quote from: MarkMLl on September 23, 2023, 09:07:46 amQuote from: ccrause on September 23, 2023, 08:33:57 amThis isn't entirely true.  Root access is required if accessing the GPIO memory via /dev/mem That isn't entirely true either :-) You can- and should- use POSIX capabilities in preference to granting a program carte blanche access. Unfortunately Lazarus doesn't make it easy to set that, I think I submitted a patch back in the Mantis days that improved the situation but GitLab seems to have forgotten all about me. Can you remember the principle / system call involved? ---------------------------------------------------------------------- MarkMLl · 2023-09-23 12:29 Quote from: ccrause on September 23, 2023, 01:23:02 pmCan you remember the principle / system call involved? I've not got an immediately-accessible note of the capabilities needed for GPIO access, and am in the middle of something moderately urgent right now. However in order to start a daemon listening on a low- numbered port something like the notes below would be relevant: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i (* This requires that the program be running with sufficient capabilities to be *)(* able to create a unix-domain socket in /var/run and to bind to a port < 1024,*)(* however immediately after these operations it goes to a lot of trouble to    *)(* relinquish as many privileges as possible.                                   *)(*                                                                            *)(*  *   If linked with gtk2, it is not possible to run setuid root but explicit *)(*      capabilities may be added during installation:                        *)(*                                                                            *)(*      # setcap CAP_DAC_OVERRIDE,CAP_NET_BIND_SERVICE,CAP_NET_RAW=p+e *gtk2    *)(*                                                                          *)(*      Note that capabilities are stored as extended attributes, which DO NOT  *)(*      normally accompany a file if it is subsequently copied.                 *)(*                                                                            *)(*  *   If linked with Qt, the program may be run setuid root:                  *)(*                                                                          *)(*      # chown root:root *qt                                                 *)(*      # chmod u+s *qt                                                       *)(*      # chmod g+s *qt                                                       *)(*                                                                            *)(*      or have extra capabilities as above.                                    *)(*                                                                            *)(*  *   In any case, the program may be started by the superuser (i.e. run as   *)(*      root).                                                                *)(*                                                                            *)(*  *   It may be debugged by being run like                                    *)(*                                                                            *)(*      # gdbserver :2345 ./Watchxxx-x86_64-linux-gtk2                        *)(*                                                                            *)(*      with the Lazarus IDE debugger backend set to remote/gdbserver.          *)(*                                                                            *)(* After the ports have been bound, the CAP_DAC_OVERRIDE, CAP_NET_BIND_SERVICE  *)(* and CAP_NET_RAW permitted and effective capabilities are relinquished. If  *)(* the program is running setuid root, then it reverts to the actual user; if   *)(* it is running as root it assumes group and user IDs as given by the          *)(* ownership of the executable, or ID 65534 as ultimate fallback ("nobody" in   *)(* recent Debian releases).                                     MarkMLl.        *)  Once you'd completed whatever needed the enhanced access (i.e. at the same point where you'd traditionally relinquish privileged uid and gid) you'd use something like Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i (* Relinquish the capabilities which allowed a non-root user to create a socket  in /var/run or with a port number < 1024.*)procedure RelinquishCapabilities; var  cap: boolean; begin (* When not running as root, relinquish any capabilities we've been granted.    *)(* Even if running as root do this silently, to allow for the case where a    *)(* capability has been explicitly added. Note that I'm avoiding "permissions"   *)(* etc. here as ambiguous, I don't think there's any need to translate          *)(* "capability" etc. in this context.                                         *) (* WARNING: visibility of capabilities might be modified if running under the   *)(* control of the debugger. Always test outside the debugger before jumping to  *)(* any conclusions.                                                           *)   if (FpGetgid <> 0) or (FpGetuid <> 0) then begin      (* Give up capability   *)    if GetCapability(cap, CAP_DAC_OVERRIDE) then      if cap then begin        Write(StdErr, 'Relinquishing CAP_DAC_OVERRIDE capability... ');        if SetCapability(false, CAP_DAC_OVERRIDE) then          WriteLn(StdErr, 'OK')        else        WriteLn(StdErr, 'failed')      end else begin end;    if GetCapability(cap, CAP_DAC_OVERRIDE, CAP_PERMITTED) then      if cap then begin        Write(StdErr, 'Relinquishing CAP_DAC_OVERRIDE permittivity... ');        if SetCapability(false, CAP_DAC_OVERRIDE, CAP_PERMITTED) then          WriteLn(StdErr, 'OK')        else        WriteLn(StdErr, 'failed')      end else begin end;    if GetCapability(cap, CAP_NET_BIND_SERVICE) then      if cap then begin      Write(StdErr, 'Relinquishing NET_BIND_SERVICE capability... ');      if SetCapability(false, CAP_NET_BIND_SERVICE) then          WriteLn(StdErr, 'OK')        else          WriteLn(StdErr, 'failed')    end else begin end;    if GetCapability(cap, CAP_NET_BIND_SERVICE, CAP_PERMITTED) then      if cap then begin        Write(StdErr, 'Relinquishing NET_BIND_SERVICE permittivity... ');        if SetCapability(false, CAP_NET_BIND_SERVICE, CAP_PERMITTED) then        WriteLn(StdErr, 'OK')        else          WriteLn(StdErr, 'failed')      end else begin end;    if GetCapability(cap, CAP_NET_RAW) then    if cap then begin        Write(StdErr, 'Relinquishing NET_RAW capability... ');        if SetCapability(false, CAP_NET_RAW) then        WriteLn(StdErr, 'OK')        else          WriteLn(StdErr, 'failed')      end else begin end;    if GetCapability(cap, CAP_NET_RAW, CAP_PERMITTED) then      if cap then begin        Write(StdErr, 'Relinquishing NET_RAW permittivity... ');        if SetCapability(false, CAP_NET_RAW, CAP_PERMITTED) then          WriteLn(StdErr, 'OK')        else          WriteLn(StdErr, 'failed')    end else begin end  end else begin    SetCapability(false, CAP_DAC_OVERRIDE);    SetCapability(false, CAP_DAC_OVERRIDE, CAP_PERMITTED);    SetCapability(false, CAP_NET_BIND_SERVICE);    SetCapability(false, CAP_NET_BIND_SERVICE, CAP_PERMITTED);    SetCapability(false, CAP_NET_RAW);    SetCapability(false, CAP_NET_RAW, CAP_PERMITTED)  endend { RelinquishCapabilities } ;  I really can't remember the details, but I think I submitted a Linux- specific patch to allow the "post-build" command to be run as root, using the same prompt mechanism as SSH etc. I'll try to come back to this and be more definite when I have a bit of time. Updated: Still got my hands very full of things, but see https://github.com/MarkMLl/telnetsrv/blob/main/telnetprivs.pas hence https://github.com/MarkMLl/telnetsrv/blob/main/libcap.pas which uses either static or dynamic linkage. Upupdated: Thread at https://forum.lazarus.freepascal.org/index.php/to pic,53541.msg396133.html#msg396133 discusses setting a binary's capabilities from the IDE. MarkMLl ---------------------------------------------------------------------- ccrause · 2023-10-05 13:24 Quote from: MarkMLl on September 23, 2023, 02:29:34 pmQuote from: ccrause on September 23, 2023, 01:23:02 pmCan you remember the principle / system call involved? I've not got an immediately-accessible note of the capabilities needed for GPIO access, and am in the middle of something moderately urgent right now. However in order to start a daemon listening on a low- numbered port something like the notes below would be relevant: Thanks for the pointers Mark.  I noticed FPC provide capget/capset functionality in the linux unit.  This at can be used to check whether the current thread has the CAP_SYS_RAWIO capability, and act accordingly. ---------------------------------------------------------------------- MarkMLl · 2023-10-05 16:47 Quote from: ccrause on October 05, 2023, 03:24:49 pmThanks for the pointers Mark.  I noticed FPC provide capget/capset functionality in the linux unit.  This at can be used to check whether the current thread has the CAP_SYS_RAWIO capability, and act accordingly. My recollection is that capabilities are set "en masse" by simply running as root etc., hence may be individually checked. Today's kerfuffle about bugs in glibc's loader https://www.qualys.com/2023/10/03/cve-2023-4911/looney-tunables-local- privilege-escalation-glibc-ld-so.txt is very likely to have the effect of persuading various execution environments to further deprecate programs marked setuid root, in favour of carefully-chosen capabilities and SE-Linux rules. Actually applying these is going to be a problem... MarkMLl ---------------------------------------------------------------------- Thaddy · 2023-10-06 07:05 I use the virtual address of the GPIO (differs from the real address). Caveat: this also differs per RPi model. If you use that, you can use the bit manipulation helpers from sysutils. I actually wrote those with a.o. GPIO manipulation in mind and use it quite often. Although I run it mostly as root, accessing the GPIO through its virtual address need not have root. The addresses can be obtained from the model's technical documentation. ---------------------------------------------------------------------- ccrause · 2023-10-06 07:49 Quote from: Thaddy on October 06, 2023, 09:05:46 amI use the virtual address of the GPIO (differs from the real address). Caveat: this also differs per RPi model. Are you using fpmmap? Do you have example code for me to inspect?