📄 rioboot.c
字号:
/*** -----------------------------------------------------------------------------**** Perle Specialix driver for Linux** Ported from existing RIO Driver for SCO sources. * * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK. * * 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; either version 2 of the License, or * (at your option) any later version. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA.**** Module : rioboot.c** SID : 1.3** Last Modified : 11/6/98 10:33:36** Retrieved : 11/6/98 10:33:48**** ident @(#)rioboot.c 1.3**** -----------------------------------------------------------------------------*/#ifdef SCCS_LABELSstatic char *_rioboot_c_sccs_ = "@(#)rioboot.c 1.3";#endif#include <linux/module.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/system.h>#include <asm/string.h>#include <asm/semaphore.h>#include <linux/termios.h>#include <linux/serial.h>#include <linux/generic_serial.h>#include "linux_compat.h"#include "rio_linux.h"#include "typdef.h"#include "pkt.h"#include "daemon.h"#include "rio.h"#include "riospace.h"#include "top.h"#include "cmdpkt.h"#include "map.h"#include "riotypes.h"#include "rup.h"#include "port.h"#include "riodrvr.h"#include "rioinfo.h"#include "func.h"#include "errors.h"#include "pci.h"#include "parmmap.h"#include "unixrup.h"#include "board.h"#include "host.h"#include "error.h"#include "phb.h"#include "link.h"#include "cmdblk.h"#include "route.h"static int RIOBootComplete( struct rio_info *p, struct Host *HostP, uint Rup, struct PktCmd *PktCmdP );static ucharRIOAtVec2Ctrl[] ={ /* 0 */ INTERRUPT_DISABLE, /* 1 */ INTERRUPT_DISABLE, /* 2 */ INTERRUPT_DISABLE, /* 3 */ INTERRUPT_DISABLE, /* 4 */ INTERRUPT_DISABLE, /* 5 */ INTERRUPT_DISABLE, /* 6 */ INTERRUPT_DISABLE, /* 7 */ INTERRUPT_DISABLE, /* 8 */ INTERRUPT_DISABLE, /* 9 */ IRQ_9|INTERRUPT_ENABLE, /* 10 */ INTERRUPT_DISABLE, /* 11 */ IRQ_11|INTERRUPT_ENABLE, /* 12 */ IRQ_12|INTERRUPT_ENABLE, /* 13 */ INTERRUPT_DISABLE, /* 14 */ INTERRUPT_DISABLE, /* 15 */ IRQ_15|INTERRUPT_ENABLE};/*** Load in the RTA boot code.*/intRIOBootCodeRTA(p, rbp)struct rio_info * p;struct DownLoad * rbp; { int offset; func_enter (); /* Linux doesn't allow you to disable interrupts during a "copyin". (Crash when a pagefault occurs). */ /* disable(oldspl); */ rio_dprintk (RIO_DEBUG_BOOT, "Data at user address 0x%x\n",(int)rbp->DataP); /* ** Check that we have set asside enough memory for this */ if ( rbp->Count > SIXTY_FOUR_K ) { rio_dprintk (RIO_DEBUG_BOOT, "RTA Boot Code Too Large!\n"); p->RIOError.Error = HOST_FILE_TOO_LARGE; /* restore(oldspl); */ func_exit (); return -ENOMEM; } if ( p->RIOBooting ) { rio_dprintk (RIO_DEBUG_BOOT, "RTA Boot Code : BUSY BUSY BUSY!\n"); p->RIOError.Error = BOOT_IN_PROGRESS; /* restore(oldspl); */ func_exit (); return -EBUSY; } /* ** The data we load in must end on a (RTA_BOOT_DATA_SIZE) byte boundary, ** so calculate how far we have to move the data up the buffer ** to achieve this. */ offset = (RTA_BOOT_DATA_SIZE - (rbp->Count % RTA_BOOT_DATA_SIZE)) % RTA_BOOT_DATA_SIZE; /* ** Be clean, and clear the 'unused' portion of the boot buffer, ** because it will (eventually) be part of the Rta run time environment ** and so should be zeroed. */ bzero( (caddr_t)p->RIOBootPackets, offset ); /* ** Copy the data from user space. */ if ( copyin((int)rbp->DataP,((caddr_t)(p->RIOBootPackets))+offset, rbp->Count) ==COPYFAIL ) { rio_dprintk (RIO_DEBUG_BOOT, "Bad data copy from user space\n"); p->RIOError.Error = COPYIN_FAILED; /* restore(oldspl); */ func_exit (); return -EFAULT; } /* ** Make sure that our copy of the size includes that offset we discussed ** earlier. */ p->RIONumBootPkts = (rbp->Count+offset)/RTA_BOOT_DATA_SIZE; p->RIOBootCount = rbp->Count; /* restore(oldspl); */ func_exit(); return 0;}void rio_start_card_running (struct Host * HostP){ func_enter (); switch ( HostP->Type ) { case RIO_AT: rio_dprintk (RIO_DEBUG_BOOT, "Start ISA card running\n"); WBYTE(HostP->Control, BOOT_FROM_RAM | EXTERNAL_BUS_ON | HostP->Mode | RIOAtVec2Ctrl[HostP->Ivec & 0xF] ); break; #ifdef FUTURE_RELEASE case RIO_MCA: /* ** MCA handles IRQ vectors differently, so we don't write ** them to this register. */ rio_dprintk (RIO_DEBUG_BOOT, "Start MCA card running\n"); WBYTE(HostP->Control, McaTpBootFromRam | McaTpBusEnable | HostP->Mode); break; case RIO_EISA: /* ** EISA is totally different and expects OUTBZs to turn it on. */ rio_dprintk (RIO_DEBUG_BOOT, "Start EISA card running\n"); OUTBZ( HostP->Slot, EISA_CONTROL_PORT, HostP->Mode | RIOEisaVec2Ctrl[HostP->Ivec] | EISA_TP_RUN | EISA_TP_BUS_ENABLE | EISA_TP_BOOT_FROM_RAM ); break;#endif case RIO_PCI: /* ** PCI is much the same as MCA. Everything is once again memory ** mapped, so we are writing to memory registers instead of io ** ports. */ rio_dprintk (RIO_DEBUG_BOOT, "Start PCI card running\n"); WBYTE(HostP->Control, PCITpBootFromRam | PCITpBusEnable | HostP->Mode); break; default: rio_dprintk (RIO_DEBUG_BOOT, "Unknown host type %d\n", HostP->Type); break; }/* printk (KERN_INFO "Done with starting the card\n"); func_exit ();*/ return;}/*** Load in the host boot code - load it directly onto all halted hosts** of the correct type.**** Put your rubber pants on before messing with this code - even the magic** numbers have trouble understanding what they are doing here.*/intRIOBootCodeHOST(p, rbp)struct rio_info * p;register struct DownLoad *rbp;{ register struct Host *HostP; register caddr_t Cad; register PARM_MAP *ParmMapP; register int RupN; int PortN; uint host; caddr_t StartP; BYTE *DestP; int wait_count; ushort OldParmMap; ushort offset; /* It is very important that this is a ushort */ /* uint byte; */ caddr_t DownCode = NULL; unsigned long flags; HostP = NULL; /* Assure the compiler we've initialized it */ for ( host=0; host<p->RIONumHosts; host++ ) { rio_dprintk (RIO_DEBUG_BOOT, "Attempt to boot host %d\n",host); HostP = &p->RIOHosts[host]; rio_dprintk (RIO_DEBUG_BOOT, "Host Type = 0x%x, Mode = 0x%x, IVec = 0x%x\n", HostP->Type, HostP->Mode, HostP->Ivec); if ( (HostP->Flags & RUN_STATE) != RC_WAITING ) { rio_dprintk (RIO_DEBUG_BOOT, "%s %d already running\n","Host",host); continue; } /* ** Grab a 32 bit pointer to the card. */ Cad = HostP->Caddr; /* ** We are going to (try) and load in rbp->Count bytes. ** The last byte will reside at p->RIOConf.HostLoadBase-1; ** Therefore, we need to start copying at address ** (caddr+p->RIOConf.HostLoadBase-rbp->Count) */ StartP = (caddr_t)&Cad[p->RIOConf.HostLoadBase-rbp->Count]; rio_dprintk (RIO_DEBUG_BOOT, "kernel virtual address for host is 0x%x\n", (int)Cad ); rio_dprintk (RIO_DEBUG_BOOT, "kernel virtual address for download is 0x%x\n", (int)StartP); rio_dprintk (RIO_DEBUG_BOOT, "host loadbase is 0x%x\n",p->RIOConf.HostLoadBase); rio_dprintk (RIO_DEBUG_BOOT, "size of download is 0x%x\n", rbp->Count); if ( p->RIOConf.HostLoadBase < rbp->Count ) { rio_dprintk (RIO_DEBUG_BOOT, "Bin too large\n"); p->RIOError.Error = HOST_FILE_TOO_LARGE; func_exit (); return -EFBIG; } /* ** Ensure that the host really is stopped. ** Disable it's external bus & twang its reset line. */ RIOHostReset( HostP->Type, (struct DpRam *)HostP->CardP, HostP->Slot ); /* ** Copy the data directly from user space to the SRAM. ** This ain't going to be none too clever if the download ** code is bigger than this segment. */ rio_dprintk (RIO_DEBUG_BOOT, "Copy in code\n"); /* ** PCI hostcard can't cope with 32 bit accesses and so need to copy ** data to a local buffer, and then dripfeed the card. */ if ( HostP->Type == RIO_PCI ) { /* int offset; */ DownCode = sysbrk(rbp->Count); if ( !DownCode ) { rio_dprintk (RIO_DEBUG_BOOT, "No system memory available\n"); p->RIOError.Error = NOT_ENOUGH_CORE_FOR_PCI_COPY; func_exit (); return -ENOMEM; } bzero(DownCode, rbp->Count); if ( copyin((int)rbp->DataP,DownCode,rbp->Count)==COPYFAIL ) { rio_dprintk (RIO_DEBUG_BOOT, "Bad copyin of host data\n"); sysfree( DownCode, rbp->Count ); p->RIOError.Error = COPYIN_FAILED; func_exit (); return -EFAULT; } HostP->Copy( DownCode, StartP, rbp->Count ); sysfree( DownCode, rbp->Count ); } else if ( copyin((int)rbp->DataP,StartP,rbp->Count)==COPYFAIL ) { rio_dprintk (RIO_DEBUG_BOOT, "Bad copyin of host data\n"); p->RIOError.Error = COPYIN_FAILED; func_exit (); return -EFAULT; } rio_dprintk (RIO_DEBUG_BOOT, "Copy completed\n"); /* ** S T O P ! ** ** Upto this point the code has been fairly rational, and possibly ** even straight forward. What follows is a pile of crud that will ** magically turn into six bytes of transputer assembler. Normally ** you would expect an array or something, but, being me, I have ** chosen [been told] to use a technique whereby the startup code ** will be correct if we change the loadbase for the code. Which ** brings us onto another issue - the loadbase is the *end* of the ** code, not the start. ** ** If I were you I wouldn't start from here. */ /* ** We now need to insert a short boot section into ** the memory at the end of Sram2. This is normally (de)composed ** of the last eight bytes of the download code. The ** download has been assembled/compiled to expect to be ** loaded from 0x7FFF downwards. We have loaded it ** at some other address. The startup code goes into the small ** ram window at Sram2, in the last 8 bytes, which are really ** at addresses 0x7FF8-0x7FFF. ** ** If the loadbase is, say, 0x7C00, then we need to branch to ** address 0x7BFE to run the host.bin startup code. We assemble ** this jump manually. ** ** The two byte sequence 60 08 is loaded into memory at address ** 0x7FFE,F. This is a local branch to location 0x7FF8 (60 is nfix 0, ** which adds '0' to the .O register, complements .O, and then shifts ** it left by 4 bit positions, 08 is a jump .O+8 instruction. This will ** add 8 to .O (which was 0xFFF0), and will branch RELATIVE to the new ** location. Now, the branch starts from the value of .PC (or .IP or ** whatever the bloody register is called on this chip), and the .PC ** will be pointing to the location AFTER the branch, in this case ** .PC == 0x8000, so the branch will be to 0x8000+0xFFF8 = 0x7FF8. ** ** A long branch is coded at 0x7FF8. This consists of loading a four ** byte offset into .O using nfix (as above) and pfix operators. The ** pfix operates in exactly the same way as the nfix operator, but ** without the complement operation. The offset, of course, must be ** relative to the address of the byte AFTER the branch instruction, ** which will be (urm) 0x7FFC, so, our final destination of the branch ** (loadbase-2), has to be reached from here. Imagine that the loadbase ** is 0x7C00 (which it is), then we will need to branch to 0x7BFE (which ** is the first byte of the initial two byte short local branch of the ** download code). ** ** To code a jump from 0x7FFC (which is where the branch will start ** from) to 0x7BFE, we will need to branch 0xFC02 bytes (0x7FFC+0xFC02)= ** 0x7BFE. ** This will be coded as four bytes: ** 60 2C 20 02 ** being nfix .O+0 ** pfix .O+C ** pfix .O+0 ** jump .O+2 ** ** The nfix operator is used, so that the startup code will be ** compatible with the whole Tp family. (lies, damn lies, it'll never ** work in a month of Sundays). ** ** The nfix nyble is the 1s complement of the nyble value you ** want to load - in this case we wanted 'F' so we nfix loaded '0'. */ /* ** Dest points to the top 8 bytes of Sram2. The Tp jumps ** to 0x7FFE at reset time, and starts executing. This is ** a short branch to 0x7FF8, where a long branch is coded. */ DestP = (BYTE *)&Cad[0x7FF8]; /* <<<---- READ THE ABOVE COMMENTS */#define NFIX(N) (0x60 | (N)) /* .O = (~(.O + N))<<4 */#define PFIX(N) (0x20 | (N)) /* .O = (.O + N)<<4 */#define JUMP(N) (0x00 | (N)) /* .PC = .PC + .O */ /* ** 0x7FFC is the address of the location following the last byte of ** the four byte jump instruction. ** READ THE ABOVE COMMENTS ** ** offset is (TO-FROM) % MEMSIZE, but with compound buggering about. ** Memsize is 64K for this range of Tp, so offset is a short (unsigned, ** cos I don't understand 2's complement). */ offset = (p->RIOConf.HostLoadBase-2)-0x7FFC; WBYTE( DestP[0] , NFIX(((ushort)(~offset) >> (ushort)12) & 0xF) ); WBYTE( DestP[1] , PFIX(( offset >> 8) & 0xF) ); WBYTE( DestP[2] , PFIX(( offset >> 4) & 0xF) ); WBYTE( DestP[3] , JUMP( offset & 0xF) ); WBYTE( DestP[6] , NFIX(0) ); WBYTE( DestP[7] , JUMP(8) ); rio_dprintk (RIO_DEBUG_BOOT, "host loadbase is 0x%x\n",p->RIOConf.HostLoadBase); rio_dprintk (RIO_DEBUG_BOOT, "startup offset is 0x%x\n",offset); /* ** Flag what is going on */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -