[comp.os.minix] Enhancements to 1.2 to use AT extended memory and hard disk root

ast@cs.vu.nl (Andy Tanenbaum) (08/16/87)

Although MINIX 1.2 is now been sent off and I don't plan on 1.3 until 1988, I
did make some changes to 1.2 that will eventually be in 1.3 and which are
worth passing on.  They essentially incorporate Larry Hubble's patches
of several months ago, but in a more flexible way.

The changes have the following effect.  When the initial menu comes up, you
can insert the root file system floppy, and it is read in, as before.  However,
if you leave the boot diskette in drive 0 and hit the = key, MINIX reads the
"superblock" of the boot diskette, sees that it is not a valid file system, 
and loads the root file system from a dedicated hard disk partition containing
a bit-for-bit image of the root file system.  The hard disk partition to use
is determined by RAM_IMAGE in fs/main.c.  This fix is compatible with the old
way of booting, allows the hard disk to be used, but also allows floppies to
override the hard disk.

The other change is only applicable to AT's, but the binary will work on a PC
as well.  If the root file system is 256K or more (either read from the floppy
or from the hard disk, it doesn't matter), the root file system is placed in
extended memory (above 1 MB), otherwise it goes in "low" core.  The current 
distributions use either 240K or 116K root devices, and these will continue to
go in low core.  While this distinction is somewhat heuristic, it seems
unlikely that anyone will want more than 255K of root file system in a
computer with only 640K of memory.  It seems equally unlikely that anyone
with extended memory will have less than 256K of it.  In any event, the
constant MAX_CRD (MAXimum Core Ram Disk) can be changed if need be.  

The advantage of these two fixes is that the binary remains compatible for
PCs, ATs, machines with root file systems on hard disk, those without, machines
with extended memory and those without.  It is all determined dynamically.
Needless to say, when making a root file system to be used in extended RAM,
be sure to set the root file system size <= to the size of the extended
memory.  If you try to put a 1M RAM disk in a 512K extended memory, it won't
work (you didn't expect that really, did you)?

There are five files affected: 
  kernel/main.c  kernel/klib88.s  kernel/memory.c  fs/main.c  mm/main.c

If there are problems, please post them.  The diff listings below were
made with Erik Baalbergen's diff program and the new files can be generated
with his fix program, e.g.
  fix klib88.s klib88.diff >newklib88.s

THE DIFFS ARE RELATIVE TO 1.2, NOT 1.1, SO WHEN YOU RUN FIX, USE THE 1.2
KLIB88.S, NOT THE 1.1 VERSION, (DITTO FOR THE OTHER FILES).

Andy Tanenbaum (ast@cs.vu.nl)
---------------

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin
echo Extracting \f\s\:\m\a\i\n\.\d\i\f\f
sed 's/^X//' > \f\s\:\m\a\i\n\.\d\i\f\f << '+ END-OF-FILE '\f\s\:\m\a\i\n\.\d\i\f\f
X27c27,30
X< #define MAX_RAM          512	/* maxium RAM disk size in blocks */
X---
X> #define MAX_RAM        16384	/* maximum RAM disk size in blocks */
X> #define RAM_IMAGE (dev_nr)0x303	/* major-minor dev where root image is kept */
X> #define EM_ORIGIN   0x100000	/* origin of extended memory RAM disk on AT */
X> #define MAX_CRD           255	/* if root fs > MAX_CRD, use extended mem */
X225c228,230
X<   phys_clicks ram_clicks, init_org, init_text_clicks, init_data_clicks;
X---
X>   dev_nr root_device;
X>   phys_clicks ram_clicks, init_org, init_text_clicks, init_data_clicks;
X>   long base;
X233,239c238,256
X< 
X<   /* Get size of RAM disk by reading root file system's super block */
X<   bp = get_block(BOOT_DEV, SUPER_BLOCK, NORMAL);  /* get RAM super block */
X<   copy(super_block, bp->b_data, sizeof(struct super_block));
X<   sp = &super_block[0];
X<   if (sp->s_magic != SUPER_MAGIC)
X< 	panic("Diskette in drive 0 is not root file system", NO_NUM);
X---
X>   base = (long) init_org + (long) init_text_clicks + (long) init_data_clicks;
X>   base = base << CLICK_SHIFT;
X> 
X>   /* Get size of RAM disk by reading root file system's super block.
X>    * First read block 0 from the floppy.  If this is a valid file system, use
X>    * it as the root image, otherwise try the hard disk (RAM_IMAGE).  
X>    */
X>   root_device = BOOT_DEV;	/* try floppy disk first */
X>   bp = get_block(root_device, SUPER_BLOCK, NORMAL);  /* get RAM super block */
X>   copy(super_block, bp->b_data, sizeof(struct super_block));
X>   sp = &super_block[0];
X>   if (sp->s_magic != SUPER_MAGIC) {
X> 	root_device = RAM_IMAGE;
X> 	bp = get_block(root_device, SUPER_BLOCK, NORMAL);  /* get RAM super block */
X> 	copy(super_block, bp->b_data, sizeof(struct super_block));
X> 	sp = &super_block[0];
X>   }
X>   if (sp->s_magic != SUPER_MAGIC)
X> 	panic("Invalid root file system", NO_NUM);
X244a262,272
X>   /* There are two possibilities now (by convention):  
X>    *    count < MAX_CRD  ==> RAM disk is in core
X>    *    count >=MAX_CRD  ==> RAM disk is in extended memory (AT only)
X>    * In the latter case, tell MM that RAM disk size is 0 and tell the ram disk
X>    * driver than the device begins at 1MB.
X>    */
X>   if (count > MAX_CRD) {
X> 	ram_clicks = 0;		/* MM does not have to allocate any core */
X> 	base = EM_ORIGIN;	/* tell RAM disk driver RAM disk origin */
X>   }
X> 
X258,259c286
X<   m1.POSITION = (long) init_org + (long) init_text_clicks + init_data_clicks;
X<   m1.POSITION = m1.POSITION << CLICK_SHIFT;
X---
X>   m1.POSITION = base;
X264,266c291,295
X<   printf("Loading RAM disk from root diskette.      Loaded:   0K ");
X<   for (i = 0; i < count; i++) {
X< 	bp = get_block(BOOT_DEV, (block_nr) i, NORMAL);
X---
X>   if (ram_clicks == 0) 	
X> 	printf("RAM disk of %d blocks is in extended memory\n\n", count);
X>   printf("Loading RAM disk.                          Loaded:   0K ");
X>   for (i = 0; i < count; i++) {
X> 	bp = get_block(root_device, (block_nr) i, NORMAL);
X273,276c302,307
X< 	if (k_loaded % 5 == 0) printf("\b\b\b\b\b%3DK %c", k_loaded, 0);
X<   }
X< 
X<   printf("\rRAM disk loaded.  Please remove root diskette.           \n\n");
X---
X> 	if (k_loaded % 5 == 0) printf("\b\b\b\b\b\b%4DK %c", k_loaded, 0);
X>   }
X>   if (root_device == BOOT_DEV)
X> 	printf("\rRAM disk loaded.    Please remove root diskette.           \n\n");
X>   else
X> 	printf("\rRAM disk loaded.                                           \n\n");
+ END-OF-FILE fs:main.diff
chmod 'u=rw,g=r,o=r' \f\s\:\m\a\i\n\.\d\i\f\f
set `sum \f\s\:\m\a\i\n\.\d\i\f\f`
sum=$1
case $sum in
52706)	:;;
*)	echo 'Bad sum in '\f\s\:\m\a\i\n\.\d\i\f\f >&2
esac
echo Extracting \k\e\r\n\:\m\a\i\n\.\d\i\f\f
sed 's/^X//' > \k\e\r\n\:\m\a\i\n\.\d\i\f\f << '+ END-OF-FILE '\k\e\r\n\:\m\a\i\n\.\d\i\f\f
X29a30
X> #define EM_VEC          0x15	/* vector for extended memory BIOS calls */
X128,131c129,134
X<   if (pc_at)
X< 	  set_vec(AT_WINI_VECTOR, wini_int, base_click);
X<   else
X< 	  set_vec(XT_WINI_VECTOR, wini_int, base_click);
X---
X>   if (pc_at) {
X> 	set_vec(AT_WINI_VECTOR, wini_int, base_click);
X> 	phys_copy(phys_b + 4L*EM_VEC, 4L*EM_VEC, 4L);	/* extended mem vec */
X>   } else {
X> 	set_vec(XT_WINI_VECTOR, wini_int, base_click);
X>   }
+ END-OF-FILE kern:main.diff
chmod 'u=rw,g=r,o=r' \k\e\r\n\:\m\a\i\n\.\d\i\f\f
set `sum \k\e\r\n\:\m\a\i\n\.\d\i\f\f`
sum=$1
case $sum in
48322)	:;;
*)	echo 'Bad sum in '\k\e\r\n\:\m\a\i\n\.\d\i\f\f >&2
esac
echo Extracting \k\l\i\b\8\8\.\d\i\f\f
sed 's/^X//' > \k\l\i\b\8\8\.\d\i\f\f << '+ END-OF-FILE '\k\l\i\b\8\8\.\d\i\f\f
X20a21
X> |   em_xfer:	read or write AT extended memory using the BIOS
X25c26
X< .globl _wreboot, _dma_read, _dma_write
X---
X> .globl _wreboot, _dma_read, _dma_write, _em_xfer
X495,561c496,692
X< 
X< 
X< 
X< |*===========================================================================*
X< |*				reboot & wreboot			     *
X< |*===========================================================================*
X< | This code reboots the PC
X< 
X< _reboot:
X< 	cli			| disable interrupts
X< 	mov ax,#0x20		| re-enable interrupt controller
X< 	out 0x20
X< 	call resvec		| restore the vectors in low core
X< 	mov ax,#0x40
X< 	mov ds,ax
X< 	mov ax,#0x1234
X< 	mov 0x72,ax
X< 	mov ax,#0xFFFF
X< 	mov ds,ax
X< 	mov ax,3
X< 	push ax
X< 	mov ax,1
X< 	push ax
X< 	reti
X< 
X< _wreboot:
X< 	cli			| disable interrupts
X< 	mov ax,#0x20		| re-enable interrupt controller
X< 	out 0x20
X< 	call resvec		| restore the vectors in low core
X< 	xor ax,ax		| wait for character before continuing
X< 	int 0x16		| get char
X< 	mov ax,#0x40
X< 	mov ds,ax
X< 	mov ax,#0x1234
X< 	mov 0x72,ax
X< 	mov ax,#0xFFFF
X< 	mov ds,ax
X< 	mov ax,3
X< 	push ax
X< 	mov ax,1
X< 	push ax
X< 	reti
X< 
X< | Restore the interrupt vectors in low core.
X< resvec:	cld
X< 	mov cx,#2*71
X< 	mov si,#_vec_table
X< 	xor di,di
X< 	mov es,di
X< 	rep
X< 	movw
X< 	ret
X< 
X< | Some library routines use exit, so this label is needed.
X< | Actual calls to exit cannot occur in the kernel.
X< .globl _exit
X< _exit:	sti
X< 	jmp _exit
X< 
X< .data
X< lockvar:	.word 0		| place to store flags for lock()/restore()
X< vidlock:	.word 0		| dummy variable for use with lock prefix
X< splimit:	.word 0		| stack limit for current task (kernel only)
X< tmp:		.word 0		| count of bytes already copied
X< stkoverrun:	.asciz "Kernel stack overrun, task = "
X< _vec_table:	.zerow 142	| storage for interrupt vectors
X---
X> |===========================================================================
X> |                		em_xfer
X> |===========================================================================
X> |
X> |  This file contains one routine which transfers words between user memory
X> |  and extended memory on an AT or clone.  A BIOS call (INT 15h, Func 87h)
X> |  is used to accomplish the transfer.  The BIOS call is "faked" by pushing
X> |  the processor flags on the stack and then doing a far call to the actual
X> |  BIOS location.  An actual INT 15h would get a MINIX complaint from an
X> |  unexpected trap.
X> |
X> |  NOTE:  WARNING:  CAUTION: ...
X> |  Before using this routine, you must find your BIOS address for INT 15h.
X> |  The debug command "d 0:54 57" will give you the segment and address of
X> |  the BIOS call.  On my machine this generates:
X> |      0000:0050      59 F8 00 F0                          Y...
X> |  These values are then plugged into the two strange ".word xxxx" lines
X> |  near the end of this routine.  They correspond to offset=0xf859 and
X> |  seg=0xf000.  The offset is the first two bytes and the segment is the
X> |  last two bytes (Note the byte swap).
X> |
X> |  This particular BIOS routine runs with interrupts off since the 80286
X> |  must be placed in protected mode to access the memory above 1 Mbyte.
X> |  So there should be no problems using the BIOS call.
X> |
X> 	.text
X> gdt:				| Begin global descriptor table
X> 					| Dummy descriptor
X> 	.word 0		| segment length (limit)
X> 	.word 0		| bits 15-0 of physical address
X> 	.byte 0		| bits 23-16 of physical address
X> 	.byte 0		| access rights byte
X> 	.word 0		| reserved
X> 					| descriptor for GDT itself
X> 	.word 0		| segment length (limit)
X> 	.word 0		| bits 15-0 of physical address
X> 	.byte 0		| bits 23-16 of physical address
X> 	.byte 0		| access rights byte
X> 	.word 0		| reserved
X> src:					| source descriptor
X> srcsz:	.word 0		| segment length (limit)
X> srcl:	.word 0		| bits 15-0 of physical address
X> srch:	.byte 0		| bits 23-16 of physical address
X> 	.byte 0x93	| access rights byte
X> 	.word 0		| reserved
X> tgt:					| target descriptor
X> tgtsz:	.word 0		| segment length (limit)
X> tgtl:	.word 0		| bits 15-0 of physical address
X> tgth:	.byte 0		| bits 23-16 of physical address
X> 	.byte 0x93	| access rights byte
X> 	.word 0		| reserved
X> 					| BIOS CS descriptor
X> 	.word 0		| segment length (limit)
X> 	.word 0		| bits 15-0 of physical address
X> 	.byte 0		| bits 23-16 of physical address
X> 	.byte 0		| access rights byte
X> 	.word 0		| reserved
X> 					| stack segment descriptor
X> 	.word 0		| segment length (limit)
X> 	.word 0		| bits 15-0 of physical address
X> 	.byte 0		| bits 23-16 of physical address
X> 	.byte 0		| access rights byte
X> 	.word 0		| reserved
X> 
X> |
X> |
X> |  Execute a transfer between user memory and extended memory.
X> |
X> |  status = em_xfer(source, dest, count);
X> |
X> |    Where:
X> |       status => return code (0 => OK)
X> |       source => Physical source address (32-bit)
X> |       dest   => Physical destination address (32-bit)
X> |       count  => Number of words to transfer
X> |
X> |
X> |
X> _em_xfer:
X> 
X> 	push	bp		| Save registers
X> 	mov	bp,sp
X> 	push	si
X> 	push	es
X> 	push	cx
X> |
X> |  Pick up source and destination addresses and update descriptor tables
X> |
X> 	mov ax,4(bp)
X> 	seg cs
X> 	mov srcl,ax
X> 	mov ax,6(bp)
X> 	seg cs
X> 	movb srch,al
X> 	mov ax,8(bp)
X> 	seg cs
X> 	mov tgtl,ax
X> 	mov ax,10(bp)
X> 	seg cs
X> 	movb tgth,al
X> |
X> |  Update descriptor table segment limits
X> |
X> 	mov cx,12(bp)
X> 	mov ax,cx
X> 	add ax,ax
X> 	seg cs
X> 	mov tgtsz,ax
X> 	seg cs
X> 	mov srcsz,ax
X> |
X> |  Now do actual DOS call
X> |
X> 	push cs
X> 	pop es
X> 	seg cs
X> 	mov si,#gdt
X> 	movb ah,#0x87
X> 	pushf
X> 	int 0x15		| Do a far call to BIOS routine
X> |
X> |  All done, return to caller.
X> |
X> 
X> 	pop	cx		| restore registers
X> 	pop	es
X> 	pop	si
X> 	mov	sp,bp
X> 	pop	bp
X> 	ret
X> 
X> 
X> 
X> |*===========================================================================*
X> |*				reboot & wreboot			     *
X> |*===========================================================================*
X> | This code reboots the PC
X> 
X> _reboot:
X> 	cli			| disable interrupts
X> 	mov ax,#0x20		| re-enable interrupt controller
X> 	out 0x20
X> 	call resvec		| restore the vectors in low core
X> 	mov ax,#0x40
X> 	mov ds,ax
X> 	mov ax,#0x1234
X> 	mov 0x72,ax
X> 	mov ax,#0xFFFF
X> 	mov ds,ax
X> 	mov ax,3
X> 	push ax
X> 	mov ax,1
X> 	push ax
X> 	reti
X> 
X> _wreboot:
X> 	cli			| disable interrupts
X> 	mov ax,#0x20		| re-enable interrupt controller
X> 	out 0x20
X> 	call resvec		| restore the vectors in low core
X> 	xor ax,ax		| wait for character before continuing
X> 	int 0x16		| get char
X> 	mov ax,#0x40
X> 	mov ds,ax
X> 	mov ax,#0x1234
X> 	mov 0x72,ax
X> 	mov ax,#0xFFFF
X> 	mov ds,ax
X> 	mov ax,3
X> 	push ax
X> 	mov ax,1
X> 	push ax
X> 	reti
X> 
X> | Restore the interrupt vectors in low core.
X> resvec:	cld
X> 	mov cx,#2*71
X> 	mov si,#_vec_table
X> 	xor di,di
X> 	mov es,di
X> 	rep
X> 	movw
X> 	ret
X> 
X> | Some library routines use exit, so this label is needed.
X> | Actual calls to exit cannot occur in the kernel.
X> .globl _exit
X> _exit:	sti
X> 	jmp _exit
X> 
X> .data
X> lockvar:	.word 0		| place to store flags for lock()/restore()
X> vidlock:	.word 0		| dummy variable for use with lock prefix
X> splimit:	.word 0		| stack limit for current task (kernel only)
X> tmp:		.word 0		| count of bytes already copied
X> stkoverrun:	.asciz "Kernel stack overrun, task = "
X> _vec_table:	.zerow 142	| storage for interrupt vectors
+ END-OF-FILE klib88.diff
chmod 'u=rw,g=r,o=r' \k\l\i\b\8\8\.\d\i\f\f
set `sum \k\l\i\b\8\8\.\d\i\f\f`
sum=$1
case $sum in
16428)	:;;
*)	echo 'Bad sum in '\k\l\i\b\8\8\.\d\i\f\f >&2
esac
echo Extracting \m\e\m\o\r\y\.\d\i\f\f
sed 's/^X//' > \m\e\m\o\r\y\.\d\i\f\f << '+ END-OF-FILE '\m\e\m\o\r\y\.\d\i\f\f
X35c35
X< 
X---
X> #define EM_ORIGIN   0x100000	/* origin of extended memory on the AT */
X93c93
X<   int device, count;
X---
X>   int device, count, words, status;
X116,120c116,133
X<   /* Copy the data. */
X<   if (m_ptr->m_type == DISK_READ)
X< 	phys_copy(mem_phys, user_phys, (long) count);
X<   else
X< 	phys_copy(user_phys, mem_phys, (long) count);
X---
X>   /* Copy the data. Origin above EM_ORIGIN means AT extended memory */
X>   if (ram_origin[device] < EM_ORIGIN) {
X> 	/* Ordinary case.  RAM disk is below 640K. */
X> 	if (m_ptr->m_type == DISK_READ)
X> 		phys_copy(mem_phys, user_phys, (long) count);
X> 	else
X> 		phys_copy(user_phys, mem_phys, (long) count);
X>   } else {
X> 	/* AT with RAM disk in extended memory (above 1 MB). */
X> 	if (count & 1) panic("RAM disk got odd byte count\n", m_ptr);
X> 	words = count >> 1;	/* # words is half # bytes */
X> 	if (m_ptr->m_type == DISK_READ) {
X> 		status = em_xfer(mem_phys, user_phys, words);
X> 	} else {
X> 		status = em_xfer(user_phys, mem_phys, words);
X> 	}
X> 	if (status != 0) count = -1;
X>   }	
+ END-OF-FILE memory.diff
chmod 'u=rw,g=r,o=r' \m\e\m\o\r\y\.\d\i\f\f
set `sum \m\e\m\o\r\y\.\d\i\f\f`
sum=$1
case $sum in
12743)	:;;
*)	echo 'Bad sum in '\m\e\m\o\r\y\.\d\i\f\f >&2
esac
echo Extracting \m\m\:\m\a\i\n\.\d\i\f\f
sed 's/^X//' > \m\m\:\m\a\i\n\.\d\i\f\f << '+ END-OF-FILE '\m\m\:\m\a\i\n\.\d\i\f\f
X166,168c166,168
X<   printf("Memory size = %dK     ", mem1);
X<   printf("MINIX = %dK     ", mem2);
X<   printf("RAM disk = %dK     ", mem3);
X---
X>   printf("Memory size = %3dK     ", mem1);
X>   printf("MINIX = %3dK     ", mem2);
X>   printf("RAM disk = %3dK     ", mem3);
+ END-OF-FILE mm:main.diff
chmod 'u=rw,g=r,o=r' \m\m\:\m\a\i\n\.\d\i\f\f
set `sum \m\m\:\m\a\i\n\.\d\i\f\f`
sum=$1
case $sum in
25416)	:;;
*)	echo 'Bad sum in '\m\m\:\m\a\i\n\.\d\i\f\f >&2
esac
exit 0