#include "pun.h" #include "xhdi.h" #include #ifdef MFS_XFS #include "minixfs.h" #include "global.h" #define XRWABS RWABS #define DWARN(mes,drive) ALERT("Drive %c: " mes,drive) #else #define DWARN(mes,drive) fprintf(stderr,"Drive %c: " mes "\n",drive) #define ALERT(x) fprintf(stderr,x "\n") #define NEED_SUPER #define RWABS Rwabs #define Kmalloc malloc #define Kfree free #define GETBPB Getbpb #include #include #include #include "hdio.h" #define XRWABS(a,b,c,d,e,f) \ trap_13_wwlwwwl((short)(0x04),(short)(a),(long)(b),(short)(c),(short)(d)\ ,(short)(e),(long)(f) ) #define trap_13_wwlwwwl(n, a, b, c, d, e, f) \ ({ \ register long retvalue __asm__("d0"); \ volatile short _a = (volatile short)(a); \ volatile long _b = (volatile long) (b); \ volatile short _c = (volatile short)(c); \ volatile short _d = (volatile short)(d); \ volatile short _e = (volatile short)(e); \ volatile long _f = (volatile long) (f); \ \ __asm__ volatile \ ("\ movl %5,sp@-; \ movw %4,sp@-; \ movw %3,sp@-; \ movw %2,sp@-; \ movl %1,sp@-; \ movw %0,sp@- " \ : /* outputs */ \ : "r"(_a), "r"(_b), "r"(_c), "r"(_d), "r"(_e), "r"(_f) /* inputs */ \ ); \ \ __asm__ volatile \ ("\ movw %1,sp@-; \ trap #13; \ addw #18,sp " \ : "=r"(retvalue) /* outputs */ \ : "g"(n) /* inputs */ \ : "d0", "d1", "d2", "a0", "a1", "a2" /* clobbered regs */ \ ); \ retvalue; \ }) #endif /* List of error codes for get_hddinf */ char *hdd_err[] = { "OK", "Bad BPB on floppy drive", /* 1 */ "Need drive A-P for PUN_INFO", /* 2 */ "Invalid or no PUN_INFO structure", /* 3 */ "Invalid drive", /* 4 */ "Physical mode disabled for ICD software", /* 5 */ "Physical lrecno error", /* 6 */ "XHInqDev2 failed (old XHDI version?) and bad BPB", /* 7 */ "XHInqDev failed", /* 8 */ "Unrecognised partition id", /* 9 */ "XHInqTarget failed", /* 10 */ "Unsupported physical sector size", /* 11 */ "Invalid partition start (zero BPB?)" /* 12 */ "ICD software too old to fix", /* 13 */ /* These are from set_lrecno */ "Memory allocation failure", /* 14 */ "Unable to access last block" /* 15 */ }; /* * Hard disk info. This is a general purpose routine to handle minixfs' needs * for hard disks. If this function returns non-zero then the partition * cannot be accessed. XHDI and pun_info are used to get partition info. * The structure 'hdinf' is filled in as approproiate. * * If this looks awful then that's because it *is*. */ static char rno_xhdi,try_xhdi; static char try_lrecno,rno_lrecno; static char try_plrecno,rno_plrecno; char is_icd =-1 ; unsigned char *cache_icd; int get_hddinf(drive,hdinf,flag) int drive; struct hdinfo *hdinf; char flag; { long ret; #ifdef NEED_SUPER long tstack; tstack=Super(0l); if(!((*(long *)0x4c2) & (1l<major=drive; /* May get overwritten later */ hdinf->drive=drive; init_icd(); bpb=GETBPB(drive); if( flag ) bpb=0; /* Step 1: if bpb OK and sector size 512 bytes or 1K we may get away * with normal Rwabs. */ if( !bpb || (bpb->recsiz!=512 && bpb->recsiz!=1024) ) { long tsecsiz; char mpid[4]; /* OK can't use normal Rwabs: try XHDI or pun_info */ /* Bypass this rubbish for floppies */ if(drive < 2 ) return 1; /* Try and get info from pun_inf structure */ if( no_xhdi() ) { struct pun_info *pinf; if(drive >= MAXUNITS) return 2; if(!(*(long *)0x516)) return 3; pinf=PUN_PTR; if(!pinf || (PUN_VALID & pinf->pun[drive]) ) return 4; hdinf->scsiz = 1; if(is_icd) #ifdef NO_ICD_PHYS return 5; #else { if(is_icd==2) return 13; hdinf->start = pinf->partition_start[drive+4]; } #endif else hdinf->start = pinf->partition_start[drive]; if(!hdinf->start) return 12; hdinf->size = 0; hdinf->minor = pinf->pun[drive]; if(is_icd) hdinf->major=drive; else hdinf->major = (hdinf->minor & PUN_DEV) +2; hdinf->rwmode = RW_PHYS; /* We want to access at least first few sectors */ if(hdinf->start > 0xfff0) { if(no_plrecno(hdinf->major)) return 6; else hdinf->rwmode |= RW_LRECNO; } return 0; } hdinf->rwmode = RW_XHDI | RW_LRECNO; /* Hmmmm Getbpb failed or bad secsize: see what XHDI can do */ if( XHInqDev2(drive,&hdinf->major,&hdinf->minor,&hdinf->start, 0,&hdinf->size,mpid) ) { if(!bpb && !flag ) return 7; if( XHInqDev(drive,&hdinf->major,&hdinf->minor, &hdinf->start,0) ) return 8; hdinf->size=0; } else if(!bpb && strcmp(mpid,"RAW") && strcmp(mpid,"MIX") && strcmp(mpid,"BGM") && strcmp(mpid,"GEM") ) return 9; /* Get physical sector size */ if( XHInqTarget(hdinf->major,hdinf->minor,&tsecsiz,0,0) ) return 10; if(tsecsiz==512) hdinf->scsiz=1; else { if(tsecsiz==1024) hdinf->scsiz=0; else return 11; } return 0; } if(bpb->recsiz==512) hdinf->scsiz=1; else hdinf->scsiz=0; hdinf->size=0; hdinf->rwmode = RW_NORMAL; return 0; } /* Special kludge for icd software. This software accesses far too many sectors * when physical mode I/O is attempted on sectors bigger than 0xffff with the * cache on. What we do * is to: * 1. Test for ICD software. * 2. Set a pointer to the 'cache flag'. * Return values: * 0 Non ICD software or PUN_INFO problem. * 1 Probably non ICD host adaptor used with ICD software. * 2 ICD software doesn't have a 'cache flag' (probably too old). * 3 Probably kludgable (OK). * Only '2' is fatal. */ int init_icd() { char *icd_magic; if(is_icd!=-1) return is_icd; if(!*((long *)0x516)) return is_icd=0; icd_magic=((char *)PUN_PTR)-6; if(strncmp(icd_magic,"ICDB",4)) return is_icd=0; else { char *icdh_magic; if( icd_magic[5] < 0x50 ) return is_icd=2; icdh_magic=*((char ** )(icd_magic-4)); if(strncmp(icdh_magic,"ICDH",4)) return is_icd=1; if( icdh_magic[4] < 0x50 ) return is_icd=2; cache_icd = (unsigned char *)(icdh_magic+6); return is_icd=3; } } /* This function is called after get_hddinf and is used to finalise the * accessibility of a partition. The 'size' parameter is the size of the * partition in K; this info will typically come from the super block of * a filesystem. * Return values: * 0 OK * 1 Malloc failure. * 2 Can't access last block. * 3 Physical lrecno error. */ int set_lrecno(hdinf,size) struct hdinfo *hdinf; long size; { long tsize; tsize=size; if(hdinf->scsiz) tsize <<=1; if( ( (hdinf->rwmode & RW_MODE) == RW_XHDI) && hdinf->size && (hdinf->size < tsize) ) { DWARN("Filesystem size bigger than partition size!", hdinf->drive); } else hdinf->size = tsize; hdinf->rwmode |= RW_CHECK; if(hdinf->rwmode & RW_LRECNO) return 0; switch(hdinf->rwmode & RW_MODE) { case RW_NORMAL: if(tsize < 0xffff) { char *tmp_buf; tmp_buf=Kmalloc(1024); if(!tmp_buf) return 14; /* Try to read last block */ if(!block_rwabs(2,tmp_buf,1,size-1,hdinf)) { Kfree(tmp_buf); return 0; } Kfree(tmp_buf); #ifndef BYPASS_LOGICAL return 15; #endif } if( tsize < 0xffff || no_lrecno(hdinf->major) ) { /* Bad lrecno or access error, try physical mode access */ int drive,err; drive = hdinf->major; if( (err=get_hddinf(drive,hdinf,1)) ) return err; else return (set_lrecno(hdinf,size)); } else hdinf->rwmode |= RW_LRECNO; return 0; case RW_PHYS: if(tsize+hdinf->start >= 0xfffe) { if(no_plrecno(hdinf->major)) return 1; hdinf->size = tsize; hdinf->rwmode |= RW_LRECNO; } return 0; } return 1; /* This can't happen */ } /* Test for 'lrecno' parameter on drive 'drive': mode' is RWABS mode to use * (2=logical,10=physical). * Return values: * 0 OK * 1 Read error. * 2 No lrecno recognised. * 3 Error reading large sector number (possibly OK if partition too small). * 4 Wraparound bug present. * 5 Allocation error. */ int test_lrecno(drive,mode) int drive; int mode; { char *block_buf1,*block_buf2; int size; _BPB *bpb; bpb=Getbpb(drive); if( (mode & 8) || !bpb ) size=1024; else size=bpb->recsiz; block_buf1=Kmalloc(size<<1); block_buf2=block_buf1+size; bzero(block_buf1,size<<1); if(!block_buf1) return 5; /* read in boot sector */ if(RWABS(mode,block_buf1,1,0,drive)) { Kfree(block_buf1); return 1; } /* read it in with lrecno */ if( XRWABS(mode,block_buf2,1,-1,drive,0l) ) { Kfree(block_buf1); return 2; } /* Compare the two */ if(bcmp(block_buf1,block_buf2,size)) { Kfree(block_buf1); return 2; } /* read in next sector with lrecno */ if(XRWABS(mode,block_buf2,1,-1,drive,1l)) { Kfree(block_buf1); return 1; } /* compare the two */ if(!bcmp(block_buf1,block_buf2,size)) { Kfree(block_buf1); return 2; } /* Check for lrecno bug, this causes the upper word of a long sector * number to be ignored. Read in sector 0 and 0x10000, if the bug is * present then these will be identical. */ bzero(block_buf2,size); if(XRWABS(mode,block_buf2,1,-1,drive,0x10000l)) { Kfree(block_buf1); return 3; } else if(!bcmp(block_buf1,block_buf2,size)) { Kfree(block_buf1); return 4; } Kfree(block_buf1); return 0; } int no_lrecno(drv) int drv; { if( !try_lrecno ) { rno_lrecno = test_lrecno(drv,2) ; try_lrecno = 1; } return rno_lrecno; } int no_plrecno(drv) int drv; { #ifdef NO_ICD_PHYS if(is_icd) return 1; #endif if( !try_plrecno ) { unsigned char cache_tmp=0; try_plrecno = 1; if(cache_icd) { cache_tmp=*cache_icd; *cache_icd=0; } rno_plrecno = test_lrecno(drv,10) ; if(cache_icd) *cache_icd=cache_tmp; } return rno_plrecno; } int no_xhdi() { if( !try_xhdi ) { if( !XHGetVersion() ) rno_xhdi=1; try_xhdi=1; } return rno_xhdi; } /* * This is (finally!) the I/O function hdinf uses. It reads/writes in 1K chunks * and calls the relevant functions according to the hdinf structure. */ long block_rwabs(rw,buf,num,recno,hdinf) int rw; void *buf; unsigned num; long recno; struct hdinfo *hdinf; { unsigned char cache_tmp=0; long ret; if( hdinf->scsiz ) { recno <<=1; num <<=1; } if( (hdinf->rwmode & RW_CHECK) && (recno+num > hdinf->size) ) { DWARN("Attempted access outside partition",hdinf->drive); return -1; } switch(hdinf->rwmode & (RW_MODE|RW_LRECNO)) { case RW_NORMAL: return RWABS(rw,buf,num,(unsigned)recno,hdinf->major); case RW_NORMAL | RW_LRECNO: return XRWABS(rw,buf,num,-1,hdinf->major,recno); case RW_PHYS: if(cache_icd) { cache_tmp=*cache_icd; *cache_icd=0; } ret = RWABS(rw | 8,buf,num,(unsigned)(recno+hdinf->start), hdinf->major); if(cache_icd) *cache_icd=cache_tmp; return ret; case RW_PHYS | RW_LRECNO: if(cache_icd) { cache_tmp=*cache_icd; *cache_icd=0; } ret = XRWABS(rw | 8,buf,num,-1,hdinf->major, recno+hdinf->start); if(cache_icd) *cache_icd=cache_tmp; return ret; case RW_XHDI | RW_LRECNO: return XHReadWrite(hdinf->major,hdinf->minor,rw, recno+hdinf->start,num,buf); } return 1; /* This can't happen ! */ } #ifndef SUPER_CALL /* Determine partition size from drive info. Check Rwabs errors ... it should * give an error for an attempt to read past the end of a partition. From then * on use a log search to find partition size. * If this seems awkward you are right ... certain disk software doesn't enter * the correct size in the boot sector (to correct a TOS bug). */ char *size_err[] = { "OK", "Getbpb failed", "Allocation error", "Driver software incompatible" }; int get_size(drive,size) int drive; long *size; { char *buf; _BPB *bpb; unsigned secnum,bitnum; if(! (bpb=GETBPB(drive)) ) return 1; /* Bad bpb */ if(! (buf=Kmalloc(bpb->recsiz)) ) return 2; /* alloc error */ if(!RWABS(2,buf,1,0xfffe,drive)) { Kfree(buf); return 3; /* Software doesn't return errors */ } bitnum=0x8000; secnum=0; do { secnum |= bitnum; if( RWABS(2,buf,1,secnum,drive) ) secnum &=~bitnum; bitnum >>=1 ; } while(bitnum); secnum++; *size=(((long)secnum) * bpb->recsiz)>>10; Kfree(buf); return 0; } #endif