1 /*
2 * Copyright 2021 NXP
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include <assert.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 #include <arch_helpers.h>
16 #include "caam.h"
17 #include <common/debug.h>
18 #include "jobdesc.h"
19 #include "sec_hw_specific.h"
20
21
22 /* Job rings used for communication with SEC HW */
23 extern struct sec_job_ring_t g_job_rings[MAX_SEC_JOB_RINGS];
24
25 /* The current state of SEC user space driver */
26 extern volatile sec_driver_state_t g_driver_state;
27
28 /* The number of job rings used by SEC user space driver */
29 extern int g_job_rings_no;
30
31 /* LOCAL FUNCTIONS */
hw_set_input_ring_start_addr(struct jobring_regs * regs,phys_addr_t * start_addr)32 static inline void hw_set_input_ring_start_addr(struct jobring_regs *regs,
33 phys_addr_t *start_addr)
34 {
35 #if defined(CONFIG_PHYS_64BIT)
36 sec_out32(®s->irba_h, PHYS_ADDR_HI(start_addr));
37 #else
38 sec_out32(®s->irba_h, 0);
39 #endif
40 sec_out32(®s->irba_l, PHYS_ADDR_LO(start_addr));
41 }
42
hw_set_output_ring_start_addr(struct jobring_regs * regs,phys_addr_t * start_addr)43 static inline void hw_set_output_ring_start_addr(struct jobring_regs *regs,
44 phys_addr_t *start_addr)
45 {
46 #if defined(CONFIG_PHYS_64BIT)
47 sec_out32(®s->orba_h, PHYS_ADDR_HI(start_addr));
48 #else
49 sec_out32(®s->orba_h, 0);
50 #endif
51 sec_out32(®s->orba_l, PHYS_ADDR_LO(start_addr));
52 }
53
54 /* ORJR - Output Ring Jobs Removed Register shows how many jobs were
55 * removed from the Output Ring for processing by software. This is done after
56 * the software has processed the entries.
57 */
hw_remove_entries(sec_job_ring_t * jr,int num)58 static inline void hw_remove_entries(sec_job_ring_t *jr, int num)
59 {
60 struct jobring_regs *regs =
61 (struct jobring_regs *)jr->register_base_addr;
62
63 sec_out32(®s->orjr, num);
64 }
65
66 /* IRSA - Input Ring Slots Available register holds the number of entries in
67 * the Job Ring's input ring. Once a job is enqueued, the value returned is
68 * decremented by the hardware by the number of jobs enqueued.
69 */
hw_get_available_slots(sec_job_ring_t * jr)70 static inline int hw_get_available_slots(sec_job_ring_t *jr)
71 {
72 struct jobring_regs *regs =
73 (struct jobring_regs *)jr->register_base_addr;
74
75 return sec_in32(®s->irsa);
76 }
77
78 /* ORSFR - Output Ring Slots Full register holds the number of jobs which were
79 * processed by the SEC and can be retrieved by the software. Once a job has
80 * been processed by software, the user will call hw_remove_one_entry in order
81 * to notify the SEC that the entry was processed
82 */
hw_get_no_finished_jobs(sec_job_ring_t * jr)83 static inline int hw_get_no_finished_jobs(sec_job_ring_t *jr)
84 {
85 struct jobring_regs *regs =
86 (struct jobring_regs *)jr->register_base_addr;
87
88 return sec_in32(®s->orsf);
89 }
90
91 /* @brief Process Jump Halt Condition related errors
92 * @param [in] error_code The error code in the descriptor status word
93 */
hw_handle_jmp_halt_cond_err(union hw_error_code error_code)94 static inline void hw_handle_jmp_halt_cond_err(union hw_error_code error_code)
95 {
96 ERROR("JMP %x\n", error_code.error_desc.jmp_halt_cond_src.jmp);
97 ERROR("Descriptor Index: %d\n",
98 error_code.error_desc.jmp_halt_cond_src.desc_idx);
99 ERROR(" Condition %x\n", error_code.error_desc.jmp_halt_cond_src.cond);
100 }
101
102 /* @brief Process DECO related errors
103 * @param [in] error_code The error code in the descriptor status word
104 */
hw_handle_deco_err(union hw_error_code error_code)105 static inline void hw_handle_deco_err(union hw_error_code error_code)
106 {
107 ERROR("JMP %x\n", error_code.error_desc.deco_src.jmp);
108 ERROR("Descriptor Index: 0x%x",
109 error_code.error_desc.deco_src.desc_idx);
110
111 switch (error_code.error_desc.deco_src.desc_err) {
112 case SEC_HW_ERR_DECO_HFN_THRESHOLD:
113 WARN(" Descriptor completed but exceeds the Threshold");
114 break;
115 default:
116 ERROR("Error 0x%04x not implemented",
117 error_code.error_desc.deco_src.desc_err);
118 break;
119 }
120 }
121
122 /* @brief Process Jump Halt User Status related errors
123 * @param [in] error_code The error code in the descriptor status word
124 */
hw_handle_jmp_halt_user_err(union hw_error_code error_code)125 static inline void hw_handle_jmp_halt_user_err(union hw_error_code error_code)
126 {
127 WARN(" Not implemented");
128 }
129
130 /* @brief Process CCB related errors
131 * @param [in] error_code The error code in the descriptor status word
132 */
hw_handle_ccb_err(union hw_error_code hw_error_code)133 static inline void hw_handle_ccb_err(union hw_error_code hw_error_code)
134 {
135 WARN(" Not implemented");
136 }
137
138 /* @brief Process Job Ring related errors
139 * @param [in] error_code The error code in the descriptor status word
140 */
hw_handle_jr_err(union hw_error_code hw_error_code)141 static inline void hw_handle_jr_err(union hw_error_code hw_error_code)
142 {
143 WARN(" Not implemented");
144 }
145
146 /* GLOBAL FUNCTIONS */
147
hw_reset_job_ring(sec_job_ring_t * job_ring)148 int hw_reset_job_ring(sec_job_ring_t *job_ring)
149 {
150 int ret = 0;
151 struct jobring_regs *regs =
152 (struct jobring_regs *)job_ring->register_base_addr;
153
154 /* First reset the job ring in hw */
155 ret = hw_shutdown_job_ring(job_ring);
156 if (ret != 0) {
157 ERROR("Failed resetting job ring in hardware");
158 return ret;
159 }
160 /* In order to have the HW JR in a workable state
161 *after a reset, I need to re-write the input
162 * queue size, input start address, output queue
163 * size and output start address
164 * Write the JR input queue size to the HW register
165 */
166 sec_out32(®s->irs, SEC_JOB_RING_SIZE);
167
168 /* Write the JR output queue size to the HW register */
169 sec_out32(®s->ors, SEC_JOB_RING_SIZE);
170
171 /* Write the JR input queue start address */
172 hw_set_input_ring_start_addr(regs, vtop(job_ring->input_ring));
173
174 /* Write the JR output queue start address */
175 hw_set_output_ring_start_addr(regs, vtop(job_ring->output_ring));
176
177 return 0;
178 }
179
hw_shutdown_job_ring(sec_job_ring_t * job_ring)180 int hw_shutdown_job_ring(sec_job_ring_t *job_ring)
181 {
182 struct jobring_regs *regs =
183 (struct jobring_regs *)job_ring->register_base_addr;
184 unsigned int timeout = SEC_TIMEOUT;
185 uint32_t tmp = 0U;
186
187 VERBOSE("Resetting Job ring\n");
188
189 /*
190 * Mask interrupts since we are going to poll
191 * for reset completion status
192 * Also, at POR, interrupts are ENABLED on a JR, thus
193 * this is the point where I can disable them without
194 * changing the code logic too much
195 */
196
197 jr_disable_irqs(job_ring);
198
199 /* initiate flush (required prior to reset) */
200 sec_out32(®s->jrcr, JR_REG_JRCR_VAL_RESET);
201
202 /* dummy read */
203 tmp = sec_in32(®s->jrcr);
204
205 do {
206 tmp = sec_in32(®s->jrint);
207 } while (((tmp & JRINT_ERR_HALT_MASK) ==
208 JRINT_ERR_HALT_INPROGRESS) && ((--timeout) != 0U));
209
210 if ((tmp & JRINT_ERR_HALT_MASK) != JRINT_ERR_HALT_COMPLETE ||
211 timeout == 0U) {
212 ERROR("Failed to flush hw job ring %x\n %u", tmp, timeout);
213 /* unmask interrupts */
214 if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) {
215 jr_enable_irqs(job_ring);
216 }
217 return -1;
218 }
219 /* Initiate reset */
220 timeout = SEC_TIMEOUT;
221 sec_out32(®s->jrcr, JR_REG_JRCR_VAL_RESET);
222
223 do {
224 tmp = sec_in32(®s->jrcr);
225 } while (((tmp & JR_REG_JRCR_VAL_RESET) != 0U) &&
226 ((--timeout) != 0U));
227
228 if (timeout == 0U) {
229 ERROR("Failed to reset hw job ring\n");
230 /* unmask interrupts */
231 if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) {
232 jr_enable_irqs(job_ring);
233 }
234 return -1;
235 }
236 /* unmask interrupts */
237 if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) {
238 jr_enable_irqs(job_ring);
239 }
240 return 0;
241
242 }
243
hw_handle_job_ring_error(sec_job_ring_t * job_ring,uint32_t error_code)244 void hw_handle_job_ring_error(sec_job_ring_t *job_ring, uint32_t error_code)
245 {
246 union hw_error_code hw_err_code;
247
248 hw_err_code.error = error_code;
249
250 switch (hw_err_code.error_desc.value.ssrc) {
251 case SEC_HW_ERR_SSRC_NO_SRC:
252 INFO("No Status Source ");
253 break;
254 case SEC_HW_ERR_SSRC_CCB_ERR:
255 INFO("CCB Status Source");
256 hw_handle_ccb_err(hw_err_code);
257 break;
258 case SEC_HW_ERR_SSRC_JMP_HALT_U:
259 INFO("Jump Halt User Status Source");
260 hw_handle_jmp_halt_user_err(hw_err_code);
261 break;
262 case SEC_HW_ERR_SSRC_DECO:
263 INFO("DECO Status Source");
264 hw_handle_deco_err(hw_err_code);
265 break;
266 case SEC_HW_ERR_SSRC_JR:
267 INFO("Job Ring Status Source");
268 hw_handle_jr_err(hw_err_code);
269 break;
270 case SEC_HW_ERR_SSRC_JMP_HALT_COND:
271 INFO("Jump Halt Condition Codes");
272 hw_handle_jmp_halt_cond_err(hw_err_code);
273 break;
274 default:
275 INFO("Unknown SSRC");
276 break;
277 }
278 }
279
hw_job_ring_error(sec_job_ring_t * job_ring)280 int hw_job_ring_error(sec_job_ring_t *job_ring)
281 {
282 uint32_t jrint_error_code;
283 struct jobring_regs *regs =
284 (struct jobring_regs *)job_ring->register_base_addr;
285
286 if (JR_REG_JRINT_JRE_EXTRACT(sec_in32(®s->jrint)) == 0) {
287 return 0;
288 }
289
290 jrint_error_code =
291 JR_REG_JRINT_ERR_TYPE_EXTRACT(sec_in32(®s->jrint));
292 switch (jrint_error_code) {
293 case JRINT_ERR_WRITE_STATUS:
294 ERROR("Error writing status to Output Ring ");
295 break;
296 case JRINT_ERR_BAD_INPUT_BASE:
297 ERROR("Bad Input Ring Base (not on a 4-byte boundary)\n");
298 break;
299 case JRINT_ERR_BAD_OUTPUT_BASE:
300 ERROR("Bad Output Ring Base (not on a 4-byte boundary)\n");
301 break;
302 case JRINT_ERR_WRITE_2_IRBA:
303 ERROR("Invalid write to Input Ring Base Address Register\n");
304 break;
305 case JRINT_ERR_WRITE_2_ORBA:
306 ERROR("Invalid write to Output Ring Base Address Register\n");
307 break;
308 case JRINT_ERR_RES_B4_HALT:
309 ERROR("Job Ring released before Job Ring is halted\n");
310 break;
311 case JRINT_ERR_REM_TOO_MANY:
312 ERROR("Removed too many jobs from job ring\n");
313 break;
314 case JRINT_ERR_ADD_TOO_MANY:
315 ERROR("Added too many jobs on job ring\n");
316 break;
317 default:
318 ERROR("Unknown SEC JR Error :%d\n", jrint_error_code);
319 break;
320 }
321 return jrint_error_code;
322 }
323
hw_job_ring_set_coalescing_param(sec_job_ring_t * job_ring,uint16_t irq_coalescing_timer,uint8_t irq_coalescing_count)324 int hw_job_ring_set_coalescing_param(sec_job_ring_t *job_ring,
325 uint16_t irq_coalescing_timer,
326 uint8_t irq_coalescing_count)
327 {
328 uint32_t reg_val = 0U;
329 struct jobring_regs *regs =
330 (struct jobring_regs *)job_ring->register_base_addr;
331
332 /* Set descriptor count coalescing */
333 reg_val |= (irq_coalescing_count << JR_REG_JRCFG_LO_ICDCT_SHIFT);
334
335 /* Set coalescing timer value */
336 reg_val |= (irq_coalescing_timer << JR_REG_JRCFG_LO_ICTT_SHIFT);
337
338 /* Update parameters in HW */
339 sec_out32(®s->jrcfg1, reg_val);
340
341 VERBOSE("Set coalescing params on jr\n");
342
343 return 0;
344 }
345
hw_job_ring_enable_coalescing(sec_job_ring_t * job_ring)346 int hw_job_ring_enable_coalescing(sec_job_ring_t *job_ring)
347 {
348 uint32_t reg_val = 0U;
349 struct jobring_regs *regs =
350 (struct jobring_regs *)job_ring->register_base_addr;
351
352 /* Get the current value of the register */
353 reg_val = sec_in32(®s->jrcfg1);
354
355 /* Enable coalescing */
356 reg_val |= JR_REG_JRCFG_LO_ICEN_EN;
357
358 /* Write in hw */
359 sec_out32(®s->jrcfg1, reg_val);
360
361 VERBOSE("Enabled coalescing on jr\n");
362
363 return 0;
364 }
365
hw_job_ring_disable_coalescing(sec_job_ring_t * job_ring)366 int hw_job_ring_disable_coalescing(sec_job_ring_t *job_ring)
367 {
368 uint32_t reg_val = 0U;
369 struct jobring_regs *regs =
370 (struct jobring_regs *)job_ring->register_base_addr;
371
372 /* Get the current value of the register */
373 reg_val = sec_in32(®s->jrcfg1);
374
375 /* Disable coalescing */
376 reg_val &= ~JR_REG_JRCFG_LO_ICEN_EN;
377
378 /* Write in hw */
379 sec_out32(®s->jrcfg1, reg_val);
380
381 VERBOSE("Disabled coalescing on jr");
382
383 return 0;
384
385 }
386
hw_flush_job_ring(struct sec_job_ring_t * job_ring,uint32_t do_notify,uint32_t error_code,uint32_t * notified_descs)387 void hw_flush_job_ring(struct sec_job_ring_t *job_ring,
388 uint32_t do_notify,
389 uint32_t error_code, uint32_t *notified_descs)
390 {
391 int32_t jobs_no_to_discard = 0;
392 int32_t discarded_descs_no = 0;
393 int32_t number_of_jobs_available = 0;
394
395 VERBOSE("JR pi[%d]i ci[%d]\n", job_ring->pidx, job_ring->cidx);
396 VERBOSE("error code %x\n", error_code);
397 VERBOSE("Notify_desc = %d\n", do_notify);
398
399 number_of_jobs_available = hw_get_no_finished_jobs(job_ring);
400
401 /* Discard all jobs */
402 jobs_no_to_discard = number_of_jobs_available;
403
404 VERBOSE("JR pi[%d]i ci[%d]\n", job_ring->pidx, job_ring->cidx);
405 VERBOSE("Discarding desc = %d\n", jobs_no_to_discard);
406
407 while (jobs_no_to_discard > discarded_descs_no) {
408 discarded_descs_no++;
409 /* Now increment the consumer index for the current job ring,
410 * AFTER saving job in temporary location!
411 * Increment the consumer index for the current job ring
412 */
413
414 job_ring->cidx = SEC_CIRCULAR_COUNTER(job_ring->cidx,
415 SEC_JOB_RING_SIZE);
416
417 hw_remove_entries(job_ring, 1);
418 }
419
420 if (do_notify == true) {
421 if (notified_descs == NULL) {
422 return;
423 }
424 *notified_descs = discarded_descs_no;
425 }
426 }
427
428 /* return >0 in case of success
429 * -1 in case of error from SEC block
430 * 0 in case job not yet processed by SEC
431 * or Descriptor returned is NULL after dequeue
432 */
hw_poll_job_ring(struct sec_job_ring_t * job_ring,int32_t limit)433 int hw_poll_job_ring(struct sec_job_ring_t *job_ring, int32_t limit)
434 {
435 int32_t jobs_no_to_notify = 0;
436 int32_t number_of_jobs_available = 0;
437 int32_t notified_descs_no = 0;
438 uint32_t error_descs_no = 0U;
439 uint32_t sec_error_code = 0U;
440 uint32_t do_driver_shutdown = false;
441 phys_addr_t *fnptr, *arg_addr;
442 user_callback usercall = NULL;
443 uint8_t *current_desc;
444 void *arg;
445 uintptr_t current_desc_addr;
446 phys_addr_t current_desc_loc;
447
448 #if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2)
449 inv_dcache_range((uintptr_t)job_ring->register_base_addr, sizeof(struct jobring_regs));
450 dmbsy();
451 #endif
452
453 /* check here if any JR error that cannot be written
454 * in the output status word has occurred
455 */
456 sec_error_code = hw_job_ring_error(job_ring);
457 if (unlikely(sec_error_code) != 0) {
458 ERROR("Error here itself %x\n", sec_error_code);
459 return -1;
460 }
461 /* Compute the number of notifications that need to be raised to UA
462 * If limit < 0 -> notify all done jobs
463 * If limit > total number of done jobs -> notify all done jobs
464 * If limit = 0 -> error
465 * If limit > 0 && limit < total number of done jobs -> notify a number
466 * of done jobs equal with limit
467 */
468
469 /*compute the number of jobs available in the job ring based on the
470 * producer and consumer index values.
471 */
472
473 number_of_jobs_available = hw_get_no_finished_jobs(job_ring);
474 jobs_no_to_notify = (limit < 0 || limit > number_of_jobs_available) ?
475 number_of_jobs_available : limit;
476 VERBOSE("JR - pi %d, ci %d, ", job_ring->pidx, job_ring->cidx);
477 VERBOSE("Jobs submitted %d", number_of_jobs_available);
478 VERBOSE("Jobs to notify %d\n", jobs_no_to_notify);
479
480 while (jobs_no_to_notify > notified_descs_no) {
481
482 #if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2)
483 inv_dcache_range(
484 (uintptr_t)(&job_ring->output_ring[job_ring->cidx]),
485 sizeof(struct sec_outring_entry));
486 dmbsy();
487 #endif
488
489 /* Get job status here */
490 sec_error_code =
491 sec_in32(&(job_ring->output_ring[job_ring->cidx].status));
492
493 /* Get completed descriptor
494 */
495 current_desc_loc = (uintptr_t)
496 &job_ring->output_ring[job_ring->cidx].desc;
497 current_desc_addr = sec_read_addr(current_desc_loc);
498
499 current_desc = ptov((phys_addr_t *) current_desc_addr);
500 if (current_desc == 0) {
501 ERROR("No descriptor returned from SEC");
502 assert(current_desc);
503 return 0;
504 }
505 /* now increment the consumer index for the current job ring,
506 * AFTER saving job in temporary location!
507 */
508 job_ring->cidx = SEC_CIRCULAR_COUNTER(job_ring->cidx,
509 SEC_JOB_RING_SIZE);
510
511 if (sec_error_code != 0) {
512 ERROR("desc at cidx %d\n ", job_ring->cidx);
513 ERROR("generated error %x\n", sec_error_code);
514
515 sec_handle_desc_error(job_ring,
516 sec_error_code,
517 &error_descs_no,
518 &do_driver_shutdown);
519 hw_remove_entries(job_ring, 1);
520
521 return -1;
522 }
523 /* Signal that the job has been processed & the slot is free */
524 hw_remove_entries(job_ring, 1);
525 notified_descs_no++;
526
527 arg_addr = (phys_addr_t *) (current_desc +
528 (MAX_DESC_SIZE_WORDS * sizeof(uint32_t)));
529
530 fnptr = (phys_addr_t *) (current_desc +
531 (MAX_DESC_SIZE_WORDS * sizeof(uint32_t)
532 + sizeof(void *)));
533
534 arg = (void *)*(arg_addr);
535 if (*fnptr != 0) {
536 VERBOSE("Callback Function called\n");
537 usercall = (user_callback) *(fnptr);
538 (*usercall) ((uint32_t *) current_desc,
539 sec_error_code, arg, job_ring);
540 }
541 }
542
543 return notified_descs_no;
544 }
545
sec_handle_desc_error(sec_job_ring_t * job_ring,uint32_t sec_error_code,uint32_t * notified_descs,uint32_t * do_driver_shutdown)546 void sec_handle_desc_error(sec_job_ring_t *job_ring,
547 uint32_t sec_error_code,
548 uint32_t *notified_descs,
549 uint32_t *do_driver_shutdown)
550 {
551 /* Analyze the SEC error on this job ring */
552 hw_handle_job_ring_error(job_ring, sec_error_code);
553 }
554
flush_job_rings(void)555 void flush_job_rings(void)
556 {
557 struct sec_job_ring_t *job_ring = NULL;
558 int i = 0;
559
560 for (i = 0; i < g_job_rings_no; i++) {
561 job_ring = &g_job_rings[i];
562 /* Producer index is frozen. If consumer index is not equal
563 * with producer index, then we have descs to flush.
564 */
565 while (job_ring->pidx != job_ring->cidx) {
566 hw_flush_job_ring(job_ring, false, 0, /* no error */
567 NULL);
568 }
569 }
570 }
571
shutdown_job_ring(struct sec_job_ring_t * job_ring)572 int shutdown_job_ring(struct sec_job_ring_t *job_ring)
573 {
574 int ret = 0;
575
576 ret = hw_shutdown_job_ring(job_ring);
577 if (ret != 0) {
578 ERROR("Failed to shutdown hardware job ring\n");
579 return ret;
580 }
581
582 if (job_ring->coalescing_en != 0) {
583 hw_job_ring_disable_coalescing(job_ring);
584 }
585
586 if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) {
587 ret = jr_disable_irqs(job_ring);
588 if (ret != 0) {
589 ERROR("Failed to disable irqs for job ring");
590 return ret;
591 }
592 }
593
594 return 0;
595 }
596
jr_enable_irqs(struct sec_job_ring_t * job_ring)597 int jr_enable_irqs(struct sec_job_ring_t *job_ring)
598 {
599 uint32_t reg_val = 0U;
600 struct jobring_regs *regs =
601 (struct jobring_regs *)job_ring->register_base_addr;
602
603 /* Get the current value of the register */
604 reg_val = sec_in32(®s->jrcfg1);
605
606 /* Enable interrupts by disabling interrupt masking*/
607 reg_val &= ~JR_REG_JRCFG_LO_IMSK_EN;
608
609 /* Update parameters in HW */
610 sec_out32(®s->jrcfg1, reg_val);
611
612 VERBOSE("Enable interrupts on JR\n");
613
614 return 0;
615 }
616
jr_disable_irqs(struct sec_job_ring_t * job_ring)617 int jr_disable_irqs(struct sec_job_ring_t *job_ring)
618 {
619 uint32_t reg_val = 0U;
620 struct jobring_regs *regs =
621 (struct jobring_regs *)job_ring->register_base_addr;
622
623 /* Get the current value of the register */
624 reg_val = sec_in32(®s->jrcfg1);
625
626 /* Disable interrupts by enabling interrupt masking*/
627 reg_val |= JR_REG_JRCFG_LO_IMSK_EN;
628
629 /* Update parameters in HW */
630 sec_out32(®s->jrcfg1, reg_val);
631
632 VERBOSE("Disable interrupts on JR\n");
633
634 return 0;
635 }
636