linux/drivers/isdn/hysdn/hysdn_boot.c
<<
>>
Prefs
   1/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
   2 *
   3 * Linux driver for HYSDN cards
   4 * specific routines for booting and pof handling
   5 *
   6 * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
   7 * Copyright 1999 by Werner Cornelius (werner@titro.de)
   8 *
   9 * This software may be used and distributed according to the terms
  10 * of the GNU General Public License, incorporated herein by reference.
  11 *
  12 */
  13
  14#include <linux/vmalloc.h>
  15#include <linux/slab.h>
  16#include <linux/uaccess.h>
  17
  18#include "hysdn_defs.h"
  19#include "hysdn_pof.h"
  20
  21/********************************/
  22/* defines for pof read handler */
  23/********************************/
  24#define POF_READ_FILE_HEAD  0
  25#define POF_READ_TAG_HEAD   1
  26#define POF_READ_TAG_DATA   2
  27
  28/************************************************************/
  29/* definition of boot specific data area. This data is only */
  30/* needed during boot and so allocated dynamically.         */
  31/************************************************************/
  32struct boot_data {
  33        unsigned short Cryptor; /* for use with Decrypt function */
  34        unsigned short Nrecs;   /* records remaining in file */
  35        unsigned char pof_state;/* actual state of read handler */
  36        unsigned char is_crypted;/* card data is crypted */
  37        int BufSize;            /* actual number of bytes bufferd */
  38        int last_error;         /* last occurred error */
  39        unsigned short pof_recid;/* actual pof recid */
  40        unsigned long pof_reclen;/* total length of pof record data */
  41        unsigned long pof_recoffset;/* actual offset inside pof record */
  42        union {
  43                unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
  44                tPofRecHdr PofRecHdr;   /* header for actual record/chunk */
  45                tPofFileHdr PofFileHdr;         /* header from POF file */
  46                tPofTimeStamp PofTime;  /* time information */
  47        } buf;
  48};
  49
  50/*****************************************************/
  51/*  start decryption of successive POF file chuncks.  */
  52/*                                                   */
  53/*  to be called at start of POF file reading,       */
  54/*  before starting any decryption on any POF record. */
  55/*****************************************************/
  56static void
  57StartDecryption(struct boot_data *boot)
  58{
  59        boot->Cryptor = CRYPT_STARTTERM;
  60}                               /* StartDecryption */
  61
  62
  63/***************************************************************/
  64/* decrypt complete BootBuf                                    */
  65/* NOTE: decryption must be applied to all or none boot tags - */
  66/*       to HI and LO boot loader and (all) seq tags, because  */
  67/*       global Cryptor is started for whole POF.              */
  68/***************************************************************/
  69static void
  70DecryptBuf(struct boot_data *boot, int cnt)
  71{
  72        unsigned char *bufp = boot->buf.BootBuf;
  73
  74        while (cnt--) {
  75                boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
  76                *bufp++ ^= (unsigned char)boot->Cryptor;
  77        }
  78}                               /* DecryptBuf */
  79
  80/********************************************************************************/
  81/* pof_handle_data executes the required actions dependent on the active record */
  82/* id. If successful 0 is returned, a negative value shows an error.           */
  83/********************************************************************************/
  84static int
  85pof_handle_data(hysdn_card *card, int datlen)
  86{
  87        struct boot_data *boot = card->boot;    /* pointer to boot specific data */
  88        long l;
  89        unsigned char *imgp;
  90        int img_len;
  91
  92        /* handle the different record types */
  93        switch (boot->pof_recid) {
  94
  95        case TAG_TIMESTMP:
  96                if (card->debug_flags & LOG_POF_RECORD)
  97                        hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
  98                break;
  99
 100        case TAG_CBOOTDTA:
 101                DecryptBuf(boot, datlen);       /* we need to encrypt the buffer */
 102        case TAG_BOOTDTA:
 103                if (card->debug_flags & LOG_POF_RECORD)
 104                        hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
 105                                     (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
 106                                     datlen, boot->pof_recoffset);
 107
 108                if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
 109                        boot->last_error = EPOF_BAD_IMG_SIZE;   /* invalid length */
 110                        return (boot->last_error);
 111                }
 112                imgp = boot->buf.BootBuf;       /* start of buffer */
 113                img_len = datlen;       /* maximum length to transfer */
 114
 115                l = POF_BOOT_LOADER_OFF_IN_PAGE -
 116                        (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
 117                if (l > 0) {
 118                        /* buffer needs to be truncated */
 119                        imgp += l;      /* advance pointer */
 120                        img_len -= l;   /* adjust len */
 121                }
 122                /* at this point no special handling for data wrapping over buffer */
 123                /* is necessary, because the boot image always will be adjusted to */
 124                /* match a page boundary inside the buffer.                        */
 125                /* The buffer for the boot image on the card is filled in 2 cycles */
 126                /* first the 1024 hi-words are put in the buffer, then the low 1024 */
 127                /* word are handled in the same way with different offset.         */
 128
 129                if (img_len > 0) {
 130                        /* data available for copy */
 131                        if ((boot->last_error =
 132                             card->writebootimg(card, imgp,
 133                                                (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
 134                                return (boot->last_error);
 135                }
 136                break;  /* end of case boot image hi/lo */
 137
 138        case TAG_CABSDATA:
 139                DecryptBuf(boot, datlen);       /* we need to encrypt the buffer */
 140        case TAG_ABSDATA:
 141                if (card->debug_flags & LOG_POF_RECORD)
 142                        hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
 143                                     (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
 144                                     datlen, boot->pof_recoffset);
 145
 146                if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)
 147                        return (boot->last_error);      /* error writing data */
 148
 149                if (boot->pof_recoffset + datlen >= boot->pof_reclen)
 150                        return (card->waitpofready(card));      /* data completely spooled, wait for ready */
 151
 152                break;  /* end of case boot seq data */
 153
 154        default:
 155                if (card->debug_flags & LOG_POF_RECORD)
 156                        hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
 157                                     datlen, boot->pof_recoffset);
 158
 159                break;  /* simply skip record */
 160        }                       /* switch boot->pof_recid */
 161
 162        return (0);
 163}                               /* pof_handle_data */
 164
 165
 166/******************************************************************************/
 167/* pof_write_buffer is called when the buffer has been filled with the needed */
 168/* number of data bytes. The number delivered is additionally supplied for    */
 169/* verification. The functions handles the data and returns the needed number */
 170/* of bytes for the next action. If the returned value is 0 or less an error  */
 171/* occurred and booting must be aborted.                                       */
 172/******************************************************************************/
 173int
 174pof_write_buffer(hysdn_card *card, int datlen)
 175{
 176        struct boot_data *boot = card->boot;    /* pointer to boot specific data */
 177
 178        if (!boot)
 179                return (-EFAULT);       /* invalid call */
 180        if (boot->last_error < 0)
 181                return (boot->last_error);      /* repeated error */
 182
 183        if (card->debug_flags & LOG_POF_WRITE)
 184                hysdn_addlog(card, "POF write: got %d bytes ", datlen);
 185
 186        switch (boot->pof_state) {
 187        case POF_READ_FILE_HEAD:
 188                if (card->debug_flags & LOG_POF_WRITE)
 189                        hysdn_addlog(card, "POF write: checking file header");
 190
 191                if (datlen != sizeof(tPofFileHdr)) {
 192                        boot->last_error = -EPOF_INTERNAL;
 193                        break;
 194                }
 195                if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
 196                        boot->last_error = -EPOF_BAD_MAGIC;
 197                        break;
 198                }
 199                /* Setup the new state and vars */
 200                boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
 201                boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
 202                boot->last_error = sizeof(tPofRecHdr);  /* new length */
 203                break;
 204
 205        case POF_READ_TAG_HEAD:
 206                if (card->debug_flags & LOG_POF_WRITE)
 207                        hysdn_addlog(card, "POF write: checking tag header");
 208
 209                if (datlen != sizeof(tPofRecHdr)) {
 210                        boot->last_error = -EPOF_INTERNAL;
 211                        break;
 212                }
 213                boot->pof_recid = boot->buf.PofRecHdr.PofRecId;         /* actual pof recid */
 214                boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen;   /* total length */
 215                boot->pof_recoffset = 0;        /* no starting offset */
 216
 217                if (card->debug_flags & LOG_POF_RECORD)
 218                        hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
 219                                     boot->pof_recid, boot->pof_reclen);
 220
 221                boot->pof_state = POF_READ_TAG_DATA;    /* now start with tag data */
 222                if (boot->pof_reclen < BOOT_BUF_SIZE)
 223                        boot->last_error = boot->pof_reclen;    /* limit size */
 224                else
 225                        boot->last_error = BOOT_BUF_SIZE;       /* maximum */
 226
 227                if (!boot->last_error) {        /* no data inside record */
 228                        boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
 229                        boot->last_error = sizeof(tPofRecHdr);  /* new length */
 230                }
 231                break;
 232
 233        case POF_READ_TAG_DATA:
 234                if (card->debug_flags & LOG_POF_WRITE)
 235                        hysdn_addlog(card, "POF write: getting tag data");
 236
 237                if (datlen != boot->last_error) {
 238                        boot->last_error = -EPOF_INTERNAL;
 239                        break;
 240                }
 241                if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
 242                        return (boot->last_error);      /* an error occurred */
 243                boot->pof_recoffset += datlen;
 244                if (boot->pof_recoffset >= boot->pof_reclen) {
 245                        boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
 246                        boot->last_error = sizeof(tPofRecHdr);  /* new length */
 247                } else {
 248                        if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
 249                                boot->last_error = boot->pof_reclen - boot->pof_recoffset;      /* limit size */
 250                        else
 251                                boot->last_error = BOOT_BUF_SIZE;       /* maximum */
 252                }
 253                break;
 254
 255        default:
 256                boot->last_error = -EPOF_INTERNAL;      /* unknown state */
 257                break;
 258        }                       /* switch (boot->pof_state) */
 259
 260        return (boot->last_error);
 261}                               /* pof_write_buffer */
 262
 263
 264/*******************************************************************************/
 265/* pof_write_open is called when an open for boot on the cardlog device occurs. */
 266/* The function returns the needed number of bytes for the next operation. If  */
 267/* the returned number is less or equal 0 an error specified by this code      */
 268/* occurred. Additionally the pointer to the buffer data area is set on success */
 269/*******************************************************************************/
 270int
 271pof_write_open(hysdn_card *card, unsigned char **bufp)
 272{
 273        struct boot_data *boot; /* pointer to boot specific data */
 274
 275        if (card->boot) {
 276                if (card->debug_flags & LOG_POF_OPEN)
 277                        hysdn_addlog(card, "POF open: already opened for boot");
 278                return (-ERR_ALREADY_BOOT);     /* boot already active */
 279        }
 280        /* error no mem available */
 281        if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
 282                if (card->debug_flags & LOG_MEM_ERR)
 283                        hysdn_addlog(card, "POF open: unable to allocate mem");
 284                return (-EFAULT);
 285        }
 286        card->boot = boot;
 287        card->state = CARD_STATE_BOOTING;
 288
 289        card->stopcard(card);   /* first stop the card */
 290        if (card->testram(card)) {
 291                if (card->debug_flags & LOG_POF_OPEN)
 292                        hysdn_addlog(card, "POF open: DPRAM test failure");
 293                boot->last_error = -ERR_BOARD_DPRAM;
 294                card->state = CARD_STATE_BOOTERR;       /* show boot error */
 295                return (boot->last_error);
 296        }
 297        boot->BufSize = 0;      /* Buffer is empty */
 298        boot->pof_state = POF_READ_FILE_HEAD;   /* read file header */
 299        StartDecryption(boot);  /* if POF File should be encrypted */
 300
 301        if (card->debug_flags & LOG_POF_OPEN)
 302                hysdn_addlog(card, "POF open: success");
 303
 304        *bufp = boot->buf.BootBuf;      /* point to buffer */
 305        return (sizeof(tPofFileHdr));
 306}                               /* pof_write_open */
 307
 308/********************************************************************************/
 309/* pof_write_close is called when an close of boot on the cardlog device occurs. */
 310/* The return value must be 0 if everything has happened as desired.            */
 311/********************************************************************************/
 312int
 313pof_write_close(hysdn_card *card)
 314{
 315        struct boot_data *boot = card->boot;    /* pointer to boot specific data */
 316
 317        if (!boot)
 318                return (-EFAULT);       /* invalid call */
 319
 320        card->boot = NULL;      /* no boot active */
 321        kfree(boot);
 322
 323        if (card->state == CARD_STATE_RUN)
 324                card->set_errlog_state(card, 1);        /* activate error log */
 325
 326        if (card->debug_flags & LOG_POF_OPEN)
 327                hysdn_addlog(card, "POF close: success");
 328
 329        return (0);
 330}                               /* pof_write_close */
 331
 332/*********************************************************************************/
 333/* EvalSysrTokData checks additional records delivered with the Sysready Message */
 334/* when POF has been booted. A return value of 0 is used if no error occurred.    */
 335/*********************************************************************************/
 336int
 337EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
 338{
 339        u_char *p;
 340        u_char crc;
 341
 342        if (card->debug_flags & LOG_POF_RECORD)
 343                hysdn_addlog(card, "SysReady Token data length %d", len);
 344
 345        if (len < 2) {
 346                hysdn_addlog(card, "SysReady Token Data to short");
 347                return (1);
 348        }
 349        for (p = cp, crc = 0; p < (cp + len - 2); p++)
 350                if ((crc & 0x80))
 351                        crc = (((u_char) (crc << 1)) + 1) + *p;
 352                else
 353                        crc = ((u_char) (crc << 1)) + *p;
 354        crc = ~crc;
 355        if (crc != *(cp + len - 1)) {
 356                hysdn_addlog(card, "SysReady Token Data invalid CRC");
 357                return (1);
 358        }
 359        len--;                  /* don't check CRC byte */
 360        while (len > 0) {
 361
 362                if (*cp == SYSR_TOK_END)
 363                        return (0);     /* End of Token stream */
 364
 365                if (len < (*(cp + 1) + 2)) {
 366                        hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
 367                        return (1);
 368                }
 369                switch (*cp) {
 370                case SYSR_TOK_B_CHAN:   /* 1 */
 371                        if (*(cp + 1) != 1)
 372                                return (1);     /* length invalid */
 373                        card->bchans = *(cp + 2);
 374                        break;
 375
 376                case SYSR_TOK_FAX_CHAN: /* 2 */
 377                        if (*(cp + 1) != 1)
 378                                return (1);     /* length invalid */
 379                        card->faxchans = *(cp + 2);
 380                        break;
 381
 382                case SYSR_TOK_MAC_ADDR: /* 3 */
 383                        if (*(cp + 1) != 6)
 384                                return (1);     /* length invalid */
 385                        memcpy(card->mac_addr, cp + 2, 6);
 386                        break;
 387
 388                default:
 389                        hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
 390                        break;
 391                }
 392                len -= (*(cp + 1) + 2);         /* adjust len */
 393                cp += (*(cp + 1) + 2);  /* and pointer */
 394        }
 395
 396        hysdn_addlog(card, "no end token found");
 397        return (1);
 398}                               /* EvalSysrTokData */
 399