[comp.os.minix] Use of AT Extended Memory

hubble@cae780.TEK.COM (Larry Hubble) (04/30/87)

I have managed to get my root file system to run in extended memory of my
AT clone.  (NOTE that extended memory, not expanded memory).  In addition,
I set up one partition of my hard disk to have a 384K copy of the root
file system.  When booting, the hard disk partition (/dev/hd2 in my case)
is copied to the RAM disk which has been moved to extended memory.

384K is large enough that MINIX now runs very well and copying from hard
disk during bootup takes only a few seconds.  (Of course, now only one
floppy disk is required to get MINIX up and running.

This required changes to:
    1)  kernel/memory.c to call new routines to tranfer from extended
        memory rather than normal RAM.
    2)  fs/main.c to tell mm that the RAM disk is zero length, copy hard
        disk for root filesystem, and a new origin for RAM disk.
    3)  kernel/em_xfer.s is a new routine to transfer to/from extended
        AT memory
    4)  kernel/makefile to include em_xfer.s in link of kernel

---------------------------------------------------------------------------
Here are the specific changes to kernel/memory.c. Change the last six lines
of routine do_mem() to:

  /* Copy the data. */
  if (device != RAM_DEV)  {
	if (m_ptr->m_type == DISK_READ)
		phys_copy(mem_phys, user_phys, (long) count);
	else
		phys_copy(user_phys, mem_phys, (long) count);
  } else {
	if (count & 1)
		panic("RAM disk got odd byte count", m_ptr);
	words = ( (unsigned)count ) >> 1;
	if (m_ptr->m_type == DISK_READ)
		status = em_xfer(mem_phys, user_phys, words);
	else
		status = em_xfer(user_phys, mem_phys, words);
	if (status != 0) count = -1;
  }
  return(count);

---------------------------------------------------------------------------
Here are the specific changes to fs/main.c.  Replace the whole routine
load_ram() to (Don't forget to change the define ROOT_COPY for your hard
disk, or simply define to BOOT_DEV if you still want to use floppies):

#define ROOT_COPY      0x302    /* Copy of root partition on hd part. 2 */
#define EM_ORIGIN  0x0100000    /* Origin of AT extended memory */
PRIVATE load_ram()
{
/* The root diskette contains a block-by-block image of the root file system
 * starting at 0.  Go get it and copy it to the RAM disk. 
 */

  register struct buf *bp, *bp1;
  int count;
  long k_loaded;
  struct super_block *sp;
  block_nr i;
  phys_clicks ram_clicks, init_org, init_text_clicks, init_data_clicks;
  extern phys_clicks data_org[INFO + 2];
  extern struct buf *get_block();

  /* Get size of INIT by reading block on diskette where 'build' put it. */
  init_org = data_org[INFO];
  init_text_clicks = data_org[INFO + 1];
  init_data_clicks = data_org[INFO + 2];

  /* Get size of RAM disk by reading root file system's super block */
  bp = get_block(ROOT_COPY, SUPER_BLOCK, NORMAL);  /* get RAM super block */
  copy(super_block, bp->b_data, sizeof(struct super_block));
  sp = &super_block[0];
  if (sp->s_magic != SUPER_MAGIC)
	panic("Diskette in drive 0 is not root file system", NO_NUM);
  count = sp->s_nzones << sp->s_log_zone_size;	/* # blocks on root dev */
  if (count > MAX_RAM) panic("RAM disk is too big. # blocks = ", count);
  ram_clicks = count * (BLOCK_SIZE/CLICK_SIZE);
  put_block(bp, FULL_DATA_BLOCK);

  /* Tell MM the origin and size of INIT, and the amount of memory used for the
   * system plus RAM disk combined, so it can remove all of it from the map.
   */
  m1.m_type = BRK2;
  m1.m1_i1 = init_text_clicks;
  m1.m1_i2 = init_data_clicks;
  m1.m1_i3 = init_org + init_text_clicks + init_data_clicks;
  m1.m1_p1 = (char *) init_org;
  if (sendrec(MM_PROC_NR, &m1) != OK) panic("FS Can't report to MM", NO_NUM);

  /* Tell RAM driver where RAM disk is and how big it is. */
  m1.m_type = DISK_IOCTL;
  m1.DEVICE = RAM_DEV;
  m1.POSITION = EM_ORIGIN;
  m1.COUNT = count;
  if (sendrec(MEM, &m1) != OK) panic("Can't report size to MEM", NO_NUM);

  /* Copy the blocks one at a time from the root diskette to the RAM */
  printf("Loading RAM disk from partition 2 of hard disk. Loaded:   0K ");
  for (i = 0; i < count; i++) {
	bp = get_block(ROOT_COPY, (block_nr) i, NORMAL);
	bp1 = get_block(ROOT_DEV, i, NO_READ);
	copy(bp1->b_data, bp->b_data, BLOCK_SIZE);
	bp1->b_dirt = DIRTY;
	put_block(bp, I_MAP_BLOCK);
	put_block(bp1, I_MAP_BLOCK);
	k_loaded = ( (long) i * BLOCK_SIZE)/1024L;	/* K loaded so far */
	if (k_loaded % 5 == 0) printf("\b\b\b\b\b%3DK %c", k_loaded, 0);
  }

  printf("\rRAM disk loaded.  It resides in AT Extended memory.             \n\n");
}

----------------------------------------------------------------------------

And finally, the new routine:

|---------------------------------------------------------------------------
|                New routine kernel/em_xfer.s
|---------------------------------------------------------------------------
|
|  This file contains one routine which transfers words between user memory
|  and extended memory on an AT or clone.  A BIOS call (INT 15h, Func 87h)
|  is used to accomplish the transfer.  The BIOS call is "faked" by pushing
|  the processor flags on the stack and then doing a far call to the actual
|  BIOS location.  An actual INT 15h would get a MINIX complaint from an
|  unexpected trap.
|
|  NOTE:  WARNING:  CAUTION: ...
|  Before using this routine, you must find your BIOS address for INT 15h.
|  The debug command "d 0:54 57" will give you the segment and address of
|  the BIOS call.  On my machine this generates:
|      0000:0050      59 F8 00 F0                          Y...
|  These values are then plugged into the two strange ".word xxxx" lines
|  near the end of this routine.  They correspond to offset=0xf859 and
|  seg=0xf000.  The offset is the first two bytes and the segment is the
|  last two bytes (Note the byte swap).
|
|  This particular BIOS routine runs with interrupts off since the 80286
|  must be placed in protected mode to access the memory above 1 Mbyte.
|  So there should be no problems using the BIOS call.
|
	.text
gdt:				| Begin global descriptor table
					| Dummy descriptor
	.word 0		| segment length (limit)
	.word 0		| bits 15-0 of physical address
	.byte 0		| bits 23-16 of physical address
	.byte 0		| access rights byte
	.word 0		| reserved
					| descriptor for GDT itself
	.word 0		| segment length (limit)
	.word 0		| bits 15-0 of physical address
	.byte 0		| bits 23-16 of physical address
	.byte 0		| access rights byte
	.word 0		| reserved
src:					| source descriptor
srcsz:	.word 0		| segment length (limit)
srcl:	.word 0		| bits 15-0 of physical address
srch:	.byte 0		| bits 23-16 of physical address
	.byte 0x93	| access rights byte
	.word 0		| reserved
tgt:					| target descriptor
tgtsz:	.word 0		| segment length (limit)
tgtl:	.word 0		| bits 15-0 of physical address
tgth:	.byte 0		| bits 23-16 of physical address
	.byte 0x93	| access rights byte
	.word 0		| reserved
					| BIOS CS descriptor
	.word 0		| segment length (limit)
	.word 0		| bits 15-0 of physical address
	.byte 0		| bits 23-16 of physical address
	.byte 0		| access rights byte
	.word 0		| reserved
					| stack segment descriptor
	.word 0		| segment length (limit)
	.word 0		| bits 15-0 of physical address
	.byte 0		| bits 23-16 of physical address
	.byte 0		| access rights byte
	.word 0		| reserved

|
|
|  Execute a transfer between user memory and extended memory.
|
|  status = em_xfer(source, dest, count);
|
|    Where:
|       status => return code (0 => OK)
|       source => Physical source address (32-bit)
|       dest   => Physical destination address (32-bit)
|       count  => Number of words to transfer
|
|
|
	.globl	_em_xfer
_em_xfer:
|	

	push	bp		| Save registers
	mov	bp,sp
	push	si
	push	es
	push	cx
|
|  Pick up source and destination addresses and update descriptor tables
|
	mov ax,4(bp)
	seg cs
	mov srcl,ax
	mov ax,6(bp)
	seg cs
	movb srch,al
	mov ax,8(bp)
	seg cs
	mov tgtl,ax
	mov ax,10(bp)
	seg cs
	movb tgth,al
|
|  Update descriptor table segment limits
|
	mov cx,12(bp)
	mov ax,cx
	add ax,ax
	seg cs
	mov tgtsz,ax
	seg cs
	mov srcsz,ax
|
|  Now do actual DOS call
|
	push cs
	pop es
	seg cs
	mov si,#gdt
	movb ah,#0x87
	pushf
	.byte 0x9a		| Do a far call to BIOS routine
	.word 0xf859		|      "
	.word 0xf000		|      "
|
|  All done, return to caller.
|

	pop	cx		| restore registers
	pop	es
	pop	si
	mov	sp,bp
	pop	bp
	ret

----------------------------------------------------------------------------

That's all.  Good luck if you try it.

---------------------------------------------------------------------------
                    Larry R. Hubble     Tektronix/CAE Systems Division
                    (408) 748-4782      5302 Betsy Ross Dr.
    {hplabs,tektronix}hubble@cae780     Santa Clara, CA 95054