github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/interfaces/builtin/uio.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package builtin
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  	"strings"
    26  
    27  	"github.com/snapcore/snapd/interfaces"
    28  	"github.com/snapcore/snapd/interfaces/apparmor"
    29  	"github.com/snapcore/snapd/interfaces/udev"
    30  	"github.com/snapcore/snapd/snap"
    31  )
    32  
    33  // https://www.kernel.org/doc/html/latest/driver-api/uio-howto.html
    34  const uioSummary = `allows access to specific uio device`
    35  
    36  const uioBaseDeclarationSlots = `
    37    uio:
    38      allow-installation:
    39        slot-snap-type:
    40          - core
    41          - gadget
    42      deny-auto-connection: true
    43  `
    44  
    45  type uioInterface struct{}
    46  
    47  func (iface *uioInterface) Name() string {
    48  	return "uio"
    49  }
    50  
    51  func (iface *uioInterface) StaticInfo() interfaces.StaticInfo {
    52  	return interfaces.StaticInfo{
    53  		Summary:              uioSummary,
    54  		BaseDeclarationSlots: uioBaseDeclarationSlots,
    55  	}
    56  }
    57  
    58  var uioPattern = regexp.MustCompile(`^/dev/uio[0-9]+$`)
    59  
    60  const invalidUioDeviceNodeSlotPathErrFmt = "slot %q path attribute must be a valid UIO device node"
    61  
    62  func (iface *uioInterface) path(slotRef *interfaces.SlotRef, attrs interfaces.Attrer) (string, error) {
    63  	return verifySlotPathAttribute(slotRef, attrs, uioPattern, invalidUioDeviceNodeSlotPathErrFmt)
    64  }
    65  
    66  func (iface *uioInterface) BeforePrepareSlot(slot *snap.SlotInfo) error {
    67  	_, err := verifySlotPathAttribute(&interfaces.SlotRef{Snap: slot.Snap.InstanceName(), Name: slot.Name}, slot, uioPattern, invalidUioDeviceNodeSlotPathErrFmt)
    68  	return err
    69  }
    70  
    71  func (iface *uioInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
    72  	path, err := iface.path(slot.Ref(), slot)
    73  	if err != nil {
    74  		return nil
    75  	}
    76  	spec.AddSnippet(fmt.Sprintf("%s rw,", path))
    77  	// Assuming sysfs_base is /sys/class/uio/uio[0-9]+ where the leaf directory
    78  	// name matches /dev/uio[0-9]+ device name, the following files exists or
    79  	// may exist:
    80  	//  - $sysfs_base/{name,version,event}
    81  	//  - $sysfs_base/maps/map[0-9]+/{addr,name,offset,size}
    82  	//  - $sysfs_base/portio/port[0-9]+/{name,start,size,porttype}
    83  	// The expression below matches them all as they all may be required for
    84  	// userspace drivers to operate.
    85  	//
    86  	// While it is more accurate to use:
    87  	//
    88  	//   "/sys/devices/platform/**/uio/%s/** r,", strings.TrimPrefix(path, "/dev/")
    89  	//
    90  	// multiple interface connections will result in overlapping deep
    91  	// globs of the form:
    92  	//
    93  	//   /sys/devices/platform/**/uio/uio1/** r,
    94  	//   /sys/devices/platform/**/uio/uio2/** r,
    95  	//   /sys/devices/platform/**/uio/uioN/** r,
    96  	//
    97  	// which are computationally difficult to de-duplicate provided
    98  	// large enough N. Instead, grant read only access to all uio
    99  	// sysfs files and control writable access to the specific
   100  	// device node in /dev. Use AddDeduplicatedSnippet() for clarity
   101  	// in the resulting rules.
   102  	spec.AddDeduplicatedSnippet("/sys/devices/platform/**/uio/uio[0-9]** r,  # common rule for all uio connections")
   103  	return nil
   104  }
   105  
   106  func (iface *uioInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error {
   107  	path, err := iface.path(slot.Ref(), slot)
   108  	if err != nil {
   109  		return nil
   110  	}
   111  	spec.TagDevice(fmt.Sprintf(`SUBSYSTEM=="uio", KERNEL=="%s"`, strings.TrimPrefix(path, "/dev/")))
   112  	return nil
   113  }
   114  
   115  func (iface *uioInterface) AutoConnect(*snap.PlugInfo, *snap.SlotInfo) bool {
   116  	// Allow what is allowed in the declarations
   117  	return true
   118  }
   119  
   120  func init() {
   121  	registerIface(&uioInterface{})
   122  }