GNU Linux-libre 4.19.245-gnu1
[releases.git] / drivers / isdn / hysdn / hysdn_boot.c
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 /************************************************************/
32 struct 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 /*****************************************************/
56 static void
57 StartDecryption(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 /***************************************************************/
69 static void
70 DecryptBuf(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 /********************************************************************************/
84 static int
85 pof_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                 /* fall through */
103         case TAG_BOOTDTA:
104                 if (card->debug_flags & LOG_POF_RECORD)
105                         hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
106                                      (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
107                                      datlen, boot->pof_recoffset);
108
109                 if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
110                         boot->last_error = EPOF_BAD_IMG_SIZE;   /* invalid length */
111                         return (boot->last_error);
112                 }
113                 imgp = boot->buf.BootBuf;       /* start of buffer */
114                 img_len = datlen;       /* maximum length to transfer */
115
116                 l = POF_BOOT_LOADER_OFF_IN_PAGE -
117                         (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
118                 if (l > 0) {
119                         /* buffer needs to be truncated */
120                         imgp += l;      /* advance pointer */
121                         img_len -= l;   /* adjust len */
122                 }
123                 /* at this point no special handling for data wrapping over buffer */
124                 /* is necessary, because the boot image always will be adjusted to */
125                 /* match a page boundary inside the buffer.                        */
126                 /* The buffer for the boot image on the card is filled in 2 cycles */
127                 /* first the 1024 hi-words are put in the buffer, then the low 1024 */
128                 /* word are handled in the same way with different offset.         */
129
130                 if (img_len > 0) {
131                         /* data available for copy */
132                         if ((boot->last_error =
133                              card->writebootimg(card, imgp,
134                                                 (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
135                                 return (boot->last_error);
136                 }
137                 break;  /* end of case boot image hi/lo */
138
139         case TAG_CABSDATA:
140                 DecryptBuf(boot, datlen);       /* we need to encrypt the buffer */
141                 /* fall through */
142         case TAG_ABSDATA:
143                 if (card->debug_flags & LOG_POF_RECORD)
144                         hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
145                                      (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
146                                      datlen, boot->pof_recoffset);
147
148                 if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)
149                         return (boot->last_error);      /* error writing data */
150
151                 if (boot->pof_recoffset + datlen >= boot->pof_reclen)
152                         return (card->waitpofready(card));      /* data completely spooled, wait for ready */
153
154                 break;  /* end of case boot seq data */
155
156         default:
157                 if (card->debug_flags & LOG_POF_RECORD)
158                         hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
159                                      datlen, boot->pof_recoffset);
160
161                 break;  /* simply skip record */
162         }                       /* switch boot->pof_recid */
163
164         return (0);
165 }                               /* pof_handle_data */
166
167
168 /******************************************************************************/
169 /* pof_write_buffer is called when the buffer has been filled with the needed */
170 /* number of data bytes. The number delivered is additionally supplied for    */
171 /* verification. The functions handles the data and returns the needed number */
172 /* of bytes for the next action. If the returned value is 0 or less an error  */
173 /* occurred and booting must be aborted.                                       */
174 /******************************************************************************/
175 int
176 pof_write_buffer(hysdn_card *card, int datlen)
177 {
178         struct boot_data *boot = card->boot;    /* pointer to boot specific data */
179
180         if (!boot)
181                 return (-EFAULT);       /* invalid call */
182         if (boot->last_error < 0)
183                 return (boot->last_error);      /* repeated error */
184
185         if (card->debug_flags & LOG_POF_WRITE)
186                 hysdn_addlog(card, "POF write: got %d bytes ", datlen);
187
188         switch (boot->pof_state) {
189         case POF_READ_FILE_HEAD:
190                 if (card->debug_flags & LOG_POF_WRITE)
191                         hysdn_addlog(card, "POF write: checking file header");
192
193                 if (datlen != sizeof(tPofFileHdr)) {
194                         boot->last_error = -EPOF_INTERNAL;
195                         break;
196                 }
197                 if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
198                         boot->last_error = -EPOF_BAD_MAGIC;
199                         break;
200                 }
201                 /* Setup the new state and vars */
202                 boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
203                 boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
204                 boot->last_error = sizeof(tPofRecHdr);  /* new length */
205                 break;
206
207         case POF_READ_TAG_HEAD:
208                 if (card->debug_flags & LOG_POF_WRITE)
209                         hysdn_addlog(card, "POF write: checking tag header");
210
211                 if (datlen != sizeof(tPofRecHdr)) {
212                         boot->last_error = -EPOF_INTERNAL;
213                         break;
214                 }
215                 boot->pof_recid = boot->buf.PofRecHdr.PofRecId;         /* actual pof recid */
216                 boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen;   /* total length */
217                 boot->pof_recoffset = 0;        /* no starting offset */
218
219                 if (card->debug_flags & LOG_POF_RECORD)
220                         hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
221                                      boot->pof_recid, boot->pof_reclen);
222
223                 boot->pof_state = POF_READ_TAG_DATA;    /* now start with tag data */
224                 if (boot->pof_reclen < BOOT_BUF_SIZE)
225                         boot->last_error = boot->pof_reclen;    /* limit size */
226                 else
227                         boot->last_error = BOOT_BUF_SIZE;       /* maximum */
228
229                 if (!boot->last_error) {        /* no data inside record */
230                         boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
231                         boot->last_error = sizeof(tPofRecHdr);  /* new length */
232                 }
233                 break;
234
235         case POF_READ_TAG_DATA:
236                 if (card->debug_flags & LOG_POF_WRITE)
237                         hysdn_addlog(card, "POF write: getting tag data");
238
239                 if (datlen != boot->last_error) {
240                         boot->last_error = -EPOF_INTERNAL;
241                         break;
242                 }
243                 if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
244                         return (boot->last_error);      /* an error occurred */
245                 boot->pof_recoffset += datlen;
246                 if (boot->pof_recoffset >= boot->pof_reclen) {
247                         boot->pof_state = POF_READ_TAG_HEAD;    /* now start with single tags */
248                         boot->last_error = sizeof(tPofRecHdr);  /* new length */
249                 } else {
250                         if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
251                                 boot->last_error = boot->pof_reclen - boot->pof_recoffset;      /* limit size */
252                         else
253                                 boot->last_error = BOOT_BUF_SIZE;       /* maximum */
254                 }
255                 break;
256
257         default:
258                 boot->last_error = -EPOF_INTERNAL;      /* unknown state */
259                 break;
260         }                       /* switch (boot->pof_state) */
261
262         return (boot->last_error);
263 }                               /* pof_write_buffer */
264
265
266 /*******************************************************************************/
267 /* pof_write_open is called when an open for boot on the cardlog device occurs. */
268 /* The function returns the needed number of bytes for the next operation. If  */
269 /* the returned number is less or equal 0 an error specified by this code      */
270 /* occurred. Additionally the pointer to the buffer data area is set on success */
271 /*******************************************************************************/
272 int
273 pof_write_open(hysdn_card *card, unsigned char **bufp)
274 {
275         struct boot_data *boot; /* pointer to boot specific data */
276
277         if (card->boot) {
278                 if (card->debug_flags & LOG_POF_OPEN)
279                         hysdn_addlog(card, "POF open: already opened for boot");
280                 return (-ERR_ALREADY_BOOT);     /* boot already active */
281         }
282         /* error no mem available */
283         if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
284                 if (card->debug_flags & LOG_MEM_ERR)
285                         hysdn_addlog(card, "POF open: unable to allocate mem");
286                 return (-EFAULT);
287         }
288         card->boot = boot;
289         card->state = CARD_STATE_BOOTING;
290
291         card->stopcard(card);   /* first stop the card */
292         if (card->testram(card)) {
293                 if (card->debug_flags & LOG_POF_OPEN)
294                         hysdn_addlog(card, "POF open: DPRAM test failure");
295                 boot->last_error = -ERR_BOARD_DPRAM;
296                 card->state = CARD_STATE_BOOTERR;       /* show boot error */
297                 return (boot->last_error);
298         }
299         boot->BufSize = 0;      /* Buffer is empty */
300         boot->pof_state = POF_READ_FILE_HEAD;   /* read file header */
301         StartDecryption(boot);  /* if POF File should be encrypted */
302
303         if (card->debug_flags & LOG_POF_OPEN)
304                 hysdn_addlog(card, "POF open: success");
305
306         *bufp = boot->buf.BootBuf;      /* point to buffer */
307         return (sizeof(tPofFileHdr));
308 }                               /* pof_write_open */
309
310 /********************************************************************************/
311 /* pof_write_close is called when an close of boot on the cardlog device occurs. */
312 /* The return value must be 0 if everything has happened as desired.            */
313 /********************************************************************************/
314 int
315 pof_write_close(hysdn_card *card)
316 {
317         struct boot_data *boot = card->boot;    /* pointer to boot specific data */
318
319         if (!boot)
320                 return (-EFAULT);       /* invalid call */
321
322         card->boot = NULL;      /* no boot active */
323         kfree(boot);
324
325         if (card->state == CARD_STATE_RUN)
326                 card->set_errlog_state(card, 1);        /* activate error log */
327
328         if (card->debug_flags & LOG_POF_OPEN)
329                 hysdn_addlog(card, "POF close: success");
330
331         return (0);
332 }                               /* pof_write_close */
333
334 /*********************************************************************************/
335 /* EvalSysrTokData checks additional records delivered with the Sysready Message */
336 /* when POF has been booted. A return value of 0 is used if no error occurred.    */
337 /*********************************************************************************/
338 int
339 EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
340 {
341         u_char *p;
342         u_char crc;
343
344         if (card->debug_flags & LOG_POF_RECORD)
345                 hysdn_addlog(card, "SysReady Token data length %d", len);
346
347         if (len < 2) {
348                 hysdn_addlog(card, "SysReady Token Data to short");
349                 return (1);
350         }
351         for (p = cp, crc = 0; p < (cp + len - 2); p++)
352                 if ((crc & 0x80))
353                         crc = (((u_char) (crc << 1)) + 1) + *p;
354                 else
355                         crc = ((u_char) (crc << 1)) + *p;
356         crc = ~crc;
357         if (crc != *(cp + len - 1)) {
358                 hysdn_addlog(card, "SysReady Token Data invalid CRC");
359                 return (1);
360         }
361         len--;                  /* don't check CRC byte */
362         while (len > 0) {
363
364                 if (*cp == SYSR_TOK_END)
365                         return (0);     /* End of Token stream */
366
367                 if (len < (*(cp + 1) + 2)) {
368                         hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
369                         return (1);
370                 }
371                 switch (*cp) {
372                 case SYSR_TOK_B_CHAN:   /* 1 */
373                         if (*(cp + 1) != 1)
374                                 return (1);     /* length invalid */
375                         card->bchans = *(cp + 2);
376                         break;
377
378                 case SYSR_TOK_FAX_CHAN: /* 2 */
379                         if (*(cp + 1) != 1)
380                                 return (1);     /* length invalid */
381                         card->faxchans = *(cp + 2);
382                         break;
383
384                 case SYSR_TOK_MAC_ADDR: /* 3 */
385                         if (*(cp + 1) != 6)
386                                 return (1);     /* length invalid */
387                         memcpy(card->mac_addr, cp + 2, 6);
388                         break;
389
390                 default:
391                         hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
392                         break;
393                 }
394                 len -= (*(cp + 1) + 2);         /* adjust len */
395                 cp += (*(cp + 1) + 2);  /* and pointer */
396         }
397
398         hysdn_addlog(card, "no end token found");
399         return (1);
400 }                               /* EvalSysrTokData */