This directory contains the code for a minikernel that is booted by
auxiliary microVAX processors configured on the same Q-bus backplane as
the UNIX host processor.

The kernel was written by Mike Parker at McGill University.  It is a
single process kernel, in that it only supports one distinct process
memory context. It does, however, provide asynchronous software
interrupts.  The system calls this kernel provides are enumerated
below.  They are used by RCI in creating and operating the RCI real
time control tasks.

Calls which are similar to those provided by UNIX:

brk(addr)
  Set the high water mark of user memory to the indicated address.
  Note that the sbrk() routine of UNIX is actually a library routine;
  it is automatically supported as well.  But note that you probably
  don't want to use sbrk(); see the getmem() call below for why not.

cmkrnl(fxn, arg)
  Execute a function with the specified argument in kernel mode.  This
  call is not present in standard 4.3BSD.

exit(code)
  Stop execution of the user process.  The exit code is reported back
  to the arbiter CPU if the arbiter has so requested.

getpagesize()
  Returns the pagesize used by the kernel.

close(fd)
ioctl(fd, cmd, data)
read(fd, buffer, size)
write(fd, buffer, size)
fstat(fd, buf)
  These calls emulate the close(), read(), write(), ioctl(), and fstat()
  calls on UNIX, although the emulation is limited to the default file
  descriptors 0, 1, and 2 (stdin, stdout, and stderr) which are in turn
  hard-wired to the CPU console serial line.  The calls are provided so
  that the stdio library can be used on the auxiliary CPU (though it
  will work only when speaking to the default file descriptors, of
  course: scanf(), printf(), getchar(), putchar(), etc).

Calls unique in character to the auxiliary kernel:

gettime100()
  Returns the system time as measured by the 100 hz clock on the
  microVAX CPU.

getmapplace(location)
  Returns the address of various special memory locations that are
  available for use by the process.  These locations are defined in
  'getmapplace.h', and include:

	MAP_TOY		-- address area of the Time of Year clock.

	MAP_QBUS	-- address of the Q-bus.

	MAP_IOPAGE	-- address of the Q-bus I/O page.

	MAP_QBUSMAPREGS -- address of Q-bus mapping registers.

	MAP_UAPAGE	-- a blackboard used for rapid, shared memory
			   communication between the kernel and the
			   process.  The functionality of this memory
			   area is described below.

	MAP_BBOARD(cpu) -- the local virtual address the inter-CPU
			   communication blackboard for the indicated
			   cpu.  The functionality of this blackboard
			   is described below.  The argument cpu should
			   be 0 to refer to the arbiter CPU or 1, 2, or
			   3 for the auxiliary CPUs.

gettimesch()
  Returns the time kept by the real time clock (if a real time clock is
  available; otherwise the call will always return 0).

schslippage()
  A debugging call that was used in determining (and fixing) the
  problem with missed doorbell interrupts ... this call will return the
  number of missed doorbell interrupts that have been detected in
  association with the real time clock.  If you don't know about the
  doorbell interrupt problem, don't worry about this system call.

sendsignal(cpu, sig)
  Send a software signal to the indicated CPU.  If the destination CPU
  is the UNIX arbiter CPU, then 'sig' will be interpreted as a UNIX
  signal to be delivered to any process that has so requested such
  delivery.  Signals can also be sent to auxiliary CPUs; software
  running on the auxiliary CPUs can set up handlers for these (see the
  handlesig() call below).

  NOTE: The sendsignal() call currently works only when sending signals
  to the arbiter or when sending to the CPU the call is being made on.

ast100(time, fxn, arg)
  Calls the indicated 'fxn' with argument 'arg' as a software interrupt
  when the system 100 Hz. timer reaches the indicated 'time'.  If the
  time is less than or equal to the current time (as would be returned
  by gettime100()), the software interrupt will be scheduled
  immediately.

astsch(time, fxn, arg)
  Calls the indicated 'fxn' with argument 'arg' as a software interrupt
  when the system real time clock reaches the indicated 'time'.  As
  with ast100(), if the time is past the interrupt is immediate.
  
getmem(size)
  Grow the system memory area by 'size' bytes and return a pointer to
  the allocated space. This is the equivalent of an ``sbrk()'' system
  call.  We have to do this with a separate system call (instead of
  using brk() directly, which is what sbrk() does on UNIX), because it
  is also possible for the memory to be allocated on this CPU at the
  request of the arbiter CPU.  Therefore, the information about where
  the ``high water point'' of memory currently is must be kept in the
  kernel.

clearast()
  Reenable sofware interrupts.  Any software interrupts which were
  scheduled but blocked will be delivered.

blockast()
  Block all software interrupts (ASTs).  Any software interrupts which
  are scheduled before the next clearast() will not happen until then.

handlesig(num, handler)
  Causes the function 'handler' to be called as a soft interrupt when
  the signal 'num' is received from another CPU (see 'sendsig()').

badaddr(addr, size)
  Similar in function to the UNIX kernel level function 'badaddr':
  attempts to access address 'addr' as a byte, word, or longword
  reference (corresponding to a 'size' of 1, 2, or 4); the return value
  is 0 if the reference succeeded and nonzero if it failed (caused a
  machine check).

debugger()
  Enters a very simple debugger utility, with interaction provided
  through the auxiliary CPU console interface.  This debugger is
  intended for debugging the auxiliary CPU's kernel, though it is also
  possible to examine things in the user process on the auxiliary CPU
  with it, if you know what you're doing.

halt()
  Halts the cpu.

exportclock(cpu, code)
  Used to broadcast the CPU's internal 100 Hz clock to another CPU,
  thereby achieving a global wall clock.  The clock value will appear
  as a value in the target CPU's inter-CPU blackboard.  The 'code'
  describes how the exporting should be done:

	0	-- don't export the clock.

	1	-- export the clock only by updating the value in the
		   target CPU's blackboard.

	2	-- update the value in the target CPU's blackboard, and
		   interrupt the CPU as well, so that it will be able
		   to trigger asynchronous events on the broadcast
		   clock.

  Each CPU's clock value is always present in its own blackboard area.

schedast(cpu, time, fxn, arg)
  Calls the indicated 'fxn' with argument 'arg' as a software interrupt
  when the wallclock associated with the indicated 'cpu' reaches the
  specified 'time'.  In order for the call to work, the indicated CPU
  must be broadcasting its clock to the current CPU (see exportclock(),
  above).


In addition to the system calls, there are two memory areas available
to provide shared memory (ie, fast) communication between various
pieces of software.  One area allows user-level code to communicate
with the kernel it's running under; the other is for inter-CPU
communication.  The addresses of these ``blackboards'' can be obtained
with the getmapplace() call (qv).

The local blackboard is called the user area page. The structure which
represents it is defined in "uapage.h".  The entire structure is
read/write to user code.  The fields include:

	rt_handler	-- Writable variable which is a pointer to a
			   timeout handler function. Setting this to a
			   null pointer clears the timeout.  (This
			   variable is actually an int rather than a
			   pointer to function; cast the pointer to int
			   before assigning.)

	rt_timeout	-- Writable variable which specifies the length
			   of time which should elapse before the
			   timeout fires.  The clock used is the built
			   in microVAX 100 Hz. clock.  Setting this to
			   1 or less will cause firing on the next
			   clock tick.

	rt_argument	-- Writable variable which specifies the
			   argument which the timeout handler function
			   is to be called with.

	block_asts	-- Writable variable which performs a similar
			   function to blockast(): setting it non-zero
			   masks all software interrupts.  This is made
			   available because the syscall mechanism (as
			   used by blockast()) takes a comparatively
			   long time, whereas writing a variable is
			   very fast.

	astqsize	-- Variable describing the number of software
			   interrupts which are currently pending.
			   This variable is technically read/write, but
			   should never be written to.  Writing to it
			   won't break the kernel, but it may ruin its
			   usefulness, because it won't necessarily be
			   correct thereafter.  This is provided
			   because simply clearing the block_asts
			   variable is not sufficient to unblock ASTs
			   properly: the system doesn't hear about the
			   clearing immediately.  However, most of the
			   time there's nothing pending, and we don't
			   want to take the time for a syscall when
			   it's not necessary.  If this variable is
			   non-zero after clearing block_asts, the
			   clearast() syscall should be made to cause
			   the kernel to check for pending ASTs.

The inter-CPU blackboard is defined by the UNIX system file
/sys/local/kacomm.h.  Each CPU in the system (including the arbiter
CPU) has a blackboard that is exported onto the Q-bus.  It is a small
structure, that at the moment only contains the clock values that are
used by the CPU.  This area is read/write to user code as well, though
messing with the fields may cause software interrupts scheduled for the
future to be delivered at unexpected times if you don't know what
you're doing.  The fields are:

	timesch		-- value of the real time clock.

	time100		-- time of the built in 100 Hz. microVAX clock.

	time_rt[4]	-- time value associated with a broadcast
			   clock.  If cpu 'n' is broadcasting a clock
			   to CPU 'k', then it will write the broadcast
			   clock value into the time_rt[n] of CPU k's
			   blackboard.  The array size of 4 reflects
			   the current limit of 4 microVAX CPU boards
			   on the Q-bus.  CPU 'n' will always keep
			   time_rt[n] in its own blackboard up-to-date.
