//+++2004-09-11
//    Copyright (C) 2004  Mike Rieker, Beverly, MA USA
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; version 2 of the License.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//---2004-09-11

/************************************************************************/
/*									*/
/*  This routine loads an image into the memory of the current process	*/
/*									*/
/************************************************************************/

#define _OZ_KNL_IMAGE_C

#include "ozone.h"

#include "oz_io_fs.h"
#include "oz_knl_devio.h"
#include "oz_knl_event.h"
#include "oz_knl_handle.h"
#include "oz_knl_hw.h"
#include "oz_knl_image.h"
#include "oz_knl_kmalloc.h"
#include "oz_knl_lock.h"
#include "oz_knl_printk.h"
#include "oz_knl_process.h"
#include "oz_knl_sdata.h"
#include "oz_knl_section.h"
#include "oz_knl_security.h"
#include "oz_knl_status.h"
#include "oz_knl_version.h"
#include "oz_sys_io_fs.h"
#include "oz_sys_io_fs_printf.h"

#define MAX_IMAGE_LEVELS (16)

struct OZ_Image { OZ_Objtype objtype;		/* object type OZ_OBJTYPE_IMAGE */
                  OZ_Image *next;		/* next image in list */
                  OZ_Imagelist *imagelist;	/* imagelist this image is in */
                  OZ_Procmode mprocmode;	/* procmode of memory structs (OZ_PROCMODE_KNL or _SYS) */
                  OZ_Iochan *iochan;		/* image file IO channel */
                  void *baseaddr;		/* this image's base address */
                  void *startaddr;		/* this image's start address */
                  volatile Long refcount;	/* number of references to this image */
                  OZ_Image_Refd_image *refd_images; /* what images this image references */
                  OZ_Image_Secload *secloads;	/* sections that are loaded */
                  OZ_Image_Hand *imagehand;	/* pointer to image format specific handler entrypoint table */
                  void *imagex;			/* image extension struct (defined by image format specific routine) */
                  OZ_Secattr *secattr;		/* who can access me */
                  char name[1];			/* image name (must be last in struct) */
                };

struct OZ_Imagelist { OZ_Image *images;		/* list of images that are loaded */
                      OZ_Event *event;		/* locking event flag: 0=someone is accessing 'images' list; 1=no one is accessing list */
                    };

globalref OZ_Image_Hand oz_knl_image_elf32;
globalref OZ_Image_Hand oz_knl_image_oz;

static OZ_Image_Hand *imagehands[] = { &oz_knl_image_elf32, &oz_knl_image_oz, NULL };

static uLong open_image_file (OZ_Procmode procmode, const char *filename, OZ_Iochan **iochan_r);
static uLong iowait (OZ_Event *ioevent, OZ_Iochan *iochan, uLong funcode, uLong as, void *ap);
static void lockimagelist (OZ_Imagelist *imagelist);
static void unlkimagelist (OZ_Imagelist *imagelist);

/************************************************************************/
/*									*/
/*  Load image in memory (if it isn't already loaded)			*/
/*									*/
/*    Input:								*/
/*									*/
/*	procmode  = processor mode to load it for			*/
/*	imagename = imagename, eg, LIBRTL				*/
/*	sysimage  = 0 : loading a process symbol			*/
/*	            1 : loading a system image				*/
/*	            2 : loading a system image, but don't actually 	*/
/*	                create sections or map the image		*/
/*	                (used to load the kernel image's symbol table)	*/
/*	level     = outermost call should be a zero			*/
/*									*/
/*	ipl <= softint							*/
/*									*/
/*    Output:								*/
/*									*/
/*	oz_knl_image_load = OZ_SUCCESS : successfully loaded		*/
/*	                          else : load error			*/
/*	image added to the 'images' list				*/
/*	*baseaddr_r  = base address of loaded image (lowest mem addr used)
/*	*startaddr_r = image's start address				*/
/*	*image_r     = pointer to image struct				*/
/*	               only valid in loading process context		*/
/*									*/
/************************************************************************/

uLong oz_knl_image_load (OZ_Procmode procmode, char *imagename, int sysimage, int level, void **baseaddr_r, void **startaddr_r, OZ_Image **image_r)

{
  char *imageloaddir;
  const OZ_Logvalue *logvalues;
  int i;
  OZ_Image *image;
  OZ_Image_Args *imageargs;
  OZ_Image_Hand *imagehand;
  OZ_Image_Refd_image *refd_image;
  OZ_Image_Secload *secload;
  OZ_Imagelist *imagelist, *sysimagelist;
  OZ_IO_fs_getinfo1 fs_getinfo1;
  OZ_IO_fs_getsecattr fs_getsecattr;
  OZ_Pdata *pdata;
  OZ_Pointer itsknlversion;
  OZ_Process *process;
  OZ_Procmode mprocmode;
  uLong logindex, rlen, sts;

  /* If too many levels, abort */

  if (level >= MAX_IMAGE_LEVELS) return (OZ_EXMAXIMGLEVEL);

  /* If system image, load in system image list.  Otherwise, load in process image list. */

  if (sysimage == 0) {								// see if loading image in system area
    process   = oz_knl_thread_getprocesscur ();					// if not, load it in current process area
    mprocmode = OZ_PROCMODE_KNL;						// use process-private kernel memory for its structs
  }
  else if (procmode == OZ_PROCMODE_KNL) {					// can only do kernel mode images in system area
    process   = oz_s_systemproc;						// ok, load it in system area
    mprocmode = OZ_PROCMODE_SYS;						// use system-global kernel memory for its structs
  }
  else return (OZ_KERNELONLY);

  /* Point to image list (or create one if not there) */

  pdata     = OZ_SYS_PDATA (mprocmode);						// point to image's data
  imagelist = pdata -> imagelist;						// get current imagelist struct
  if (imagelist == NULL) {
    imagelist = oz_sys_pdata_malloc (mprocmode, sizeof *imagelist);		// none there, allocate one
    if (imagelist == NULL) return (OZ_NOMEMORY);
    imagelist -> images = NULL;							// doesn't have any images loaded
    sts = oz_knl_event_create (21, "oz_knl_imagelist lock", NULL, &(imagelist -> event)); // create lock event flag
    if (sts != OZ_SUCCESS) {
      oz_sys_pdata_free (mprocmode, imagelist);
      return (sts);
    }
    unlkimagelist (imagelist);							// mark it unlocked
    if (!oz_hw_atomic_setif_ptr ((void *volatile *)&(pdata -> imagelist), imagelist, NULL)) { // try to set this as new one
      oz_knl_event_increfc (imagelist -> event, -1);				// someone else got there first, free it off
      oz_sys_pdata_free (mprocmode, imagelist);
      imagelist = pdata -> imagelist;						// ... and use the other one
    }
  }

  /* If level 0, lock the image list so others can't access or modify it */

  if (level == 0) lockimagelist (imagelist);

  /* Init stuff that we clean up at the end */

  sts = OZ_NOMEMORY;
  imageargs = oz_sys_pdata_malloc (OZ_PROCMODE_KNL, sizeof *imageargs);
  if (imageargs == NULL) goto cleanup;
  memset (imageargs, 0, sizeof *imageargs);
  imageargs -> imagename = imagename;

  memset (&fs_getinfo1,   0, sizeof fs_getinfo1);
  memset (&fs_getsecattr, 0, sizeof fs_getsecattr);

  /* See if the image is already loaded.  If so, just return the info directly. */

  sts = OZ_SUCCESS;
  for (image = imagelist -> images; image != NULL; image = image -> next) {
    if (strcmp (image -> name, imagename) == 0) goto return_info;
  }

  /* If we just scanned the process' imagelist and couldn't find it, scan the system's imagelist, too */

  if (sysimage == 0) {
    sysimagelist = OZ_SYS_PDATA_SYSTEM -> imagelist;
    lockimagelist (sysimagelist);
    for (image = sysimagelist -> images; image != NULL; image = image -> next) {	/* scan system image list */
      if (strcmp (image -> name, imagename) == 0) {					/* see if the name matches */
        OZ_HW_ATOMIC_INCBY1_LONG (image -> refcount);					/* if so, increment ref count before releasing lock */
        unlkimagelist (sysimagelist);							/* unlock system image list */
        goto return_info_ni;								/* go return the info without incrementing ref count again */
      }
    }
    unlkimagelist (sysimagelist);							/* not found, unlock system image list */
  }

  /* Create an event flag for I/O */

  sts = oz_knl_event_create (25, "oz_knl_image_load ioevent", NULL, &(imageargs -> ioevent));
  if (sts != OZ_SUCCESS) goto cleanup;

  /* Open the image */

  sts = open_image_file (procmode, imagename, &(imageargs -> iochan));
  if (sts != OZ_SUCCESS) goto cleanup;

  /* Get the security attributes from the section file to apply to the section */

  sts = iowait (imageargs -> ioevent, imageargs -> iochan, OZ_IO_FS_GETINFO1, sizeof fs_getinfo1, &fs_getinfo1);
  if (sts != OZ_SUCCESS) {
    oz_sys_io_fs_printerror ("oz_knl_image_load: error %u getting section file info for image %s\n", sts, imagename);
    goto cleanup;
  }
  rlen = 0;
  if (fs_getinfo1.secattrsize != 0) {
    fs_getsecattr.size = fs_getinfo1.secattrsize;
    fs_getsecattr.buff = oz_sys_pdata_malloc (OZ_PROCMODE_KNL, fs_getinfo1.secattrsize);
    if (fs_getsecattr.buff == NULL) {
      oz_sys_io_fs_printerror ("oz_knl_image_load: %s: no memory for %u byte security attributes\n", imagename, fs_getinfo1.secattrsize);
      sts = OZ_NOMEMORY;
      goto cleanup;
    }
    fs_getsecattr.rlen = &rlen;
    sts = iowait (imageargs -> ioevent, imageargs -> iochan, OZ_IO_FS_GETSECATTR, sizeof fs_getsecattr, &fs_getsecattr);
    if (sts != OZ_SUCCESS) {
      oz_sys_io_fs_printerror ("oz_knl_image_load: error %u getting security attributes for image %s\n", sts, imagename);
      goto cleanup;
    }
  }
  sts = oz_knl_secattr_create (rlen, fs_getsecattr.buff, NULL, &(imageargs -> secattr));
  if (sts != OZ_SUCCESS) {
    oz_sys_io_fs_printerror ("oz_knl_image_load: error %u creating security attributes for image %s\n", sts, imagename);
    goto cleanup;
  }

  /* Read the base image header into imageargs -> hdrbuf */

  sts = oz_knl_image_read (imageargs, 1, sizeof imageargs -> hdrbuf, imageargs -> hdrbuf);
  if (sts != OZ_SUCCESS) goto cleanup;

  /* Loop through list of image loaders until we find one than can process it */

  imageargs -> sysimage    = sysimage;
  imageargs -> level       = level;
  imageargs -> procmode    = procmode;
  imageargs -> mprocmode   = mprocmode;
  imageargs -> fs_getinfo1 = &fs_getinfo1;

  for (i = 0; (imagehand = imagehands[i]) != NULL; i ++) {
    imageargs -> baseaddr  = NULL;				/* clear the base address */
    imageargs -> startaddr = NULL;				/* clear the start address */
    sts = (*(imagehand -> load)) (imageargs);			/* try this image loader to see if it works */
    if (sts == OZ_SUCCESS) goto successful;			/* if successful, we're all done */
    while ((secload = imageargs -> secloads) != NULL) {		/* otherwise release any loaded sections it did */
      imageargs -> secloads = secload -> next;
      oz_knl_process_unmapsec (secload -> svpage);
      oz_knl_section_increfc (secload -> section, -1);
      oz_sys_pdata_free (mprocmode, secload);
    }
    while ((refd_image = imageargs -> refd_images) != NULL) {	/* ... and release any referenced images */
      imageargs -> refd_images = refd_image -> next;
      oz_knl_image_increfc (refd_image -> image, -1);
      oz_sys_pdata_free (mprocmode, refd_image);
    }
    if (sts != OZ_UNKIMAGEFMT) goto cleanup;			/* if funky error, we abort */
  }
  sts = OZ_UNKIMAGEFMT;						/* nothing can load it, it must be unknown image format */
  goto cleanup;

  /* We're loaded! */

successful:

  /* If image defines oz_knl_version, it MUST match our definition */
  /* Also, if it is a system image, it MUST define oz_knl_version  */

  if (!(*(imagehand -> lookup)) (imageargs -> imagex, "oz_knl_version", &itsknlversion)) {
    if (sysimage != 0) {
      oz_sys_io_fs_printerror ("oz_knl_image_load: cannot find oz_knl_version definition in %s; include oz_knl_version.h somewhere\n", imagename);
      sts = OZ_BADKNLVERSION;
      goto cleanup;
    }
  } else {
    if (itsknlversion != OZ_KNL_VERSION_INT) {
      oz_sys_io_fs_printerror ("oz_knl_image_load: image %s oz_knl_version %X, my oz_knl_version %X\n", imagename, itsknlversion, OZ_KNL_VERSION_INT);
      sts = OZ_BADKNLVERSION;
      goto cleanup;
    }
  }

  /* Make up the image struct */

  image = oz_sys_pdata_malloc (mprocmode, strlen (imagename) + sizeof *image);
  if (image == NULL) {
    sts = OZ_NOMEMORY;
    goto cleanup;
  }
  image -> objtype     = OZ_OBJTYPE_IMAGE;
  image -> next        = imagelist -> images;
  image -> imagelist   = imagelist;
  image -> baseaddr    = imageargs -> baseaddr;
  image -> startaddr   = imageargs -> startaddr;
  image -> refcount    = 0;
  image -> refd_images = imageargs -> refd_images;
  image -> secloads    = imageargs -> secloads;
  image -> imagex      = imageargs -> imagex;
  image -> iochan      = imageargs -> iochan;
  image -> imagehand   = imagehand;
  image -> mprocmode   = mprocmode;
  image -> secattr     = oz_knl_thread_getdefcresecattr (NULL);
  strcpy (image -> name, imagename);

  imagelist -> images = image;

  imageargs -> refd_images = NULL;
  imageargs -> secloads    = NULL;
  imageargs -> iochan      = NULL;

  /* Inc ref count and return requested information about the image */

return_info:
  OZ_HW_ATOMIC_INCBY1_LONG (image -> refcount);
return_info_ni:
  *image_r = image;
  if (baseaddr_r  != NULL) *baseaddr_r  = image -> baseaddr;
  if (startaddr_r != NULL) *startaddr_r = image -> startaddr;

  /* Return status after cleaning up */

cleanup:
  if (fs_getsecattr.buff != NULL) oz_sys_pdata_free (OZ_PROCMODE_KNL, fs_getsecattr.buff);
  if (imageargs != NULL) {
    if (imageargs -> secattr != NULL) oz_knl_secattr_increfc (imageargs -> secattr, -1);
    if (imageargs -> iochan  != NULL) oz_knl_iochan_increfc  (imageargs -> iochan,  -1);
    if (imageargs -> ioevent != NULL) oz_knl_event_increfc   (imageargs -> ioevent, -1);
    oz_sys_pdata_free (OZ_PROCMODE_KNL, imageargs);
  }
  if (level == 0) unlkimagelist (imagelist);
  return (sts);
}

/************************************************************************/
/*									*/
/*  Lookup global symbol in image					*/
/*									*/
/************************************************************************/

int oz_knl_image_lookup (OZ_Image *image, char *symname, OZ_Pointer *symvalu)

{
  /* Call the handler's specific lookup routine */

  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);
  return ((*(image -> imagehand -> lookup)) (image -> imagex, symname, symvalu));
}

/************************************************************************/
/*									*/
/*  Scan image symbol table						*/
/*									*/
/************************************************************************/

void *oz_knl_image_symscan (OZ_Image *image, void *lastsym, char **symname_r, OZ_Pointer *symvalu_r)

{
  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);
  return ((*(image -> imagehand -> symscan)) (image -> imagex, lastsym, symname_r, symvalu_r));
}

/************************************************************************/
/*									*/
/*  Increment image ref count						*/
/*  Image gets unloaded when ref count goes zero			*/
/*									*/
/************************************************************************/

Long oz_knl_image_increfc (OZ_Image *image, Long inc)

{
  Long refc;
  OZ_Image **limage, *ximage;
  OZ_Imagelist *imagelist;
  OZ_Image_Refd_image *refd_image;
  OZ_Image_Secload *secload;

  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);

  /* Decrement ref count.  If negative, crash.  If positive, return.  If zero, unload the image then return. */

again:
  do {
    refc = image -> refcount;
    if (refc + inc <= 0) goto going_le_zero;
  } while (!oz_hw_atomic_setif_long (&(image -> refcount), refc + inc, refc));
  return (refc + inc);

going_le_zero:
  if (refc + inc < 0) oz_crash ("oz_knl_image_increfc: %s ref count negative (%d+%d)", image -> name, refc, inc);
  imagelist = image -> imagelist;
  lockimagelist (imagelist);
  if (!oz_hw_atomic_setif_long (&(image -> refcount), 0, refc)) {
    unlkimagelist (imagelist);
    goto again;
  }

  /* Unlink the image from the image list */

  for (limage = &(imagelist -> images); (ximage = *limage) != image; limage = &(ximage -> next)) {
    if (ximage == NULL) oz_crash ("image %s not found in its image list", image -> name);
  }
  *limage = ximage -> next;

  /* Unmap and delete all sections this image has mapped */

  while ((secload = image -> secloads) != NULL) {
    image -> secloads = secload -> next;
    if (secload -> seclock != NULL) oz_knl_section_iounlk (secload -> seclock);
    oz_knl_process_unmapsec (secload -> svpage);
    oz_knl_section_increfc (secload -> section, -1);
    oz_sys_pdata_free (image -> mprocmode, secload);
  }

  /* Unlock now so we can call our self to unload other images on the list   */
  /* This is ok because 'image' is the only pointer to the image struct left */

  unlkimagelist (imagelist);

  /* Unload all images this image refers to */

  while ((refd_image = image -> refd_images) != NULL) {
    image -> refd_images = refd_image -> next;
    oz_knl_image_increfc (refd_image -> image, -1);
    oz_sys_pdata_free (image -> mprocmode, refd_image);
  }

  /* Call the handler's specific unload routine */

  (*(image -> imagehand -> unload)) (image -> imagex, image -> mprocmode);

  /* Finally, close image file and free off the main image block */

  oz_knl_iochan_increfc (image -> iochan, -1);
  oz_sys_pdata_free (image -> mprocmode, image);
  return (0);
}

/************************************************************************/
/*									*/
/*  Lock all of the image's sections in memory				*/
/*									*/
/************************************************************************/

uLong oz_knl_image_lockinmem (OZ_Image *image, OZ_Procmode procmode)

{
  OZ_Image_Secload *secload;
  uLong sts;

  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);

  for (secload = image -> secloads; secload != NULL; secload = secload -> next) {	// loop through the image's sections
    if (secload -> seclock != NULL) continue;						// if already locked, skip it
    sts = oz_knl_section_iolock (procmode, 						// try to lock section in memory
                                 secload -> npages << OZ_HW_L2PAGESIZE, 		// - this many bytes
                                 OZ_HW_VPAGETOVADDR (secload -> svpage), 		// - starting at this address
                                 secload -> writable, 					// - maybe make sure it's read/write
                                 &(secload -> seclock), 				// - this is the unlock key
                                 NULL, NULL, NULL);					// - we don't care about physical pages
    if (sts != OZ_SUCCESS) return (sts);						// abort if error, maybe it's been unmapped
  }
  return (OZ_SUCCESS);
}

/************************************************************************/
/*									*/
/*  Get image's security attributes					*/
/*									*/
/************************************************************************/

OZ_Secattr *oz_knl_image_getsecattr (OZ_Image *image)

{
  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);
  oz_knl_secattr_increfc (image -> secattr, 1);
  return (image -> secattr);
}

/************************************************************************/
/*									*/
/*  Open image file and return i/o channel				*/
/*									*/
/************************************************************************/

static uLong open_image_file (OZ_Procmode procmode, const char *filename, OZ_Iochan **iochan_r)

{
  uLong sts;
  OZ_Handle h_image_file;
  OZ_IO_fs_open fs_open;

  *iochan_r = NULL;
  memset (&fs_open, 0, sizeof fs_open);
  fs_open.name = filename;
  fs_open.lockmode = OZ_LOCKMODE_PR;
  sts = oz_sys_io_fs_open2 (sizeof fs_open, &fs_open, 0, "OZ_IMAGE_DIR", &h_image_file);
  if (sts == OZ_SUCCESS) {
    sts = oz_knl_handle_takeout (h_image_file, OZ_PROCMODE_KNL, OZ_SECACCMSK_READ, OZ_OBJTYPE_IOCHAN, iochan_r, NULL);
    if (sts != OZ_SUCCESS) oz_crash ("oz_knl_image_load: image file handle lookup error %u", sts);
    oz_knl_iochan_increfc (*iochan_r, 1);
    oz_knl_iochan_setprocmode (*iochan_r, procmode);
    oz_knl_handle_putback (h_image_file);
    oz_knl_handle_release (h_image_file, OZ_PROCMODE_KNL);
  } else if (sts != OZ_NOSUCHFILE) {
    oz_sys_io_fs_printerror ("oz_knl_image_load: error %u opening file %s in OZ_IMAGE_DIR\n", sts, filename);
  }
  return (sts);
}

/************************************************************************/
/*									*/
/*  Read a number of bytes from the image file starting at a given vbn	*/
/*									*/
/*    Input:								*/
/*									*/
/*	imageargs = image arg list pointer				*/
/*	vbn  = starting virtual block to read from			*/
/*	offs = offset within that block to start reading		*/
/*	size = number of bytes to read					*/
/*	buff = where to put the data					*/
/*									*/
/*    Output:								*/
/*									*/
/*	oz_knl_image_read = OZ_SUCCESS : successful			*/
/*	                          else : error status			*/
/*	*buff = filled in with data					*/
/*									*/
/************************************************************************/

uLong oz_knl_image_read (OZ_Image_Args *imageargs, OZ_Dbn vbn, uLong size, void *buff)

{
  return (oz_knl_image_read2 (imageargs, vbn, 0, size, buff));
}

uLong oz_knl_image_read2 (OZ_Image_Args *imageargs, OZ_Dbn vbn, uLong offs, uLong size, void *buff)

{
  uLong sts;
  OZ_IO_fs_readblocks fs_readblocks;

  memset (&fs_readblocks, 0, sizeof fs_readblocks);
  fs_readblocks.size = size;
  fs_readblocks.buff = buff;
  fs_readblocks.svbn = vbn;
  fs_readblocks.offs = offs;
  sts = iowait (imageargs -> ioevent, imageargs -> iochan, OZ_IO_FS_READBLOCKS, sizeof fs_readblocks, &fs_readblocks);
  if (sts != OZ_SUCCESS) {
    oz_sys_io_fs_printerror ("oz_knl_image_read: error %u reading %u bytes from image file %s at block %u\n", sts, size, imageargs -> imagename, vbn);
  }
  return (sts);
}

/************************************************************************/
/*									*/
/*  Do I/O and wait for it to complete					*/
/*									*/
/************************************************************************/

static uLong iowait (OZ_Event *ioevent, OZ_Iochan *iochan, uLong funcode, uLong as, void *ap)

{
  uLong sts;
  volatile uLong status;

  status = OZ_PENDING;
  sts = oz_knl_iostart (iochan, OZ_PROCMODE_KNL, NULL, NULL, &status, ioevent, NULL, NULL, funcode, as, ap);
  if (sts == OZ_STARTED) {
    while ((sts = status) == OZ_PENDING) {
      oz_knl_event_waitone (ioevent);
      oz_knl_event_set (ioevent, 0);
    }
  }
  return (sts);
}

/************************************************************************/
/*									*/
/*  Copy image list from old process to new process.  Actually, the 	*/
/*  list itself has already been copied as a result of the 		*/
/*  oz_knl_process_copy operation.  So all we have to do is increment 	*/
/*  reference counts.  The new process is current on the cpu.		*/
/*									*/
/*  Image list doesn't have to be locked as no one outside the caller 	*/
/*  knows the process exists yet					*/
/*									*/
/************************************************************************/

uLong oz_knl_imagelist_copied (int nsecmaps, OZ_Section *oldsections[], OZ_Section *newsections[])

{
  int i;
  OZ_Image *image;
  OZ_Image_Refd_image *refd_image;
  OZ_Image_Secload *secload;
  OZ_Imagelist *imagelist;
  uLong sts;

  imagelist = OZ_SYS_PDATA_FROM_KNL (OZ_PROCMODE_KNL) -> imagelist;			// point to new process' image list

  imagelist -> event = NULL;								// we don't want to share lock event flag
  sts = oz_knl_event_create (21, "oz_knl_imagelist lock", NULL, &(imagelist -> event)); // create new lock event flag
  if (sts != OZ_SUCCESS) return (sts);
  oz_knl_event_set (imagelist -> event, 1);						// mark it unlocked

  for (image = imagelist -> images; image != NULL; image = image -> next) {		// loop through all its images
    oz_knl_iochan_increfc (image -> iochan, 1);						// inc image file IO channel refcount
											// ... as the pointer was just copied
    for (secload = image -> secloads; secload != NULL; secload = secload -> next) {	// loop through all image sections
      secload -> seclock = NULL;							// new pages aren't locked by us
      for (i = nsecmaps; -- i >= 0;) {							// scan the given arrays
        if (secload -> section == oldsections[i]) break;				// see if we found the old section
      }
      if (i < 0) secload -> section = NULL;						// section no longer mapped to process, forget about it
      else {
        secload -> section = newsections[i];						// save new section pointer
        oz_knl_section_increfc (newsections[i],  1);					// increment new refcount
      }
    }
    for (refd_image = image -> refd_images; refd_image != NULL; refd_image = refd_image -> next) {
      if (OZ_HW_ISSYSPAGE (OZ_HW_VADDRTOVPAGE (refd_image -> image))) {
        oz_knl_image_increfc (refd_image -> image, 1);					// increment referenced images' refcount
      }
    }
  }
  return (OZ_SUCCESS);									// successful
}

/************************************************************************/
/*									*/
/*  Called by process termination routine to get rid of imagelist 	*/
/*  struct.  It unloads any images that might still be there.		*/
/*									*/
/************************************************************************/

void oz_knl_imagelist_close (void)

{
  OZ_Image *image;
  OZ_Imagelist *imagelist;
  OZ_Pdata *pdata;

  pdata     = OZ_SYS_PDATA_FROM_KNL (OZ_PROCMODE_KNL);
  imagelist = pdata -> imagelist;							// point to current process' image list
  if (imagelist != NULL) {								// see if it has one
    while ((image = imagelist -> images) != NULL) oz_knl_image_increfc (image, -1);	// ok, release any loaded images
    oz_knl_event_increfc (imagelist -> event, -1);					// release the locking event flag
    oz_sys_pdata_free (OZ_PROCMODE_KNL, imagelist);					// free off the struct
    pdata -> imagelist = NULL;								// (in case it matters)
  }
}

/************************************************************************/
/*									*/
/*  Return first/next image on list					*/
/*									*/
/*    Input:								*/
/*									*/
/*	lastimage = NULL : get first image				*/
/*	            else : points to last image fetched			*/
/*	sysimage = 0 : get process local images				*/
/*	           1 : get system global images				*/
/*	smplevel = softint						*/
/*									*/
/*    Output:								*/
/*									*/
/*	oz_knl_image_next = NULL : no (more) images			*/
/*	                    else : image pointer, refcount incd		*/
/*									*/
/************************************************************************/

OZ_Image *oz_knl_image_next (OZ_Image *lastimage, int sysimage, int lockit)

{
  OZ_Image *nextimage;
  OZ_Imagelist *imagelist;
  OZ_Pdata *pdata;

  OZ_KNL_CHKOBJTYPE (lastimage, OZ_OBJTYPE_IMAGE);
  pdata     = OZ_SYS_PDATA (sysimage ? OZ_PROCMODE_SYS : OZ_PROCMODE_KNL);
  imagelist = pdata -> imagelist;
  if (imagelist == NULL) return (NULL);
  if (lockit) lockimagelist (imagelist);
  if (lastimage != NULL) nextimage = lastimage -> next;
                    else nextimage = imagelist -> images;
  if (lockit) {
    if (nextimage != NULL) oz_knl_image_increfc (nextimage, 1);
    unlkimagelist (imagelist);
  }
  return (nextimage);
}

/************************************************************************/
/*									*/
/*  Return pointer to list of sections loaded by this image		*/
/*									*/
/************************************************************************/

OZ_Image_Secload *oz_knl_image_secloads (OZ_Image *image)

{
  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);
  return (image -> secloads);
}

/************************************************************************/
/*									*/
/*  Get an image's name							*/
/*									*/
/************************************************************************/

const char *oz_knl_image_name (OZ_Image *image)

{
  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);
  return (image -> name);
}

/************************************************************************/
/*									*/
/*  Get an image's base address						*/
/*									*/
/************************************************************************/

void *oz_knl_image_base (OZ_Image *image)

{
  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);
  return (image -> baseaddr);
}

/************************************************************************/
/*									*/
/*  Get an image's IO channel						*/
/*									*/
/*  Note:  It is caller's responsibility to inc refcount if needed.  	*/
/*									*/
/************************************************************************/

OZ_Iochan *oz_knl_image_iochan (OZ_Image *image)

{
  OZ_KNL_CHKOBJTYPE (image, OZ_OBJTYPE_IMAGE);
  return (image -> iochan);
}

/************************************************************************/
/*									*/
/*  Lock/Unlock an Image List						*/
/*									*/
/************************************************************************/

static void lockimagelist (OZ_Imagelist *imagelist)

{
  while (oz_knl_event_set (imagelist -> event, 0) == 0) {
    oz_knl_event_waitone (imagelist -> event);
  }
}

static void unlkimagelist (OZ_Imagelist *imagelist)

{
  oz_knl_event_set (imagelist -> event, 1);
}