
Privilege Controlled Memory
---------------------------
This is the modularised version of the "Privilege Controlled Memory" kernel
hack for Linux.  PCMEM is used to give userspace programs access to
specific areas of your machine's memory & hardware without compromising system
security.  This is achieved by allowing privileged programs that run as
root or suid to map out user accessible memory areas with permission based
access restrictions.  Once this is done, non-root user space programs can map
those memory areas (if they pass the necessary permission tests).

PCMEM is mostly intended for creating very fast, SECURE user-space hardware
libraries on Linux, removing the need for hardware drivers that run as 
privileged services (as X11 does).  PCMEM was originally designed for programs 
requiring direct graphics card access, but could be useful for just about
anything requiring direct, safe hardware access.

Overall this gives us an important security improvement to the kernel, as
user programs that may have previously required all-or-nothing root access for
hardware interaction may now use PCMEM instead.  Hurrah :)


Compiling PCMEM
---------------
Source code compilation is designed for kernel 2.6 by default.  You may
have to fool around with the makefile for earlier kernel releases.

You'll need your original kernel source installed prior to compilation.  Use
the following command to compile the PCMEM module for kernel 2.6:

  make KERNDIR=/path/to/kernel/src
  
For kernel 2.4, e.g. Red Hat 9:

  make pcmem.o KERNDIR=/path/to/kernel/src  

This will produce a pcmem.ko file for kernel 2.6, or pcmem.o for earlier
kernels.  Copy this file to /lib/modules/*/kernel/drivers/char/ to install
it.  Then run:

  depmod -A
  modprobe pcmem

If you want to check whether or not the kernel could load the module,
type 'dmesg'.

If you aren't using DevFS, you must create the pcmem device under /dev/ with
the following:

  mknod /dev/pcmem c 240 12
  chmod ugo+rw /dev/pcmem

Please test the PCMEM installation by running the following - it will test 
PCMEM's security to ensure correctness:

  make test
  ./test

You'll want the PCMEM module to load automatically when your system boots up.  
This is distro dependent, but most systems have an /etc/modules file that you
can add the line "pcmem" to.  Otherwise, find a way to add the line 
"modprobe pcmem" to your startup sequence.  Afterwards you can check if the 
module has loaded on bootup with the "lsmod" command.


Developers: Using PCMEM
-----------------------
The PCMEM functionality can only be utilised by opening /dev/pcmem as a file
and sending it ioctl messages.  There are only 3 ioctl's available, one of
which is intended for debugging.

   MMIO_PERMIT_RANGE - For permitting public access to a memory range.
   MMIO_REMOVE_RANGE - For removing public access to a memory range.
   MMIO_REMOVE_ALL   - Closes all open memory ranges.

All ioctl's require superuser priviliges.  The only non-privileged
functionality in PCMEM is in its support for mmap().

To permit access to an address range, use this code:

   struct mm_permit permit;

   permit.start = start_address;
   permit.end   = end_address;
   permit.mode  = S_IRWXU | permissions...;
   ioctl(pcmem_fd, MMIO_PERMIT_RANGE, &permit);

To remove access to an address range, use this code:

   remove.start = start_address;
   remove.end   = end_address;
   ioctl(pcmem_fd, MMIO_PERMIT_RANGE, &remove);

To map a pcmem memory range in a user program, open /dev/pcmem and use it in
the same way as you would use /dev/mem.  Please see the mmap() man page for
details.  99% of the time where you cannot get access to an address range, it
is probably not mapped as accessible, or the program does not have sufficient
permisssions (EACCES will be the error code).

This example demonstrates how you would declare the interrupt vectors and BIOS
information in the 0x0 - 0x502 address range as publicly accessible to members
of the program's user group.  It calls MMIO_PERMIT_RANGE to declare the memory
as accessible and then maps the memory region to test it.  This code makes a
PCMEM ioctl() call, so it will only work if run as root.

   struct mm_permit permit;
   LONG pcmem

   if ((pcmem = open("/dev/pcmem", O_RDWR)) != -1) {
      permit.start = 0;
      permit.end   = 0x502;
      permit.mode  = S_IRWXU | S_IRWXG;
      ioctl(glMemFD, MMIO_PERMIT_RANGE, &permit);

      if ((mmap((APTR)permit.start, permit.end - permit.start,
            PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, pcmem,
            permit.start)) IS (APTR)-1) {
         printf("You must have root privileges to run this program!\n");
         printf("Failed to map\naddress range $%.8x - $%.8x", permit.start, permit.end);
      }
   }

See the test program for a working example.
   

About PCMEM
-----------
The PCMEM device code is written by Paul Manias of Rocklyte Systems,
pmanias@rocklyte.com.  This file was last updated 19 July 2004. 
