1/* $Id: hysdn_boot.c,v 1.1.1.1 2007/08/03 18:52:36 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 <asm/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