]> Zhao Yanbai Git Server - minix.git/commitdiff
Changed do_devio not to require DIO_TYPE, but to extract type
authorBen Gras <ben@minix3.org>
Tue, 20 Jun 2006 10:03:10 +0000 (10:03 +0000)
committerBen Gras <ben@minix3.org>
Tue, 20 Jun 2006 10:03:10 +0000 (10:03 +0000)
from DIO_REQUEST. Also do_vdevio. Also do_sdevio, but this
function also supports grant id's and offsets.

do_segctl: rename protected to prot.

do_umap: support for GRANT_SEG umap.

do_privctl: support SYS_PRIV_SET_GRANTS, which sets location and size
of in-own-address-space grant table.

do_safecopy: functions to verify and perform 'safe' (grant-based) copies.

kernel/system/Makefile
kernel/system/do_devio.c
kernel/system/do_privctl.c
kernel/system/do_safecopy.c [new file with mode: 0644]
kernel/system/do_sdevio.c
kernel/system/do_segctl.c
kernel/system/do_umap.c
kernel/system/do_vdevio.c

index 8d18fbefadb542cffa6a6f19e635b1087b31b211..2d82716501ff44a8b9e4c8a68a6ae648b3d5e50d 100644 (file)
@@ -37,6 +37,7 @@ OBJECTS       = \
        $(SYSTEM)(do_memset.o) \
        $(SYSTEM)(do_privctl.o) \
        $(SYSTEM)(do_segctl.o) \
+       $(SYSTEM)(do_safecopy.o) \
        $(SYSTEM)(do_getksig.o) \
        $(SYSTEM)(do_endksig.o) \
        $(SYSTEM)(do_kill.o) \
@@ -139,6 +140,9 @@ $(SYSTEM)(do_abort.o):      do_abort.c
 $(SYSTEM)(do_privctl.o):       do_privctl.c
        $(CC) do_privctl.c
 
+$(SYSTEM)(do_safecopy.o):      do_safecopy.c
+       $(CC) do_safecopy.c
+
 $(SYSTEM)(do_segctl.o):        do_segctl.c
        $(CC) do_segctl.c
 
index a1da1a2749e1b46d896779f446cb35a53cb7a6f3..9c98bba00ece34325c3f34f5b7f9edcd9e8ce38f 100644 (file)
@@ -3,7 +3,6 @@
  *
  * The parameters for this kernel call are:
  *   m2_i3:    DIO_REQUEST     (request input or output)       
- *   m2_i1:    DIO_TYPE        (flag indicating byte, word, or long)
  *   m2_l1:    DIO_PORT        (port to read/ write)   
  *   m2_l2:    DIO_VALUE       (value to write/ return value read)     
  */
@@ -25,6 +24,10 @@ register message *m_ptr;     /* pointer to request message */
     port_t port;
     struct io_range *iorp;
     int i, size, nr_io_range;
+    int io_type, io_dir;
+
+    io_type = m_ptr->DIO_REQUEST & _DIO_TYPEMASK;
+    io_dir  = m_ptr->DIO_REQUEST & _DIO_DIRMASK;
 
     rp= proc_addr(who_p);
     privp= priv(rp);
@@ -35,11 +38,11 @@ register message *m_ptr;    /* pointer to request message */
     }
     if (privp->s_flags & CHECK_IO_PORT)
     {
-       switch (m_ptr->DIO_TYPE)
+       switch (io_type)
        {
-       case DIO_BYTE: size= 1; break;
-       case DIO_WORD: size= 2; break;
-       case DIO_LONG: size= 4; break;
+       case _DIO_BYTE: size= 1; break;
+       case _DIO_WORD: size= 2; break;
+       case _DIO_LONG: size= 4; break;
        default: size= 4; break;        /* Be conservative */
        }
        port= m_ptr->DIO_PORT;
@@ -61,18 +64,18 @@ register message *m_ptr;    /* pointer to request message */
 doit:
 
 /* Process a single I/O request for byte, word, and long values. */
-    if (m_ptr->DIO_REQUEST == DIO_INPUT) { 
-      switch (m_ptr->DIO_TYPE) {
-        case DIO_BYTE: m_ptr->DIO_VALUE = inb(m_ptr->DIO_PORT); break; 
-        case DIO_WORD: m_ptr->DIO_VALUE = inw(m_ptr->DIO_PORT); break; 
-        case DIO_LONG: m_ptr->DIO_VALUE = inl(m_ptr->DIO_PORT); break; 
+    if (io_dir == _DIO_INPUT) { 
+      switch (io_type) {
+        case _DIO_BYTE: m_ptr->DIO_VALUE = inb(m_ptr->DIO_PORT); break; 
+        case _DIO_WORD: m_ptr->DIO_VALUE = inw(m_ptr->DIO_PORT); break; 
+        case _DIO_LONG: m_ptr->DIO_VALUE = inl(m_ptr->DIO_PORT); break; 
        default: return(EINVAL);
       } 
     } else { 
-      switch (m_ptr->DIO_TYPE) {
-        case DIO_BYTE: outb(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;  
-        case DIO_WORD: outw(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;  
-        case DIO_LONG: outl(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;  
+      switch (io_type) {
+        case _DIO_BYTE: outb(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;  
+        case _DIO_WORD: outw(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;  
+        case _DIO_LONG: outl(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break;  
        default: return(EINVAL);
       } 
     }
index e01ff2333f4d0e51ad175e054ec060871ee3d19b..c567c8a263814768912ecfb221a34317a2219cfc 100644 (file)
@@ -40,7 +40,8 @@ message *m_ptr;                       /* pointer to request message */
    */
   caller_ptr = proc_addr(who_p);
   if (! (priv(caller_ptr)->s_flags & SYS_PROC)) return(EPERM); 
-  if(!isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
+  if(m_ptr->PR_ENDPT == SELF) proc_nr = who_p;
+  else if(!isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
   rp = proc_addr(proc_nr);
 
   switch(m_ptr->CTL_REQUEST)
@@ -79,10 +80,12 @@ message *m_ptr;                     /* pointer to request message */
            }
        }
 
-       /* No I/O resources, no memory resources, no IRQs */
+       /* No I/O resources, no memory resources, no IRQs, no grant table */
        priv(rp)->s_nr_io_range= 0;
        priv(rp)->s_nr_mem_range= 0;
        priv(rp)->s_nr_irq= 0;
+       priv(rp)->s_grant_table= 0;
+       priv(rp)->s_grant_entries= 0;
 
        /* Done. Privileges have been set. Allow process to run again. */
        old_flags = rp->p_rts_flags;            /* save value of the flags */
@@ -160,6 +163,11 @@ message *m_ptr;                    /* pointer to request message */
        priv(rp)->s_nr_irq++;
 
        return OK;
+  case SYS_PRIV_SET_GRANTS:
+       if ((rp->p_rts_flags & NO_PRIV) || !(priv(rp))) return(EPERM);
+       _K_SET_GRANT_TABLE(rp, 
+               (vir_bytes) m_ptr->CTL_ARG_PTR, m_ptr->CTL_MM_PRIV);
+       return OK;
 
   default:
        kprintf("do_privctl: bad request %d\n", m_ptr->CTL_REQUEST);
diff --git a/kernel/system/do_safecopy.c b/kernel/system/do_safecopy.c
new file mode 100644 (file)
index 0000000..1541928
--- /dev/null
@@ -0,0 +1,269 @@
+/* The kernel call implemented in this file:
+ *   m_type:   SYS_SAFECOPYFROM or SYS_SAFECOPYTO
+ *
+ * The parameters for this kernel call are:
+ *     SCP_FROM_TO     other endpoint
+ *     SCP_INFO        encoded: caller's own src/dst segment
+ *     SCP_GID         grant id
+ *     SCP_OFFSET      offset within granted space
+ *     SCP_ADDRESS     address in own address space
+ *     SCP_BYTES       bytes to be copied
+ */
+
+#include "../system.h"
+#include <minix/type.h>
+#include <minix/safecopies.h>
+
+#define MEM_TOP 0xFFFFFFFFUL
+
+/*===========================================================================*
+ *                             verify_grant                                 *
+ *===========================================================================*/
+PUBLIC int verify_grant(granter, grantee, grant, bytes, access,
+       offset_in, offset_result, e_granter)
+endpoint_t granter, grantee;   /* copyee, copyer */
+cp_grant_id_t grant;           /* grant id */
+vir_bytes bytes;               /* copy size */
+int access;                    /* direction (read/write) */
+vir_bytes offset_in;           /* copy offset within grant */
+vir_bytes *offset_result;      /* copy offset within virtual address space */
+endpoint_t *e_granter;         /* new granter (magic grants) */
+{
+       static cp_grant_t g;
+       static int proc_nr;
+       static struct proc *granter_proc;
+       static phys_bytes phys_grant;
+
+       /* Get granter process slot (if valid), and check range of
+        * grant id.
+        */
+       if(!isokendpt(granter, &proc_nr) || !GRANT_VALID(grant)) {
+               kprintf("grant verify failed: invalid granter or grant\n");
+               return(EINVAL);
+       }
+       granter_proc = proc_addr(proc_nr);
+
+       /* If there is no priv. structure, or no grant table in the
+        * priv. structure, or the grant table in the priv. structure
+        * is too small for the grant,
+        *
+        * then there exists no such grant, so
+        *
+        * return EPERM.
+        *
+        * (Don't leak how big the grant table is by returning
+        * EINVAL for grant-out-of-range, in case this turns out to be
+        * interesting information.)
+        */
+       if((granter_proc->p_rts_flags & NO_PRIV) || !(priv(granter_proc)) ||
+         priv(granter_proc)->s_grant_table < 1) {
+               kprintf("grant verify failed in ep %d proc %d: "
+               "no priv table, or no grant table\n",
+                       granter, proc_nr);
+               return(EPERM);
+       }
+
+       if(priv(granter_proc)->s_grant_entries <= grant) {
+               kprintf("grant verify failed in ep %d proc %d: "
+               "grant %d out of range for table size %d\n",
+                       granter, proc_nr, grant, priv(granter_proc)->s_grant_entries);
+               return(EPERM);
+       }
+
+       /* Copy the grant entry corresponding to this id to see what it
+        * looks like. If it fails, hide the fact that granter has
+        * (presumably) set an invalid grant table entry by returning
+        * EPERM, just like with an invalid grant id.
+        */
+       if(!(phys_grant = umap_local(granter_proc, D,
+         priv(granter_proc)->s_grant_table + sizeof(g)*grant, sizeof(g)))) {
+               kprintf("grant verify failed: umap failed\n");
+               return EPERM;
+       }
+
+       phys_copy(phys_grant, vir2phys(&g), sizeof(g));
+
+       /* Check validity. */
+       if(!(g.cp_flags & CPF_USED)) {
+               kprintf("grant verify failed: invalid\n");
+               return EPERM;
+       }
+
+       /* Check access of grant. */
+       if(((g.cp_flags & access) != access)) {
+               kprintf("grant verify failed: access invalid; want %x, have %x\n",
+                       access, g.cp_flags);
+               return EPERM;
+       }
+
+       if((g.cp_flags & CPF_DIRECT)) {
+               /* Don't fiddle around with grants that wrap, arithmetic
+                * below may be confused.
+                */
+               if(MEM_TOP - g.cp_u.cp_direct.cp_len <
+                       g.cp_u.cp_direct.cp_start - 1) {
+                       kprintf("direct grant verify failed: len too long\n");
+                       return EPERM;
+               }
+
+               /* Verify actual grantee. */
+               if(g.cp_u.cp_direct.cp_who_to != grantee && grantee != ANY) {
+                       kprintf("direct grant verify failed: bad grantee\n");
+                       return EPERM;
+               }
+
+               /* Verify actual copy range. */
+               if((offset_in+bytes < offset_in) ||
+                   offset_in+bytes > g.cp_u.cp_direct.cp_len) {
+                       kprintf("direct grant verify failed: bad size or range. "
+                               "granted %d bytes @ 0x%lx; wanted %d bytes @ 0x%lx\n",
+                               g.cp_u.cp_direct.cp_len, g.cp_u.cp_direct.cp_start,
+                               bytes, offset_in);
+                       return EPERM;
+               }
+
+               /* Verify successful - tell caller what address it is. */
+               *offset_result = g.cp_u.cp_direct.cp_start + offset_in;
+               *e_granter = granter;
+       } else if(g.cp_flags & CPF_MAGIC) {
+               /* Currently, it is hardcoded that only FS may do
+                * magic grants.
+                */
+#if 0
+               kprintf("magic grant ..\n");
+#endif
+               if(granter != FS_PROC_NR) {
+                       kprintf("magic grant verify failed: granter (%d) "
+                               "is not FS (%d)\n", granter, FS_PROC_NR);
+                       return EPERM;
+               }
+
+               /* Don't fiddle around with grants that wrap, arithmetic
+                * below may be confused.
+                */
+               if(MEM_TOP - g.cp_u.cp_magic.cp_len <
+                       g.cp_u.cp_magic.cp_start - 1) {
+                       kprintf("magic grant verify failed: len too long\n");
+                       return EPERM;
+               }
+
+               /* Verify actual grantee. */
+               if(g.cp_u.cp_magic.cp_who_to != grantee && grantee != ANY) {
+                       kprintf("magic grant verify failed: bad grantee\n");
+                       return EPERM;
+               }
+
+               /* Verify actual copy range. */
+               if((offset_in+bytes < offset_in) ||
+                   offset_in+bytes > g.cp_u.cp_magic.cp_len) {
+                       kprintf("magic grant verify failed: bad size or range. "
+                               "granted %d bytes @ 0x%lx; wanted %d bytes @ 0x%lx\n",
+                               g.cp_u.cp_magic.cp_len, g.cp_u.cp_magic.cp_start,
+                               bytes, offset_in);
+                       return EPERM;
+               }
+
+               /* Verify successful - tell caller what address it is. */
+               *offset_result = g.cp_u.cp_magic.cp_start + offset_in;
+               *e_granter = g.cp_u.cp_magic.cp_who_from;
+       } else {
+               kprintf("grant verify failed: unknown grant type\n");
+               return EPERM;
+       }
+
+#if 0
+       kprintf("grant verify successful %d -> %d (grant %d): %d bytes\n",
+               granter, grantee, grant, bytes);
+#endif
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             do_safecopy                                  *
+ *===========================================================================*/
+PUBLIC int do_safecopy(m_ptr)
+register message *m_ptr;       /* pointer to request message */
+{
+       static endpoint_t granter, grantee, *src, *dst, new_granter;
+       static int access, r, src_seg, dst_seg;
+       static struct vir_addr v_src, v_dst;
+       static vir_bytes offset, bytes;
+
+       granter = m_ptr->SCP_FROM_TO;
+       grantee = who_e;
+
+       /* Set src and dst. One of these is always the caller (who_e),
+        * depending on whether the call was SYS_SAFECOPYFROM or 
+        * SYS_SAFECOPYTO. The caller's seg is encoded in the SCP_INFO
+        * field.
+        */
+       if(sys_call_code == SYS_SAFECOPYFROM) {
+               src = &granter;
+               dst = &grantee;
+               src_seg = D;
+               dst_seg = SCP_INFO2SEG(m_ptr->SCP_INFO);
+               access = CPF_READ;
+       } else if(sys_call_code == SYS_SAFECOPYTO) {
+               src = &grantee;
+               dst = &granter;
+               src_seg = SCP_INFO2SEG(m_ptr->SCP_INFO);
+               dst_seg = D;
+               access = CPF_WRITE;
+       } else panic("Impossible system call nr. ", sys_call_code);
+
+#if 0
+       kprintf("safecopy: %d -> %d\n", *src, *dst);
+#endif
+
+       bytes = m_ptr->SCP_BYTES;
+
+       /* Verify permission exists. */
+       if((r=verify_grant(granter, grantee, m_ptr->SCP_GID, bytes, access,
+           m_ptr->SCP_OFFSET, &offset, &new_granter)) != OK) {
+               kprintf("grant %d verify to copy %d->%d by %d failed: err %d\n",
+                m_ptr->SCP_GID, *src, *dst, grantee, r);
+               return r;
+       }
+
+       /* verify_grant() can redirect the grantee to someone else,
+        * meaning the source or destination changes.
+        */
+       granter = new_granter;
+#if 0
+       kprintf("verified safecopy: %d -> %d\n", *src, *dst);
+#endif
+
+       /* Now it's a regular copy. */
+       v_src.segment = src_seg;
+       v_dst.segment = dst_seg;
+       v_src.proc_nr_e = *src;
+       v_dst.proc_nr_e = *dst;
+
+       /* Now the offset in virtual addressing is known in 'offset'.
+        * Depending on the call, this is the source or destination
+        * address.
+        */
+       if(sys_call_code == SYS_SAFECOPYFROM) {
+               v_src.offset = offset;
+               v_dst.offset = (vir_bytes) m_ptr->SCP_ADDRESS;
+       } else {
+               v_src.offset = (vir_bytes) m_ptr->SCP_ADDRESS;
+               v_dst.offset = offset;
+       }
+
+#if 0
+       kprintf("copy 0x%lx (%d) in %d to 0x%lx (%d) in %d (%d bytes)\n",
+               v_src.offset, v_src.segment, *src,
+               v_dst.offset, v_dst.segment, *dst,
+               bytes);
+#endif
+
+#if DEBUG_SAFE_COUNT
+       unsafe_copy_log(0,0);
+#endif
+
+       /* Do the regular copy. */
+       return virtual_copy(&v_src, &v_dst, bytes);
+}
+
index 82a707f8e0be5b661ee127e96d2a816c35924b8d..dad932a23300901293a8550cca2ad600b4cf4adb 100644 (file)
@@ -26,6 +26,7 @@ register message *m_ptr;      /* pointer to request message */
   int count = m_ptr->DIO_VEC_SIZE;
   long port = m_ptr->DIO_PORT;
   phys_bytes phys_buf;
+  int req_type, req_dir;
 
   /* Check if process endpoint is OK. 
    * A driver may directly provide a pointer to a buffer at the user-process
@@ -38,21 +39,38 @@ register message *m_ptr;    /* pointer to request message */
                return(EINVAL);
   if (iskerneln(proc_nr)) return(EPERM);
 
-  /* Get and check physical address. */
-  if ((phys_buf = numap_local(proc_nr, (vir_bytes) m_ptr->DIO_VEC_ADDR, count)) == 0)
-      return(EFAULT);
+  /* Extract direction (in or out) and type (size). */
+  req_dir = m_ptr->DIO_REQUEST & _DIO_DIRMASK;
+  req_type = m_ptr->DIO_REQUEST & _DIO_TYPEMASK;
+
+  /* Check for 'safe' variants. */
+  if((m_ptr->DIO_REQUEST & _DIO_SAFEMASK) == _DIO_SAFE) {
+     /* Map grant address to physical address. */
+     if ((phys_buf = umap_verify_grant(proc_addr(proc_nr), who_e,
+       (vir_bytes) m_ptr->DIO_VEC_ADDR,
+       (vir_bytes) m_ptr->DIO_OFFSET, count,
+       req_dir == _DIO_INPUT ? CPF_WRITE : CPF_READ)) == 0)
+         return(EPERM);
+  } else {
+     if(proc_nr != who_p)
+       kprintf("unsafe sdevio by %d in %d\n", who_e, proc_nr_e);
+     /* Get and check physical address. */
+     if ((phys_buf = numap_local(proc_nr,
+        (vir_bytes) m_ptr->DIO_VEC_ADDR, count)) == 0)
+         return(EFAULT);
+  }
 
   /* Perform device I/O for bytes and words. Longs are not supported. */
-  if (m_ptr->DIO_REQUEST == DIO_INPUT) { 
-      switch (m_ptr->DIO_TYPE) {
-      case DIO_BYTE: phys_insb(port, phys_buf, count); break; 
-      case DIO_WORD: phys_insw(port, phys_buf, count); break; 
+  if (req_dir == _DIO_INPUT) { 
+      switch (req_type) {
+      case _DIO_BYTE: phys_insb(port, phys_buf, count); break; 
+      case _DIO_WORD: phys_insw(port, phys_buf, count); break; 
       default: return(EINVAL);
       } 
-  } else if (m_ptr->DIO_REQUEST == DIO_OUTPUT) { 
-      switch (m_ptr->DIO_TYPE) {
-      case DIO_BYTE: phys_outsb(port, phys_buf, count); break; 
-      case DIO_WORD: phys_outsw(port, phys_buf, count); break; 
+  } else if (req_dir == _DIO_OUTPUT) { 
+      switch (req_type) {
+      case _DIO_BYTE: phys_outsb(port, phys_buf, count); break; 
+      case _DIO_WORD: phys_outsw(port, phys_buf, count); break; 
       default: return(EINVAL);
       } 
   }
index e1b5b59881770f61b59cf7860682a8a0dd4189d3..97e56937fad824f8c9afbe640a2489e8dee9fce3 100644 (file)
@@ -44,7 +44,7 @@ register message *m_ptr;      /* pointer to request message */
   }
   if (index < 0) return(ENOSPC);
 
-  if (! machine.protected) {
+  if (! machine.prot) {
       selector = phys / HCLICK_SIZE;
       offset = phys % HCLICK_SIZE;
       result = OK;
index 613549169f7356bf72076d0a3087b01aa018b971..93331a7dddcb1400757eb8b2c22402714f9467f8 100644 (file)
@@ -46,6 +46,9 @@ register message *m_ptr;      /* pointer to request message */
   case BIOS_SEG:
       phys_addr = umap_bios(proc_addr(proc_nr), offset, count); 
       break;
+  case GRANT_SEG:
+      phys_addr = umap_grant(proc_addr(proc_nr), offset, count); 
+      break;
   default:
       return(EINVAL);
   }
index 94792bf7b60c07b4d69290f2109b91ae9cdc97b9..ae3cb78c5a3f2c8a6b973daf4189d42598c2e567 100644 (file)
@@ -38,16 +38,19 @@ register message *m_ptr;    /* pointer to request message */
   vir_bytes caller_vir;       /* virtual address at caller */
   phys_bytes caller_phys;     /* physical address at caller */
   int i;
+  int io_dir, io_type;
     
   /* Get the request, size of the request vector, and check the values. */
-  if (m_ptr->DIO_REQUEST == DIO_INPUT) io_in = TRUE;
-  else if (m_ptr->DIO_REQUEST == DIO_OUTPUT) io_in = FALSE;
+  io_dir = m_ptr->DIO_REQUEST & _DIO_DIRMASK;
+  io_type = m_ptr->DIO_REQUEST & _DIO_TYPEMASK;
+  if (io_dir == _DIO_INPUT) io_in = TRUE;
+  else if (io_dir == _DIO_OUTPUT) io_in = FALSE;
   else return(EINVAL);
   if ((vec_size = m_ptr->DIO_VEC_SIZE) <= 0) return(EINVAL);
-  switch (m_ptr->DIO_TYPE) {
-      case DIO_BYTE: bytes = vec_size * sizeof(pvb_pair_t); break;
-      case DIO_WORD: bytes = vec_size * sizeof(pvw_pair_t); break;
-      case DIO_LONG: bytes = vec_size * sizeof(pvl_pair_t); break;
+  switch (io_type) {
+      case _DIO_BYTE: bytes = vec_size * sizeof(pvb_pair_t); break;
+      case _DIO_WORD: bytes = vec_size * sizeof(pvw_pair_t); break;
+      case _DIO_LONG: bytes = vec_size * sizeof(pvl_pair_t); break;
       default:  return(EINVAL);   /* check type once and for all */
   }
   if (bytes > sizeof(vdevio_buf))  return(E2BIG);
@@ -63,12 +66,12 @@ register message *m_ptr;    /* pointer to request message */
    * batch from being interrupted. 
    */  
   lock(13, "do_vdevio");
-  switch (m_ptr->DIO_TYPE) {
-  case DIO_BYTE:                                        /* byte values */
+  switch (io_type) {
+  case _DIO_BYTE:                                       /* byte values */
       if (io_in) for (i=0; i<vec_size; i++)  pvb[i].value = inb(pvb[i].port); 
       else       for (i=0; i<vec_size; i++)  outb(pvb[i].port, pvb[i].value); 
       break; 
-  case DIO_WORD:                                         /* word values */
+  case _DIO_WORD:                                        /* word values */
       if (io_in) for (i=0; i<vec_size; i++)  pvw[i].value = inw(pvw[i].port);  
       else       for (i=0; i<vec_size; i++)  outw(pvw[i].port, pvw[i].value); 
       break;