blob: a19c2240db201367bff44c26526deb87f0eb86e0 [file] [log] [blame]
Amol Jadi7d3ad062013-01-04 15:30:19 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Shashank Mittal1fcde7a2011-07-25 13:41:50 -07002
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
Neeti Desai992ed6c2012-09-20 11:59:12 -070012 * * Neither the name of The Linux Foundation nor the names of its
Shashank Mittal1fcde7a2011-07-25 13:41:50 -070013 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
Channagoud Kadabi6d6223c2013-08-05 15:27:55 -070029#include <arch/defines.h>
Shashank Mittal1fcde7a2011-07-25 13:41:50 -070030#include <string.h>
31#include <endian.h>
32#include <debug.h>
33#include <reg.h>
34#include <bits.h>
35#include <platform/iomap.h>
36#include <crypto4_eng.h>
37#include <crypto_hash.h>
Deepa Dinamani193874e2012-02-07 14:00:04 -080038#include <scm.h>
Neeti Desai992ed6c2012-09-20 11:59:12 -070039#include <smem.h>
Shashank Mittal1fcde7a2011-07-25 13:41:50 -070040
Deepa Dinamani3467b712012-02-10 14:56:06 -080041extern void ce_async_reset();
Shashank Mittal1fcde7a2011-07-25 13:41:50 -070042
Neeti Desai992ed6c2012-09-20 11:59:12 -070043void wr_ce(uint32_t val,uint32_t reg)
44{
Channagoud Kadabif6729002013-01-16 16:42:57 -080045 uint32_t platform_id;
Neeti Desai992ed6c2012-09-20 11:59:12 -070046
Channagoud Kadabif6729002013-01-16 16:42:57 -080047 platform_id = board_platform_id();
48
49 if((platform_id == APQ8064) || (platform_id == APQ8064AA)
50 || (platform_id == APQ8064AB))
Neeti Desai992ed6c2012-09-20 11:59:12 -070051 writel(val,CRYPTO_ENG_REG(CE3_CRYPTO4_BASE, reg));
Channagoud Kadabif6729002013-01-16 16:42:57 -080052 else
53 writel(val,CRYPTO_ENG_REG(CE1_CRYPTO4_BASE, reg));
Neeti Desai992ed6c2012-09-20 11:59:12 -070054}
55uint32_t rd_ce(uint32_t reg)
56{
57
58 uint32_t val;
Channagoud Kadabif6729002013-01-16 16:42:57 -080059 uint32_t platform_id;
Neeti Desai992ed6c2012-09-20 11:59:12 -070060
Channagoud Kadabif6729002013-01-16 16:42:57 -080061 platform_id = board_platform_id();
62
63 if((platform_id == APQ8064) || (platform_id == APQ8064AA)
64 || (platform_id == APQ8064AB))
65 val = readl(CRYPTO_ENG_REG(CE3_CRYPTO4_BASE, reg));
Neeti Desai992ed6c2012-09-20 11:59:12 -070066 else
Channagoud Kadabif6729002013-01-16 16:42:57 -080067 val = readl(CRYPTO_ENG_REG(CE1_CRYPTO4_BASE, reg));
Neeti Desai992ed6c2012-09-20 11:59:12 -070068
69 return val;
70}
71
Shashank Mittal1fcde7a2011-07-25 13:41:50 -070072/*
73 * Function to reset the crypto engine.
74 */
75
76void crypto_eng_reset(void)
77{
Deepa Dinamani3467b712012-02-10 14:56:06 -080078 ce_async_reset();
Shashank Mittal1fcde7a2011-07-25 13:41:50 -070079 return;
80}
81
Deepa Dinamani193874e2012-02-07 14:00:04 -080082
83/* Function to switch the CE1 context
84 * from register to ADM
85 */
86void crypto_eng_cleanup(void)
87{
88
89 unsigned int val;
90
91 enum ap_ce_channel_type chn = AP_CE_ADM_USE;
92 /* Make a SMC call to TZ to make CE1 use ADM interface for HLOS*/
93 val = switch_ce_chn_cmd(chn);
94 dprintf(INFO, "TZ channel swith returned %d\n", val);
95
96}
97
98
Shashank Mittal1fcde7a2011-07-25 13:41:50 -070099/*
100 * Function to initialize the crypto engine for a new session. It enables the
101 * auto shutdown feature of CRYPTO and mask various interrupts since we use
102 * polling. We are not using DMOV now.
103 */
104
105void crypto_eng_init(void)
106{
Neeti Desai992ed6c2012-09-20 11:59:12 -0700107
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700108 unsigned int val;
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700109
Deepa Dinamani193874e2012-02-07 14:00:04 -0800110 enum ap_ce_channel_type chn = AP_CE_REGISTER_USE;
Neeti Desai992ed6c2012-09-20 11:59:12 -0700111 /* Make a SMC call to TZ to make CE1 use register interface for HLOS*/
Deepa Dinamani193874e2012-02-07 14:00:04 -0800112 val = switch_ce_chn_cmd(chn);
113 dprintf(INFO, "TZ channel swith returned %d\n", val);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700114}
115
116/*
117 * Function to set various SHAx registers in CRYPTO based on algorithm type.
118 */
119
Ajay Dudanib01e5062011-12-03 23:23:42 -0800120void
121crypto_set_sha_ctx(void *ctx_ptr, unsigned int bytes_to_write,
122 crypto_auth_alg_type auth_alg, bool first, bool last)
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700123{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800124 crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx *) ctx_ptr;
125 crypto_SHA256_ctx *sha256_ctx = (crypto_SHA256_ctx *) ctx_ptr;
126 unsigned int i = 0;
127 unsigned int iv_len = 0;
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700128 unsigned int *auth_iv;
129 unsigned int seg_cfg_val;
130
131 seg_cfg_val = SEG_CFG_AUTH_ALG_SHA;
132
Ajay Dudanib01e5062011-12-03 23:23:42 -0800133 if (auth_alg == CRYPTO_AUTH_ALG_SHA1) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700134 seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA1;
135
Ajay Dudanib01e5062011-12-03 23:23:42 -0800136 if (last) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700137 seg_cfg_val |= SEG_CFG_LAST;
138 }
139
140 iv_len = SHA1_INIT_VECTOR_SIZE;
141 auth_iv = sha1_ctx->auth_iv;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800142 } else if (auth_alg == CRYPTO_AUTH_ALG_SHA256) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700143 seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA256;
144
Ajay Dudanib01e5062011-12-03 23:23:42 -0800145 if (last) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700146 seg_cfg_val |= SEG_CFG_LAST;
147 }
148
149 iv_len = SHA256_INIT_VECTOR_SIZE;
150 auth_iv = sha256_ctx->auth_iv;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800151 } else {
152 dprintf(CRITICAL,
153 "crypto_set_sha_ctx invalid auth algorithm\n");
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700154 return;
155 }
156
Ajay Dudanib01e5062011-12-03 23:23:42 -0800157 for (i = 0; i < iv_len; i++) {
158 wr_ce(*(auth_iv + i), CRYPTO_AUTH_IVn(i));
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700159 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800160 wr_ce(seg_cfg_val, CRYPTO_AUTH_SEG_CFG);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700161
162 /* Typecast with crypto_SHA1_ctx because offset of auth_bytecnt in both
163 crypto_SHA1_ctx and crypto_SHA256_ctx are same */
164
Ajay Dudanib01e5062011-12-03 23:23:42 -0800165 wr_ce(((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[0],
166 CRYPTO_AUTH_BYTECNTn(0));
167 wr_ce(((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[1],
168 CRYPTO_AUTH_BYTECNTn(1));
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700169
Ajay Dudanib01e5062011-12-03 23:23:42 -0800170 wr_ce(bytes_to_write, CRYPTO_AUTH_SEG_SIZE);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700171
Ajay Dudanib01e5062011-12-03 23:23:42 -0800172 wr_ce(bytes_to_write, CRYPTO_SEG_SIZE);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700173
174 /*
175 * Ensure previous instructions (any writes to config registers)
176 * are completed.
177 *
178 * TODO: Revisit dsb.
179 */
180 dsb();
181
Ajay Dudanib01e5062011-12-03 23:23:42 -0800182 wr_ce(GOPROC_GO, CRYPTO_GOPROC);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700183
184 return;
185}
186
187/*
188 * Function to send data to CRYPTO. This is non-DMOV implementation and uses
189 * polling to send the requested amount of data.
190 */
191
Ajay Dudanib01e5062011-12-03 23:23:42 -0800192void
193crypto_send_data(void *ctx_ptr, unsigned char *data_ptr,
194 unsigned int buff_size, unsigned int bytes_to_write,
195 unsigned int *ret_status)
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700196{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800197 crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx *) ctx_ptr;
198 unsigned int bytes_left = 0;
199 unsigned int i = 0;
200 unsigned int ce_status = 0;
201 unsigned int ce_err_bmsk = 0;
202 unsigned int is_not_aligned = FALSE;
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700203 unsigned char data[4];
Ajay Dudanib01e5062011-12-03 23:23:42 -0800204 unsigned char *buff_ptr = data_ptr;
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700205
206 /* Check if the buff_ptr is aligned */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800207 if (!(IS_ALIGNED(buff_ptr))) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700208 is_not_aligned = TRUE;
209 }
210
211 /* Fill the saved_buff with data from buff_ptr. First we have to write
212 all the data from the saved_buff and then we will write data from
213 buff_ptr. We will update bytes_left and buff_ptr in the while loop
214 once are done writing all the data from saved_buff. */
215
Ajay Dudanib01e5062011-12-03 23:23:42 -0800216 if (sha1_ctx->saved_buff_indx != 0) {
217 memcpy(sha1_ctx->saved_buff + sha1_ctx->saved_buff_indx,
218 buff_ptr,
219 (((buff_size + sha1_ctx->saved_buff_indx) <=
220 CRYPTO_SHA_BLOCK_SIZE)
221 ? buff_size : (CRYPTO_SHA_BLOCK_SIZE -
222 sha1_ctx->saved_buff_indx)));
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700223
Ajay Dudanib01e5062011-12-03 23:23:42 -0800224 if (bytes_to_write >= CRYPTO_SHA_BLOCK_SIZE) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700225 bytes_left = CRYPTO_SHA_BLOCK_SIZE;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800226 } else {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700227 bytes_left = bytes_to_write;
228 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800229 } else {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700230 bytes_left = bytes_to_write;
231 }
232
233 /* Error bitmask to check crypto engine status */
234 ce_err_bmsk = (SW_ERR | DIN_RDY | DIN_SIZE_AVAIL);
235
Ajay Dudanib01e5062011-12-03 23:23:42 -0800236 while (bytes_left >= 4) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700237 ce_status = rd_ce(CRYPTO_STATUS);
238 ce_status &= ce_err_bmsk;
239
Ajay Dudanib01e5062011-12-03 23:23:42 -0800240 if (ce_status & SW_ERR) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700241 /* If there is SW_ERR, reset the engine */
242 crypto_eng_reset();
243 *ret_status = CRYPTO_ERR_FAIL;
244 dprintf(CRITICAL, "crypto_send_data sw error\n");
245 return;
246 }
247
248 /* We can write data now - 4 bytes at a time in network byte order */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800249 if ((ce_status & DIN_RDY)
250 && ((ce_status & DIN_SIZE_AVAIL) >= 4)) {
251 if (sha1_ctx->saved_buff_indx != 0) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700252 /* Write from saved_buff */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800253 wr_ce(htonl
254 (*
255 ((unsigned int *)(sha1_ctx->saved_buff) +
256 i)), CRYPTO_DATA_IN);
257 } else {
258 if (!is_not_aligned) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700259 /* Write from buff_ptr aligned */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800260 wr_ce(htonl
261 (*((unsigned int *)buff_ptr + i)),
262 CRYPTO_DATA_IN);
263 } else {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700264 /* If buff_ptr is not aligned write byte by byte */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800265 data[0] = *(buff_ptr + i);
266 data[1] = *(buff_ptr + i + 1);
267 data[2] = *(buff_ptr + i + 2);
268 data[3] = *(buff_ptr + i + 3);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700269 /* i will incremented by 1 in outside block */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800270 i += 3;
271 wr_ce(htonl(*(unsigned int *)data),
272 CRYPTO_DATA_IN);
273 memset(data, 0, 4);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700274 }
275 }
276 i++;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800277 bytes_left -= 4;
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700278
279 /* Check if we have written from saved_buff. Adjust buff_ptr and
280 bytes_left accordingly */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800281 if ((sha1_ctx->saved_buff_indx != 0)
282 && (bytes_left == 0)
283 && (bytes_to_write > CRYPTO_SHA_BLOCK_SIZE)) {
284 bytes_left =
285 (bytes_to_write - CRYPTO_SHA_BLOCK_SIZE);
286 buff_ptr =
287 (unsigned char *)((unsigned char *)data_ptr
288 + CRYPTO_SHA_BLOCK_SIZE -
289 sha1_ctx->
290 saved_buff_indx);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700291 i = 0;
292 sha1_ctx->saved_buff_indx = 0;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800293 if (!(IS_ALIGNED(buff_ptr))) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700294 is_not_aligned = TRUE;
295 }
296 }
297 }
298 }
299
300 /* We might have bytes_left < 4. Write them now if available */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800301 if (bytes_left) {
302 memset(data, 0, sizeof(unsigned int));
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700303
Ajay Dudanib01e5062011-12-03 23:23:42 -0800304 if (sha1_ctx->saved_buff_indx)
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700305 buff_ptr = (sha1_ctx->saved_buff + bytes_to_write - 1);
306 else
Ajay Dudanib01e5062011-12-03 23:23:42 -0800307 buff_ptr =
308 (((unsigned char *)data_ptr) + buff_size - 1);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700309
Ajay Dudanib01e5062011-12-03 23:23:42 -0800310 for (i = 0; i < bytes_left; i++) {
311 data[3 - i] = *(buff_ptr - bytes_left + i + 1);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700312 }
313
314 ce_status = rd_ce(CRYPTO_STATUS);
315 ce_status &= ce_err_bmsk;
316
Ajay Dudanib01e5062011-12-03 23:23:42 -0800317 if (ce_status & SW_ERR) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700318 crypto_eng_reset();
319 *ret_status = CRYPTO_ERR_FAIL;
320 dprintf(CRITICAL, "crypto_send_data sw error 2\n");
321 return;
322 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800323 if ((ce_status & DIN_RDY)
324 && ((ce_status & DIN_SIZE_AVAIL) >= 4)) {
325 wr_ce(*(unsigned int *)data, CRYPTO_DATA_IN);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700326 }
327 }
328 *ret_status = CRYPTO_ERR_NONE;
329 return;
330}
331
332/*
333 * Function to get digest from CRYPTO. We poll for AUTH_DONE from CRYPTO.
334 */
335
Ajay Dudanib01e5062011-12-03 23:23:42 -0800336void
337crypto_get_digest(unsigned char *digest_ptr, unsigned int *ret_status,
338 crypto_auth_alg_type auth_alg, bool last)
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700339{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800340 unsigned int ce_status = 0;
341 unsigned int ce_err_bmsk = 0;
342 unsigned int i = 0;
343 unsigned int digest_len = 0;
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700344
345 ce_err_bmsk = (OPERATION_DONE | SW_ERR);
346
Ajay Dudanib01e5062011-12-03 23:23:42 -0800347 do {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700348 ce_status = rd_ce(CRYPTO_STATUS);
349 ce_status &= ce_err_bmsk;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800350 }
351 while (ce_status == 0);
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700352
Ajay Dudanib01e5062011-12-03 23:23:42 -0800353 if (ce_status & SW_ERR) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700354 crypto_eng_reset();
355 *ret_status = CRYPTO_ERR_FAIL;
356 dprintf(CRITICAL, "crypto_get_digest sw error\n");
357 return;
358 }
359
360 /* Digest length depends on auth_alg */
361
Ajay Dudanib01e5062011-12-03 23:23:42 -0800362 if (auth_alg == CRYPTO_AUTH_ALG_SHA1) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700363 digest_len = SHA1_INIT_VECTOR_SIZE;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800364 } else if (auth_alg == CRYPTO_AUTH_ALG_SHA256) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700365 digest_len = SHA256_INIT_VECTOR_SIZE;
366 }
367
368 /* Retrieve digest from CRYPTO */
369
Ajay Dudanib01e5062011-12-03 23:23:42 -0800370 for (i = 0; i < digest_len; i++) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700371 unsigned int auth_iv = rd_ce(CRYPTO_AUTH_IVn(i));
372
Ajay Dudanib01e5062011-12-03 23:23:42 -0800373 if (last) {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700374 *((unsigned int *)digest_ptr + i) = htonl(auth_iv);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800375 } else {
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700376 *((unsigned int *)digest_ptr + i) = auth_iv;
377 }
378 }
379 *ret_status = CRYPTO_ERR_NONE;
380 return;
381}
382
383/* Function to restore auth_bytecnt registers for ctx_ptr */
384
385void crypto_get_ctx(void *ctx_ptr)
386{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800387 ((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[0] =
388 rd_ce(CRYPTO_AUTH_BYTECNTn(0));
389 ((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[1] =
390 rd_ce(CRYPTO_AUTH_BYTECNTn(1));
Shashank Mittal1fcde7a2011-07-25 13:41:50 -0700391 return;
392}
Amol Jadi7d3ad062013-01-04 15:30:19 -0800393
394/* Returns the max authentication block size */
395uint32_t crypto_get_max_auth_blk_size()
396{
397 return 0xFA00;
398}