123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- /* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
- *
- * Linux driver for HYSDN cards
- * specific routines for booting and pof handling
- *
- * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
- * Copyright 1999 by Werner Cornelius (werner@titro.de)
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
- #include <linux/vmalloc.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include "hysdn_defs.h"
- #include "hysdn_pof.h"
- /********************************/
- /* defines for pof read handler */
- /********************************/
- #define POF_READ_FILE_HEAD 0
- #define POF_READ_TAG_HEAD 1
- #define POF_READ_TAG_DATA 2
- /************************************************************/
- /* definition of boot specific data area. This data is only */
- /* needed during boot and so allocated dynamically. */
- /************************************************************/
- struct boot_data {
- unsigned short Cryptor; /* for use with Decrypt function */
- unsigned short Nrecs; /* records remaining in file */
- unsigned char pof_state;/* actual state of read handler */
- unsigned char is_crypted;/* card data is crypted */
- int BufSize; /* actual number of bytes bufferd */
- int last_error; /* last occurred error */
- unsigned short pof_recid;/* actual pof recid */
- unsigned long pof_reclen;/* total length of pof record data */
- unsigned long pof_recoffset;/* actual offset inside pof record */
- union {
- unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
- tPofRecHdr PofRecHdr; /* header for actual record/chunk */
- tPofFileHdr PofFileHdr; /* header from POF file */
- tPofTimeStamp PofTime; /* time information */
- } buf;
- };
- /*****************************************************/
- /* start decryption of successive POF file chuncks. */
- /* */
- /* to be called at start of POF file reading, */
- /* before starting any decryption on any POF record. */
- /*****************************************************/
- static void
- StartDecryption(struct boot_data *boot)
- {
- boot->Cryptor = CRYPT_STARTTERM;
- } /* StartDecryption */
- /***************************************************************/
- /* decrypt complete BootBuf */
- /* NOTE: decryption must be applied to all or none boot tags - */
- /* to HI and LO boot loader and (all) seq tags, because */
- /* global Cryptor is started for whole POF. */
- /***************************************************************/
- static void
- DecryptBuf(struct boot_data *boot, int cnt)
- {
- unsigned char *bufp = boot->buf.BootBuf;
- while (cnt--) {
- boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
- *bufp++ ^= (unsigned char)boot->Cryptor;
- }
- } /* DecryptBuf */
- /********************************************************************************/
- /* pof_handle_data executes the required actions dependent on the active record */
- /* id. If successful 0 is returned, a negative value shows an error. */
- /********************************************************************************/
- static int
- pof_handle_data(hysdn_card *card, int datlen)
- {
- struct boot_data *boot = card->boot; /* pointer to boot specific data */
- long l;
- unsigned char *imgp;
- int img_len;
- /* handle the different record types */
- switch (boot->pof_recid) {
- case TAG_TIMESTMP:
- if (card->debug_flags & LOG_POF_RECORD)
- hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
- break;
- case TAG_CBOOTDTA:
- DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
- /* fall through */
- case TAG_BOOTDTA:
- if (card->debug_flags & LOG_POF_RECORD)
- hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
- (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
- datlen, boot->pof_recoffset);
- if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
- boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */
- return (boot->last_error);
- }
- imgp = boot->buf.BootBuf; /* start of buffer */
- img_len = datlen; /* maximum length to transfer */
- l = POF_BOOT_LOADER_OFF_IN_PAGE -
- (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
- if (l > 0) {
- /* buffer needs to be truncated */
- imgp += l; /* advance pointer */
- img_len -= l; /* adjust len */
- }
- /* at this point no special handling for data wrapping over buffer */
- /* is necessary, because the boot image always will be adjusted to */
- /* match a page boundary inside the buffer. */
- /* The buffer for the boot image on the card is filled in 2 cycles */
- /* first the 1024 hi-words are put in the buffer, then the low 1024 */
- /* word are handled in the same way with different offset. */
- if (img_len > 0) {
- /* data available for copy */
- if ((boot->last_error =
- card->writebootimg(card, imgp,
- (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
- return (boot->last_error);
- }
- break; /* end of case boot image hi/lo */
- case TAG_CABSDATA:
- DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
- /* fall through */
- case TAG_ABSDATA:
- if (card->debug_flags & LOG_POF_RECORD)
- hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
- (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
- datlen, boot->pof_recoffset);
- if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)
- return (boot->last_error); /* error writing data */
- if (boot->pof_recoffset + datlen >= boot->pof_reclen)
- return (card->waitpofready(card)); /* data completely spooled, wait for ready */
- break; /* end of case boot seq data */
- default:
- if (card->debug_flags & LOG_POF_RECORD)
- hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
- datlen, boot->pof_recoffset);
- break; /* simply skip record */
- } /* switch boot->pof_recid */
- return (0);
- } /* pof_handle_data */
- /******************************************************************************/
- /* pof_write_buffer is called when the buffer has been filled with the needed */
- /* number of data bytes. The number delivered is additionally supplied for */
- /* verification. The functions handles the data and returns the needed number */
- /* of bytes for the next action. If the returned value is 0 or less an error */
- /* occurred and booting must be aborted. */
- /******************************************************************************/
- int
- pof_write_buffer(hysdn_card *card, int datlen)
- {
- struct boot_data *boot = card->boot; /* pointer to boot specific data */
- if (!boot)
- return (-EFAULT); /* invalid call */
- if (boot->last_error < 0)
- return (boot->last_error); /* repeated error */
- if (card->debug_flags & LOG_POF_WRITE)
- hysdn_addlog(card, "POF write: got %d bytes ", datlen);
- switch (boot->pof_state) {
- case POF_READ_FILE_HEAD:
- if (card->debug_flags & LOG_POF_WRITE)
- hysdn_addlog(card, "POF write: checking file header");
- if (datlen != sizeof(tPofFileHdr)) {
- boot->last_error = -EPOF_INTERNAL;
- break;
- }
- if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
- boot->last_error = -EPOF_BAD_MAGIC;
- break;
- }
- /* Setup the new state and vars */
- boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
- boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
- boot->last_error = sizeof(tPofRecHdr); /* new length */
- break;
- case POF_READ_TAG_HEAD:
- if (card->debug_flags & LOG_POF_WRITE)
- hysdn_addlog(card, "POF write: checking tag header");
- if (datlen != sizeof(tPofRecHdr)) {
- boot->last_error = -EPOF_INTERNAL;
- break;
- }
- boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */
- boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */
- boot->pof_recoffset = 0; /* no starting offset */
- if (card->debug_flags & LOG_POF_RECORD)
- hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
- boot->pof_recid, boot->pof_reclen);
- boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */
- if (boot->pof_reclen < BOOT_BUF_SIZE)
- boot->last_error = boot->pof_reclen; /* limit size */
- else
- boot->last_error = BOOT_BUF_SIZE; /* maximum */
- if (!boot->last_error) { /* no data inside record */
- boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
- boot->last_error = sizeof(tPofRecHdr); /* new length */
- }
- break;
- case POF_READ_TAG_DATA:
- if (card->debug_flags & LOG_POF_WRITE)
- hysdn_addlog(card, "POF write: getting tag data");
- if (datlen != boot->last_error) {
- boot->last_error = -EPOF_INTERNAL;
- break;
- }
- if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
- return (boot->last_error); /* an error occurred */
- boot->pof_recoffset += datlen;
- if (boot->pof_recoffset >= boot->pof_reclen) {
- boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
- boot->last_error = sizeof(tPofRecHdr); /* new length */
- } else {
- if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
- boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */
- else
- boot->last_error = BOOT_BUF_SIZE; /* maximum */
- }
- break;
- default:
- boot->last_error = -EPOF_INTERNAL; /* unknown state */
- break;
- } /* switch (boot->pof_state) */
- return (boot->last_error);
- } /* pof_write_buffer */
- /*******************************************************************************/
- /* pof_write_open is called when an open for boot on the cardlog device occurs. */
- /* The function returns the needed number of bytes for the next operation. If */
- /* the returned number is less or equal 0 an error specified by this code */
- /* occurred. Additionally the pointer to the buffer data area is set on success */
- /*******************************************************************************/
- int
- pof_write_open(hysdn_card *card, unsigned char **bufp)
- {
- struct boot_data *boot; /* pointer to boot specific data */
- if (card->boot) {
- if (card->debug_flags & LOG_POF_OPEN)
- hysdn_addlog(card, "POF open: already opened for boot");
- return (-ERR_ALREADY_BOOT); /* boot already active */
- }
- /* error no mem available */
- if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
- if (card->debug_flags & LOG_MEM_ERR)
- hysdn_addlog(card, "POF open: unable to allocate mem");
- return (-EFAULT);
- }
- card->boot = boot;
- card->state = CARD_STATE_BOOTING;
- card->stopcard(card); /* first stop the card */
- if (card->testram(card)) {
- if (card->debug_flags & LOG_POF_OPEN)
- hysdn_addlog(card, "POF open: DPRAM test failure");
- boot->last_error = -ERR_BOARD_DPRAM;
- card->state = CARD_STATE_BOOTERR; /* show boot error */
- return (boot->last_error);
- }
- boot->BufSize = 0; /* Buffer is empty */
- boot->pof_state = POF_READ_FILE_HEAD; /* read file header */
- StartDecryption(boot); /* if POF File should be encrypted */
- if (card->debug_flags & LOG_POF_OPEN)
- hysdn_addlog(card, "POF open: success");
- *bufp = boot->buf.BootBuf; /* point to buffer */
- return (sizeof(tPofFileHdr));
- } /* pof_write_open */
- /********************************************************************************/
- /* pof_write_close is called when an close of boot on the cardlog device occurs. */
- /* The return value must be 0 if everything has happened as desired. */
- /********************************************************************************/
- int
- pof_write_close(hysdn_card *card)
- {
- struct boot_data *boot = card->boot; /* pointer to boot specific data */
- if (!boot)
- return (-EFAULT); /* invalid call */
- card->boot = NULL; /* no boot active */
- kfree(boot);
- if (card->state == CARD_STATE_RUN)
- card->set_errlog_state(card, 1); /* activate error log */
- if (card->debug_flags & LOG_POF_OPEN)
- hysdn_addlog(card, "POF close: success");
- return (0);
- } /* pof_write_close */
- /*********************************************************************************/
- /* EvalSysrTokData checks additional records delivered with the Sysready Message */
- /* when POF has been booted. A return value of 0 is used if no error occurred. */
- /*********************************************************************************/
- int
- EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
- {
- u_char *p;
- u_char crc;
- if (card->debug_flags & LOG_POF_RECORD)
- hysdn_addlog(card, "SysReady Token data length %d", len);
- if (len < 2) {
- hysdn_addlog(card, "SysReady Token Data to short");
- return (1);
- }
- for (p = cp, crc = 0; p < (cp + len - 2); p++)
- if ((crc & 0x80))
- crc = (((u_char) (crc << 1)) + 1) + *p;
- else
- crc = ((u_char) (crc << 1)) + *p;
- crc = ~crc;
- if (crc != *(cp + len - 1)) {
- hysdn_addlog(card, "SysReady Token Data invalid CRC");
- return (1);
- }
- len--; /* don't check CRC byte */
- while (len > 0) {
- if (*cp == SYSR_TOK_END)
- return (0); /* End of Token stream */
- if (len < (*(cp + 1) + 2)) {
- hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
- return (1);
- }
- switch (*cp) {
- case SYSR_TOK_B_CHAN: /* 1 */
- if (*(cp + 1) != 1)
- return (1); /* length invalid */
- card->bchans = *(cp + 2);
- break;
- case SYSR_TOK_FAX_CHAN: /* 2 */
- if (*(cp + 1) != 1)
- return (1); /* length invalid */
- card->faxchans = *(cp + 2);
- break;
- case SYSR_TOK_MAC_ADDR: /* 3 */
- if (*(cp + 1) != 6)
- return (1); /* length invalid */
- memcpy(card->mac_addr, cp + 2, 6);
- break;
- default:
- hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
- break;
- }
- len -= (*(cp + 1) + 2); /* adjust len */
- cp += (*(cp + 1) + 2); /* and pointer */
- }
- hysdn_addlog(card, "no end token found");
- return (1);
- } /* EvalSysrTokData */
|