コード例 #1
0
ファイル: acpi.c プロジェクト: andre-werner/argentum
static Rsdp *
acpi_find_rsdp(void)
{
    Rsdp *rsdp;
    uint32_t ebda;
   
    ebda = *(uint16_t *) pa2kva(0x40E);
    ebda = ebda * 0x10 & 0xFFFFF;
    
    rsdp = __acpi_find_rsdp(pa2kva(ebda), 0x1000);
    if (!rsdp)
       rsdp = __acpi_find_rsdp(pa2kva(0xE0000), 0x20000);
    
    return rsdp;
}
コード例 #2
0
ファイル: acpi.c プロジェクト: andre-werner/argentum
static int
acpi_parse_madt(Madt *madt)
{
    uint8_t *p, *e;
    
    lapic = (uint8_t *) pa2kva(madt->lapic_address);

    e = (uint8_t *) madt + madt->header.length;

    for (p = (uint8_t *) (madt + 1); p < e; p += p[1]) {
        switch (p[0]) {
        case 1:
            // IO APIC structure
            if (ioapic)
                warn("more than one ioapic");
            else {
                ioapic = pa2kva(((Io_apic *) p)->io_apic_address);
            }
            break;
        }
    }

    return 0;
}
コード例 #3
0
ファイル: acpi.c プロジェクト: andre-werner/argentum
void
acpi_init(void)
{
    Rsdp *rsdp;

    rsdp = acpi_find_rsdp();
    if (!rsdp)
        panic("acpi_init: cannot find rsdp");

    kprintf("ACPI: RSDP %#lx %08x <v%02u %.*s>\n", 
            kva2pa(rsdp), sizeof(*rsdp), 
            rsdp->revision + 1, sizeof(rsdp->oemid), rsdp->oemid);
    
    acpi_parse_rsdt(pa2kva(rsdp->rsdt_address));
}
コード例 #4
0
ファイル: dwarf_parse.c プロジェクト: andre-werner/argentum
int
dwarf_rip_debug_info(uintptr_t rip, struct Rip_debug_info *info)
{
    // these symbols are defined in core/core.ld
    extern uint8_t _debug_aranges_begin[], _debug_aranges_end[];
    
    info->file_name = "<unknown>";
    info->fn_name = "<unknown>";
    info->fn_offset = 0;
    
    if (_debug_aranges_end <= _debug_aranges_begin)
        // no debugging info?
        return -1;

    // Search the table of address ranges for the instruction pointer
    uint8_t *p = _debug_aranges_begin; 
    while (p < _debug_aranges_end) {
        // read the header
        uint32_t length = extract_uint(p, 4, &p);
        uint8_t *e = p + length;
        extract_uint(p, 2, &p); // version
        uint32_t offset = extract_uint(p, 4, &p);
        extract_uint(p, 1, &p); // address size
        extract_uint(p, 1, &p); // segment size
        
        // the first touple begins at an offset that is a multiple of the size 
        // of a single tuple (i.e. twice the size of an address)
        p += 4;

        while (p < e) {
            uint64_t range_addr = extract_uint(p, 8, &p);
            uint64_t range_length = extract_uint(p, 8, &p);

            //kprintf("%p - %p\n", range_addr, range_addr + range_length);
        
            // each set of tuples is terminated by a 0 for the address and 0 
            // for the length
            if (range_addr == 0 && range_length == 0)
                break;

            if (rip >= range_addr && rip < (range_addr + range_length))
                // the instruction pointer belongs to this compile unit
                return parse_cu(pa2kva(offset), rip, info);
        }
    }
    return 0;
}
コード例 #5
0
ファイル: acpi.c プロジェクト: andre-werner/argentum
static int
acpi_parse_rsdt(Rsdt *rsdt)
{
    unsigned nentries;
    uint32_t *p;

    if (memcmp(rsdt->header.signature, "RSDT", 4) != 0 ||
        acpi_checksum8(rsdt, rsdt->header.length) != 0) {
        warn("ACPI: invalid RSDT");
        return -1;
    }
    
    acpi_print_sdt_header(&rsdt->header);

    nentries = (rsdt->header.length - sizeof(rsdt->header)) / 
        sizeof(rsdt->entries[0]);

    for (p = rsdt->entries; p < &rsdt->entries[nentries]; p++) {
        Sdt_header *header;
        int r;

        header = pa2kva(*p);

        if (acpi_checksum8(header, header->length)) {
            warn("invalid SDT header");
            return -1;
        }

        acpi_print_sdt_header(header);

        if (!memcmp(header->signature, "FACP", 4))
            r = acpi_parse_fadt((Fadt *) header);
        else if (!memcmp(header->signature, "APIC", 4))
            r = acpi_parse_madt((Madt *) header);

        if (r < 0)
            return r;
    }

    kprintf("ACPI: all tables successfully acquired\n");

    return 0;
}
コード例 #6
0
ファイル: dwarf_parse.c プロジェクト: andre-werner/argentum
// Parse the compile unit. Find the function to which the instruction pointer
// belongs and fill the info structure.
static int
parse_cu(uint8_t *p, uintptr_t rip, struct Rip_debug_info *info)
{
    // read the compile unit header
    uint32_t length = extract_uint(p, 4, &p);
    uint8_t *e = p + length;
    extract_uint(p, 2, &p); // version
    uint32_t abbr_offset = extract_uint(p, 4, &p);
    extract_uint(p, 1, &p); // address size

    while (p < e) {
        uint64_t code;

        // skip null DIEs
        do {
            code = extract_uleb128(p, &p);
        } while (code == 0 && p < e);
        if (p >= e)
            break;

        // Search the abbreviation table for the declaration with this code
        uint8_t *abbr = find_abbrev_decl(pa2kva(abbr_offset), code);
        uint64_t tag = extract_uleb128(abbr, &abbr);
        extract_uint(abbr, 1, &abbr);   // skip the has_children tag

        // read the attributes
        // we're interested only in several tags and attributes 
        uintptr_t fn_lo = 0, fn_hi = 0;
        char *fn_name = NULL;
        for (;;) {
            uint64_t name = extract_uleb128(abbr, &abbr);
            uint64_t form = extract_uleb128(abbr, &abbr);
            union Attr_val val;
            
            if (name == 0 && form == 0)
                break;
           
            // extract the attribute value
            if (extract_value(p, form, &val, &p) == -1)
                return -1;

            if (tag == DW_TAG_compile_unit && name == DW_AT_name) {
                info->file_name = val.string;
            } else if (tag == DW_TAG_subprogram) {
                if (name == DW_AT_name)
                    fn_name = val.string;
                else if (name == DW_AT_low_pc)
                    fn_lo = val.number;
                else if (name == DW_AT_high_pc)
                    fn_hi = val.number;
            }
        }

        if (tag == DW_TAG_subprogram) {
            // if the recently parsed tag was a function tag...
            if (rip >= fn_lo && rip <= fn_hi) {
                // ... and the instruction pointer belongs to this function
                if (fn_name)
                    info->fn_name = fn_name;
                info->fn_offset = rip - fn_lo;
            }
        }
    }
    return 0;
}
コード例 #7
0
ファイル: dwarf_parse.c プロジェクト: andre-werner/argentum
// Extract the attribute value of the given form
static int
extract_value(uint8_t *p, uint64_t form, union Attr_val *value, uint8_t **e)
{
    switch (form) {
    case DW_FORM_data1:
    case DW_FORM_ref1:
    case DW_FORM_flag:
        value->number = extract_uint(p, 1, &p);
        break;
    case DW_FORM_data2:
    case DW_FORM_ref2:
        value->number = extract_uint(p, 2, &p);
        break;
    case DW_FORM_data4:
    case DW_FORM_ref4:
        value->number = extract_uint(p, 4, &p);
        break;
    case DW_FORM_data8:
    case DW_FORM_ref8:
    case DW_FORM_addr:
    case DW_FORM_ref_addr:
        value->number = extract_uint(p, 8, &p);
        break;
    case DW_FORM_sdata:
        value->number = extract_sleb128(p, &p);
        break;
    case DW_FORM_udata:
    case DW_FORM_ref_udata:
        value->number = extract_uleb128(p, &p);
        break;
    case DW_FORM_string:
        value->string = (char *) p;
        while (*p++)
            ;
        break;
    case DW_FORM_strp:
        value->string = (char *) pa2kva(extract_uint(p, 4, &p));
        break;
    case DW_FORM_block1:
        value->block.length = extract_uint(p, 1, &p);
        value->block.data = (char *) p;
        p += value->block.length;
        break;
    case DW_FORM_block2:
        value->block.length = extract_uint(p, 2, &p);
        value->block.data = (char *) p;
        p += value->block.length;
        break;
    case DW_FORM_block4:
        value->block.length = extract_uint(p, 4, &p);
        value->block.data = (char *) p;
        p += value->block.length;
        break;
    case DW_FORM_block:
        value->block.length = extract_uleb128(p, &p);
        value->block.data = (char *) p;
        p += value->block.length;
        break;
    case DW_FORM_indirect:
        form = extract_uleb128(p, &p);
        return extract_value(p, form, value, &p);
    default:
        kprintf("uknown attribute form: %x\n", form);
        return -1;
    }
    if (e)
        *e = p;
    return 0;
}
コード例 #8
0
/*
 * sys_disk_request
 *
 * Disk I/O without going through the buffer cache.
 *
 * xn_user is the name of a pxn that grants access to the disk
 * reqbp is a list of scatter/gather requests
 * k is which capability in the env should be checked
 *
 * permission is granted to perform the operation if:
 * 1) the blocks in reqbp are covered by the pxn
 * 2) the capability gives access to the pxn
 *
 */
int
sys_disk_request (u_int sn, struct Xn_name *xn_user, struct buf *reqbp,
		  u_int k)
{
  struct Xn_name xn;
  struct Xn_xtnt xtnt;
  struct Pxn *pxn;
  cap c;
  int ret;
  int access;
  struct disk *di;
  int *resptr = NULL;
  u_int bcount = 0;
  struct buf *bp, *segbp, *nsegbp;
  int noncontigs = 0, nctemp;

#ifdef MEASURE_DISK_TIMES
  disk_pctr_start = rdtsc();
#endif

  /* XXX - use PFM or copyin instead of isreadable_* */

  /* bypass for direct scsi commands */
  if (reqbp->b_flags & B_SCSICMD) {
    return sys_disk_scsicmd (sn, k, reqbp);
  }

  /* get the capability */
  if ((ret = env_getcap (curenv, k, &c)) < 0)
    return ret;

  /* and the pxn */
  copyin (xn_user, &xn, sizeof (xn));
  if (! (pxn = lookup_pxn (&xn))) {
    warn ("sys_disk_request: no pxn found");
    return (-E_NOT_FOUND);
  }

  /* XXX - do we need to check that this is a physical disk? */
  /* get a refernce to the disk unit for this command */
  di = &(si->si_disks[xn.xa_dev]);

  /* Iterate over the request list checking:
     -- if the request is transfering data to/from
     memory that this user can read/write.
     -- if the pxn and capability specified give
     access to these blocks */
  for (segbp = reqbp; ; segbp = (struct buf *) segbp->b_sgnext) {
    if (! (isreadable_varange ((u_int)segbp, sizeof(struct buf)))) {
      warn ("sys_disk_request: bad reqbp (%p)", segbp);
      return (-E_FAULT);
    }

    if (segbp->b_flags & B_READ) {
      access = ACL_R;
    } else {
      access = ACL_W;
    }
    
    xtnt.xtnt_block = segbp->b_blkno;
    xtnt.xtnt_size = segbp->b_bcount / di->d_bsize;
    bcount += segbp->b_bcount;

    if (! pxn_authorizes_xtnt (pxn, &c, &xtnt, access, &ret)) {
      warn ("sys_disk_request: pxn/cap does not grant access to block(s)");
      return ret;
    }

    if (! ((reqbp->b_flags & B_READ) ? iswriteable_varange
	   : isreadable_varange) ((u_int) segbp->b_memaddr, segbp->b_bcount)) {
      warn ("sys_disk_request: bad b_memaddr: %p (b_bcount %d)",
	    segbp->b_memaddr, segbp->b_bcount);
      return (-E_FAULT);
    }

    if (! (segbp->b_flags & B_SCATGATH)) {
      if (segbp->b_resptr) {
	resptr = segbp->b_resptr;
	if ((((u_int) resptr) % sizeof(u_int)) || 
	    !(isvawriteable (resptr))) {
	  warn ("sys_disk_request: bad resptr (%p)", resptr);
	  return (-E_FAULT);
	}
	resptr = (int *) pa2kva (va2pa (resptr));
      }
      break;
    }
  }

  if ((reqbp->b_flags & B_SCATGATH) && bcount != reqbp->b_sgtot) {
    warn ("sys_disk_request: invalid scatter/gather, with total (%u) unequal "
	  "to sum of parts (%u)", reqbp->b_sgtot, bcount);
    return (-E_INVAL);
  }

  /* are we done before we've started? */
  if (bcount == 0) {
    if (resptr)
      (*resptr)--;
    return (0);
  }

  if (bcount & di->d_bmod) {
    warn ("sys_disk_request: bad bcount %u", bcount);
    return (-E_INVAL);
  }

  /* copy request into kernel buffer */
  segbp = reqbp;
  nsegbp = NULL;
  reqbp = NULL;
  do {
    segbp->b_dev = di->d_id;
    bp = copy_and_pin(segbp, segbp->b_bcount, &nctemp);
    if (!bp) {
      warn ("sys_disk_request: could not copy_and_pin");
      /* XXX - cleanup before returning */
      return (-E_NO_MEM);
    }
    noncontigs += nctemp;
    if (nsegbp) nsegbp->b_sgnext = bp;
    if (!reqbp) reqbp = bp;
    if (noncontigs >= DISK_MAX_SCATTER) {
      warn ("sys_disk_request: would require too many scatter/gather entries "
	    "(%d)", noncontigs);
      /* XXX - cleanup before returning */
      return (-E_INVAL);
    }
    nsegbp = bp;
    segbp = segbp->b_sgnext;
  } while (nsegbp->b_flags & B_SCATGATH);

  nsegbp->b_resptr = resptr;

  if (resptr) ppage_pin (kva2pp((u_int) resptr));

  /* call appropriate strategy routine */
  di->d_strategy (reqbp);

#ifdef MEASURE_DISK_TIMES
  disk_pctr_return = rdtsc();
#endif

  return (0);
}
コード例 #9
0
/*
 * sys_disk_mbr
 *
 * Read/Write the master boot record of a disk.
 *
 * The mbr contains the bootstrap code that the BIOS
 * loads on startup. This is always in sector 0
 * of the disk being booted. The mbr also contains the
 * partition table for the disk.
 *
 */
int
sys_disk_mbr (u_int sn, int write, u_int dev, int k, char *buffer, int *resptr)
{
  cap c;
  int ret;
  struct buf *diskbuf;

  /* get the capability */
  if ((ret = env_getcap (curenv, k, &c)) < 0)
    return ret;

  /* make sure the root cap was passed in */
  if (!cap_isroot (&c))
    return -E_CAP_INSUFF;

  /* verify the dev */
  if (dev >= si->si_ndisks)
    return -E_NOT_FOUND;

  /* check and translate the buffers we were given */
  if ((((u_int) resptr) % sizeof(u_int)) || 
      !(isvawriteable (resptr))) {
    warn ("sys_disk_mrb: bad resptr (%p)", resptr);
    return (-E_FAULT);
  }
  ppage_pin (pa2pp ((va2pa (resptr))));
  resptr = (int *) pa2kva (va2pa (resptr));

  if (write) {
    if (! (iswriteable_varange ((u_int)buffer, 512))) {
      warn ("sys_disk_mbr: bad buffer (%p)", buffer);
      return (-E_FAULT);
    }
  } else {
    if (! (isreadable_varange ((u_int)buffer, 512))) {
      warn ("sys_disk_mbr: bad buffer (%p)", buffer);
      return (-E_FAULT);
    }
  }

  /* get a disk req buffer and fill it in */
  diskbuf = disk_buf_alloc ();
  if (!diskbuf)
    return -E_NO_MEM;

  diskbuf->b_next = NULL;
  diskbuf->b_sgnext = NULL;
  diskbuf->b_dev = dev;
  diskbuf->b_blkno = 0;
  diskbuf->b_bcount = 512;	/* only want to read the first sector */
  diskbuf->b_sgtot = 512;
  diskbuf->b_memaddr = buffer;
  diskbuf->b_envid = curenv->env_id;
  diskbuf->b_resid = 0;
  diskbuf->b_resptr = resptr;
  diskbuf->b_flags = B_ABSOLUTE;		/* bypass partitions table */
  if (write) {
    diskbuf->b_flags |= B_WRITE;
  } else {
    diskbuf->b_flags |= B_READ;
  }

  /* pin it in case the user frees it before the request completes.
     This will be unpinned when sched_reqcomplete is called which
     in turn calls disk_buf_free which calls ppage_unpin. */

  ppage_pin (pa2pp ((va2pa (buffer))));

  /* start the request */
  si->si_disks[dev].d_strategy (diskbuf);

  return 0;
}
コード例 #10
0
/* XXX - we should use copyin, etc, instead of isreadable_* so that user will
   get pagefaults he can handle transparently */
static int
sys_disk_scsicmd (u_int sn, u_int k, struct buf *reqbp)
{
  struct buf *bp;
  struct scsicmd *scsicmd = (struct scsicmd *) reqbp->b_memaddr;
  struct scsicmd *scsicmd2;
  int noncontigs;
  struct disk *di;

  /* must have root capability for system to do a raw SCSI command!!   */
  /* XXX -- later, if desired, deeper checking of validity can reduce  */
  /* this restriction...                                               */
  if (k >= curenv->env_clen || ! curenv->env_clist[k].c_valid) {
    warn ("sys_disk_scsicmd: bad capability number %u\n", k);
    return (-E_CAP_INVALID);
  }
  if (! cap_isroot(&curenv->env_clist[k])) {
    warn ("sys_disk_scsicmd: cap %u is not root capability for system\n", k);
    return (-E_CAP_INSUFF);
  }

  /* must be able to read the reqbp ... */
  if (! (isreadable_varange ((u_int) reqbp, sizeof (struct buf)))) {
    warn ("sys_disk_scsicmd: bad reqbp (%p)", reqbp);
    return (-E_FAULT);
  }

  /* Should be a SCSICMD */
  if (! (reqbp->b_flags & B_SCSICMD)) {
    warn ("sys_disk_scsicmd: not a B_SCSICMD\n");
    return (-E_INVAL);
  }

  /* Must be proper environment */
  if (reqbp->b_envid != curenv->env_id) {
    warn ("sys_disk_scsicmd: bad envid\n");
    return (-E_INVAL);
  }

  /* no scatter/gather support for raw SCSI commands */
  if (reqbp->b_flags & B_SCATGATH) {
    warn ("sys_disk_scsicmd: B_SCATGATH not allowed with B_SCSICMD\n");
    return (-E_INVAL);
  }

  /* can't send request to non-existent disk... */
  if (reqbp->b_dev >= si->si_ndevs) {
    warn ("sys_disk_scsicmd: there is no disk %u in system\n", reqbp->b_dev);
    return (-E_NOT_FOUND);
  }

  /* check that everything is readable */
  if (! isreadable_varange ((u_int) reqbp->b_memaddr,
			    sizeof (struct scsicmd))) {
    warn ("sys_disk_scsicmd: SCSI command description is not readable\n");
    return (-E_FAULT);
  }

  if (! isreadable_varange ((u_int) scsicmd->scsi_cmd, scsicmd->cmdlen) ) {
    warn ("sys_disk_scsicmd: SCSI command itself is not readable\n");
    return (-E_FAULT);
  }

  if (! isreadable_varange ((u_int)scsicmd->data_addr, scsicmd->datalen) ) {
    warn ("sys_disk_scsicmd: data area for SCSI command is not readable\n");
    return (-E_FAULT);
  }

  /* length of SCSI command must not be greater than B_SCSICMD_MAXLEN */
  if (scsicmd->cmdlen > B_SCSICMD_MAXLEN) {
    /* XXX - why do we compare scsicmd->cmdlen, but we print out
       reqbp->b_bcount? */
    warn ("sys_disk_scsicmd: specified SCSI command too large (%d > %d)\n",
	  reqbp->b_bcount, B_SCSICMD_MAXLEN);
    return (-E_INVAL);
  }

  /* copy the SCSI command to avoid sharing it with app */
  bp = bp_copy (reqbp);
  if (bp == NULL) {
    warn ("sys_disk_scsicmd: kernel malloc for bp failed\n");
    return (-E_NO_MEM);
  }
  bp->b_memaddr = malloc (sizeof (struct scsicmd));
  if (bp->b_memaddr == NULL) {
    warn ("sys_disk_scsicmd: kernel malloc for scsicmd failed\n");
    free (bp);
    return (-E_NO_MEM);
  }
  scsicmd2 = (struct scsicmd *) bp->b_memaddr;
  bcopy (scsicmd, scsicmd2, sizeof (struct scsicmd));
  scsicmd2->scsi_cmd = (struct scsi_generic *) malloc (scsicmd->cmdlen);
  if (scsicmd2->scsi_cmd == NULL) {
    warn ("sys_disk_scsicmd: second kernel malloc failed\n");
    free (bp->b_memaddr);
    free (bp);
    return (-E_NO_MEM);
  }
  bcopy (scsicmd->scsi_cmd, scsicmd2->scsi_cmd, scsicmd->cmdlen);
  scsicmd2->bp = bp;
  bp->b_resid = scsicmd->datalen;
  bp->b_resptr = (int *) pa2kva (va2pa (reqbp->b_resptr));

  /* pin down the app pages that will later be used by the driver */
  ppage_pin (kva2pp ((u_int) bp->b_resptr));
  noncontigs = pin_and_count_noncontigs (scsicmd2->data_addr,
					 scsicmd2->datalen);
  if (noncontigs >= DISK_MAX_SCATTER) {
    warn ("sys_disk_scsicmd: will require too many scatter/gather entries "
	  "(%d)", noncontigs);
    disk_buf_free (bp);
    return (-E_TOO_BIG);
  }

  /* XXX */
  /* call down to the low-level driver.  GROK -- since the partition stuff   */
  /* creates and abstract disk that is separate from the real one, a hack    */
  /* is needed to get the actual disk strategy routine for raw SCSI commands */
  /* This is fine as long as all disks actually go to the same strategy      */
  /* routine.                                                                */
   di = &(si->si_disks[0]);
   di->d_strategy (bp);

   return (0);
}
コード例 #11
0
ファイル: msgring.c プロジェクト: aunali1/exopc
static msgringent *
msgringent_setup (msgringent * u_msgringent)
{
  msgringent *ktmp;
  Pte *pte = NULL;
  int scatptr = 0;
  int total_len = 0;

  ktmp = (msgringent *) malloc (sizeof (msgringent));
  if (ktmp == NULL)
  {
    warn ("msgringent_setup: failed malloc");
    return (NULL);
  }

  ktmp->appaddr = u_msgringent;
  ktmp->owner = NULL;
  ktmp->body.n = 0;

  /* Verify and translate owner field */
  if ((((u_int) u_msgringent->owner % sizeof (int)) ||
       ! (pte = va2ptep ((u_int) u_msgringent->owner)) ||
         ((*pte & WRITE_MASK) != WRITE_MASK)))
  {
    warn ("msgringent_setup: owner field failed\n");
    msgringent_free (ktmp);
    return (NULL);
  }
  ktmp->owner = (u_int *) pa2kva (va2pa (u_msgringent->owner));
  ppage_pin (kva2pp ((u_long) ktmp->owner));

  
  /* Verify and translate data field */
  if (u_msgringent->body.n > 1)
  {
    warn ("msgringent_setup: not allowed to setup disjoint message body\n");
    msgringent_free (ktmp);
    return (NULL);
  }

  scatptr = 0;
  total_len = 0;
  
  {
    int len = u_msgringent->body.r[0].sz;
    caddr_t addr = u_msgringent->body.r[0].data;
    u_int pagebound = NBPG-(((u_long)addr)&(NBPG - 1));

    while (len > 0)
    {
      u_int slen = min (len, pagebound);
      if (!(pte = va2ptep ((u_int) addr)) || 
          ((*pte & READ_MASK) != READ_MASK))
      {
        /* physical page is not accessible */
        warn ("msgringent_setup: can't read scatter ptr\n");
        msgringent_free (ktmp);
        return (NULL);
      }
            
      ktmp->body.r[scatptr].data = (char *) pa2kva (va2pa (addr));
      ktmp->body.r[scatptr].sz = slen;
      ktmp->body.n++;
	
      /* pin the page to prevent re-allocation */
      ppage_pin (kva2pp ((u_long) ktmp->body.r[scatptr].data));
      len -= slen;
      addr += slen;
      total_len += slen;
      pagebound = NBPG;
      scatptr++;
    
      if (scatptr > IPC_MAX_SCATTER_PTR || total_len > IPC_MAX_MSG_SIZE)
      {
        msgringent_free (ktmp);
        warn ("msgringent_setup: message body too big\n");
        return (NULL);
      }
    }
  }

  return (ktmp);
}