Recent

Author Topic: [Discussion] Expanding wiki for Raspberry Pi GPIO access  (Read 2682 times)

ccrause

  • Hero Member
  • *****
  • Posts: 970
[Discussion] Expanding wiki for Raspberry Pi GPIO access
« on: September 10, 2023, 11:34:32 am »
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

  • Hero Member
  • *****
  • Posts: 8035
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #1 on: September 10, 2023, 12:47:31 pm »
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
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

ccrause

  • Hero Member
  • *****
  • Posts: 970
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #2 on: September 23, 2023, 08:33:57 am »
Underlying 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
« Last Edit: October 05, 2023, 11:55:29 am by ccrause »

MarkMLl

  • Hero Member
  • *****
  • Posts: 8035
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #3 on: September 23, 2023, 09:07:46 am »
This 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
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

ccrause

  • Hero Member
  • *****
  • Posts: 970
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #4 on: September 23, 2023, 01:23:02 pm »
This 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

  • Hero Member
  • *****
  • Posts: 8035
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #5 on: September 23, 2023, 02:29:34 pm »
Can 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][+][-]
  1. (* This requires that the program be running with sufficient capabilities to be *)
  2. (* able to create a unix-domain socket in /var/run and to bind to a port < 1024,*)
  3. (* however immediately after these operations it goes to a lot of trouble to    *)
  4. (* relinquish as many privileges as possible.                                   *)
  5. (*                                                                              *)
  6. (*  *   If linked with gtk2, it is not possible to run setuid root but explicit *)
  7. (*      capabilities may be added during installation:                          *)
  8. (*                                                                              *)
  9. (*      # setcap CAP_DAC_OVERRIDE,CAP_NET_BIND_SERVICE,CAP_NET_RAW=p+e *gtk2    *)
  10. (*                                                                              *)
  11. (*      Note that capabilities are stored as extended attributes, which DO NOT  *)
  12. (*      normally accompany a file if it is subsequently copied.                 *)
  13. (*                                                                              *)
  14. (*  *   If linked with Qt, the program may be run setuid root:                  *)
  15. (*                                                                              *)
  16. (*      # chown root:root *qt                                                   *)
  17. (*      # chmod u+s *qt                                                         *)
  18. (*      # chmod g+s *qt                                                         *)
  19. (*                                                                              *)
  20. (*      or have extra capabilities as above.                                    *)
  21. (*                                                                              *)
  22. (*  *   In any case, the program may be started by the superuser (i.e. run as   *)
  23. (*      root).                                                                  *)
  24. (*                                                                              *)
  25. (*  *   It may be debugged by being run like                                    *)
  26. (*                                                                              *)
  27. (*      # gdbserver :2345 ./Watchxxx-x86_64-linux-gtk2                          *)
  28. (*                                                                              *)
  29. (*      with the Lazarus IDE debugger backend set to remote/gdbserver.          *)
  30. (*                                                                              *)
  31. (* After the ports have been bound, the CAP_DAC_OVERRIDE, CAP_NET_BIND_SERVICE  *)
  32. (* and CAP_NET_RAW permitted and effective capabilities are relinquished. If    *)
  33. (* the program is running setuid root, then it reverts to the actual user; if   *)
  34. (* it is running as root it assumes group and user IDs as given by the          *)
  35. (* ownership of the executable, or ID 65534 as ultimate fallback ("nobody" in   *)
  36. (* recent Debian releases).                                     MarkMLl.        *)
  37.  

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][+][-]
  1. (* Relinquish the capabilities which allowed a non-root user to create a socket
  2.   in /var/run or with a port number < 1024.
  3. *)
  4. procedure RelinquishCapabilities;
  5.  
  6. var
  7.   cap: boolean;
  8.  
  9. begin
  10.  
  11. (* When not running as root, relinquish any capabilities we've been granted.    *)
  12. (* Even if running as root do this silently, to allow for the case where a      *)
  13. (* capability has been explicitly added. Note that I'm avoiding "permissions"   *)
  14. (* etc. here as ambiguous, I don't think there's any need to translate          *)
  15. (* "capability" etc. in this context.                                           *)
  16.  
  17. (* WARNING: visibility of capabilities might be modified if running under the   *)
  18. (* control of the debugger. Always test outside the debugger before jumping to  *)
  19. (* any conclusions.                                                             *)
  20.  
  21.   if (FpGetgid <> 0) or (FpGetuid <> 0) then begin      (* Give up capability   *)
  22.     if GetCapability(cap, CAP_DAC_OVERRIDE) then
  23.       if cap then begin
  24.         Write(StdErr, 'Relinquishing CAP_DAC_OVERRIDE capability... ');
  25.         if SetCapability(false, CAP_DAC_OVERRIDE) then
  26.           WriteLn(StdErr, 'OK')
  27.         else
  28.           WriteLn(StdErr, 'failed')
  29.       end else begin end;
  30.     if GetCapability(cap, CAP_DAC_OVERRIDE, CAP_PERMITTED) then
  31.       if cap then begin
  32.         Write(StdErr, 'Relinquishing CAP_DAC_OVERRIDE permittivity... ');
  33.         if SetCapability(false, CAP_DAC_OVERRIDE, CAP_PERMITTED) then
  34.           WriteLn(StdErr, 'OK')
  35.         else
  36.           WriteLn(StdErr, 'failed')
  37.       end else begin end;
  38.     if GetCapability(cap, CAP_NET_BIND_SERVICE) then
  39.       if cap then begin
  40.         Write(StdErr, 'Relinquishing NET_BIND_SERVICE capability... ');
  41.         if SetCapability(false, CAP_NET_BIND_SERVICE) then
  42.           WriteLn(StdErr, 'OK')
  43.         else
  44.           WriteLn(StdErr, 'failed')
  45.       end else begin end;
  46.     if GetCapability(cap, CAP_NET_BIND_SERVICE, CAP_PERMITTED) then
  47.       if cap then begin
  48.         Write(StdErr, 'Relinquishing NET_BIND_SERVICE permittivity... ');
  49.         if SetCapability(false, CAP_NET_BIND_SERVICE, CAP_PERMITTED) then
  50.           WriteLn(StdErr, 'OK')
  51.         else
  52.           WriteLn(StdErr, 'failed')
  53.       end else begin end;
  54.     if GetCapability(cap, CAP_NET_RAW) then
  55.       if cap then begin
  56.         Write(StdErr, 'Relinquishing NET_RAW capability... ');
  57.         if SetCapability(false, CAP_NET_RAW) then
  58.           WriteLn(StdErr, 'OK')
  59.         else
  60.           WriteLn(StdErr, 'failed')
  61.       end else begin end;
  62.     if GetCapability(cap, CAP_NET_RAW, CAP_PERMITTED) then
  63.       if cap then begin
  64.         Write(StdErr, 'Relinquishing NET_RAW permittivity... ');
  65.         if SetCapability(false, CAP_NET_RAW, CAP_PERMITTED) then
  66.           WriteLn(StdErr, 'OK')
  67.         else
  68.           WriteLn(StdErr, 'failed')
  69.       end else begin end
  70.   end else begin
  71.     SetCapability(false, CAP_DAC_OVERRIDE);
  72.     SetCapability(false, CAP_DAC_OVERRIDE, CAP_PERMITTED);
  73.     SetCapability(false, CAP_NET_BIND_SERVICE);
  74.     SetCapability(false, CAP_NET_BIND_SERVICE, CAP_PERMITTED);
  75.     SetCapability(false, CAP_NET_RAW);
  76.     SetCapability(false, CAP_NET_RAW, CAP_PERMITTED)
  77.   end
  78. end { RelinquishCapabilities } ;
  79.  

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/topic,53541.msg396133.html#msg396133 discusses setting a binary's capabilities from the IDE.

MarkMLl
« Last Edit: October 05, 2023, 10:41:29 am by MarkMLl »
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

ccrause

  • Hero Member
  • *****
  • Posts: 970
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #6 on: October 05, 2023, 03:24:49 pm »
Can 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:
<snip>

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

  • Hero Member
  • *****
  • Posts: 8035
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #7 on: October 05, 2023, 06:47:42 pm »
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.

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
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 16187
  • Censorship about opinions does not belong here.
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #8 on: October 06, 2023, 09:05:46 am »
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.
« Last Edit: October 06, 2023, 09:07:27 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

ccrause

  • Hero Member
  • *****
  • Posts: 970
Re: [Discussion] Expanding wiki for Raspberry Pi GPIO access
« Reply #9 on: October 06, 2023, 09:49:56 am »
I 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?

 

TinyPortal © 2005-2018