ckrm_E16
[linux-2.6.git] / arch / sparc64 / kernel / ptrace.c
1 /* ptrace.c: Sparc process tracing support.
2  *
3  * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu)
4  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
5  *
6  * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
7  * and David Mosberger.
8  *
9  * Added Linux support -miguel (weird, eh?, the original code was meant
10  * to emulate SunOS).
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/sched.h>
15 #include <linux/mm.h>
16 #include <linux/errno.h>
17 #include <linux/ptrace.h>
18 #include <linux/user.h>
19 #include <linux/smp.h>
20 #include <linux/smp_lock.h>
21 #include <linux/security.h>
22
23 #include <asm/asi.h>
24 #include <asm/pgtable.h>
25 #include <asm/system.h>
26 #include <asm/uaccess.h>
27 #include <asm/psrcompat.h>
28 #include <asm/visasm.h>
29 #include <asm/spitfire.h>
30
31 /* Returning from ptrace is a bit tricky because the syscall return
32  * low level code assumes any value returned which is negative and
33  * is a valid errno will mean setting the condition codes to indicate
34  * an error return.  This doesn't work, so we have this hook.
35  */
36 static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
37 {
38         regs->u_regs[UREG_I0] = error;
39         regs->tstate |= (TSTATE_ICARRY | TSTATE_XCARRY);
40         regs->tpc = regs->tnpc;
41         regs->tnpc += 4;
42 }
43
44 static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
45 {
46         regs->u_regs[UREG_I0] = value;
47         regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
48         regs->tpc = regs->tnpc;
49         regs->tnpc += 4;
50 }
51
52 static inline void
53 pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr)
54 {
55         if (test_thread_flag(TIF_32BIT)) {
56                 if (put_user(value, (unsigned int __user *) addr)) {
57                         pt_error_return(regs, EFAULT);
58                         return;
59                 }
60         } else {
61                 if (put_user(value, (long __user *) addr)) {
62                         pt_error_return(regs, EFAULT);
63                         return;
64                 }
65         }
66         regs->u_regs[UREG_I0] = 0;
67         regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
68         regs->tpc = regs->tnpc;
69         regs->tnpc += 4;
70 }
71
72 static void
73 pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr)
74 {
75         if (current->personality == PER_SUNOS)
76                 pt_succ_return (regs, val);
77         else
78                 pt_succ_return_linux (regs, val, addr);
79 }
80
81 /* #define ALLOW_INIT_TRACING */
82 /* #define DEBUG_PTRACE */
83
84 #ifdef DEBUG_PTRACE
85 char *pt_rq [] = {
86         /* 0  */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
87         /* 4  */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
88         /* 8  */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
89         /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
90         /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
91         /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
92         /* 24 */ "SYSCALL", ""
93 };
94 #endif
95
96 /*
97  * Called by kernel/ptrace.c when detaching..
98  *
99  * Make sure single step bits etc are not set.
100  */
101 void ptrace_disable(struct task_struct *child)
102 {
103         /* nothing to do */
104 }
105
106 asmlinkage void do_ptrace(struct pt_regs *regs)
107 {
108         int request = regs->u_regs[UREG_I0];
109         pid_t pid = regs->u_regs[UREG_I1];
110         unsigned long addr = regs->u_regs[UREG_I2];
111         unsigned long data = regs->u_regs[UREG_I3];
112         unsigned long addr2 = regs->u_regs[UREG_I4];
113         struct task_struct *child;
114         int ret;
115
116         if (test_thread_flag(TIF_32BIT)) {
117                 addr &= 0xffffffffUL;
118                 data &= 0xffffffffUL;
119                 addr2 &= 0xffffffffUL;
120         }
121         lock_kernel();
122 #ifdef DEBUG_PTRACE
123         {
124                 char *s;
125
126                 if ((request >= 0) && (request <= 24))
127                         s = pt_rq [request];
128                 else
129                         s = "unknown";
130
131                 if (request == PTRACE_POKEDATA && data == 0x91d02001){
132                         printk ("do_ptrace: breakpoint pid=%d, addr=%016lx addr2=%016lx\n",
133                                 pid, addr, addr2);
134                 } else 
135                         printk("do_ptrace: rq=%s(%d) pid=%d addr=%016lx data=%016lx addr2=%016lx\n",
136                                s, request, pid, addr, data, addr2);
137         }
138 #endif
139         if (request == PTRACE_TRACEME) {
140                 int ret;
141
142                 /* are we already being traced? */
143                 if (current->ptrace & PT_PTRACED) {
144                         pt_error_return(regs, EPERM);
145                         goto out;
146                 }
147                 ret = security_ptrace(current->parent, current);
148                 if (ret) {
149                         pt_error_return(regs, -ret);
150                         goto out;
151                 }
152
153                 /* set the ptrace bit in the process flags. */
154                 current->ptrace |= PT_PTRACED;
155                 pt_succ_return(regs, 0);
156                 goto out;
157         }
158 #ifndef ALLOW_INIT_TRACING
159         if (pid == 1) {
160                 /* Can't dork with init. */
161                 pt_error_return(regs, EPERM);
162                 goto out;
163         }
164 #endif
165         read_lock(&tasklist_lock);
166         child = find_task_by_pid(pid);
167         if (child)
168                 get_task_struct(child);
169         read_unlock(&tasklist_lock);
170
171         if (!child) {
172                 pt_error_return(regs, ESRCH);
173                 goto out;
174         }
175
176         if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
177             || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
178                 if (ptrace_attach(child)) {
179                         pt_error_return(regs, EPERM);
180                         goto out_tsk;
181                 }
182                 pt_succ_return(regs, 0);
183                 goto out_tsk;
184         }
185
186         ret = ptrace_check_attach(child, request == PTRACE_KILL);
187         if (ret < 0) {
188                 pt_error_return(regs, -ret);
189                 goto out_tsk;
190         }
191
192         if (!(test_thread_flag(TIF_32BIT))      &&
193             ((request == PTRACE_READDATA64)             ||
194              (request == PTRACE_WRITEDATA64)            ||
195              (request == PTRACE_READTEXT64)             ||
196              (request == PTRACE_WRITETEXT64)            ||
197              (request == PTRACE_PEEKTEXT64)             ||
198              (request == PTRACE_POKETEXT64)             ||
199              (request == PTRACE_PEEKDATA64)             ||
200              (request == PTRACE_POKEDATA64))) {
201                 addr = regs->u_regs[UREG_G2];
202                 addr2 = regs->u_regs[UREG_G3];
203                 request -= 30; /* wheee... */
204         }
205
206         switch(request) {
207         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
208         case PTRACE_PEEKDATA: {
209                 unsigned long tmp64;
210                 unsigned int tmp32;
211                 int res, copied;
212
213                 res = -EIO;
214                 if (test_thread_flag(TIF_32BIT)) {
215                         copied = access_process_vm(child, addr,
216                                                    &tmp32, sizeof(tmp32), 0);
217                         tmp64 = (unsigned long) tmp32;
218                         if (copied == sizeof(tmp32))
219                                 res = 0;
220                 } else {
221                         copied = access_process_vm(child, addr,
222                                                    &tmp64, sizeof(tmp64), 0);
223                         if (copied == sizeof(tmp64))
224                                 res = 0;
225                 }
226                 if (res < 0)
227                         pt_error_return(regs, -res);
228                 else
229                         pt_os_succ_return(regs, tmp64, (long *) data);
230                 goto flush_and_out;
231         }
232
233         case PTRACE_POKETEXT: /* write the word at location addr. */
234         case PTRACE_POKEDATA: {
235                 unsigned long tmp64;
236                 unsigned int tmp32;
237                 int copied, res = -EIO;
238
239                 if (test_thread_flag(TIF_32BIT)) {
240                         tmp32 = data;
241                         copied = access_process_vm(child, addr,
242                                                    &tmp32, sizeof(tmp32), 1);
243                         if (copied == sizeof(tmp32))
244                                 res = 0;
245                 } else {
246                         tmp64 = data;
247                         copied = access_process_vm(child, addr,
248                                                    &tmp64, sizeof(tmp64), 1);
249                         if (copied == sizeof(tmp64))
250                                 res = 0;
251                 }
252                 if (res < 0)
253                         pt_error_return(regs, -res);
254                 else
255                         pt_succ_return(regs, res);
256                 goto flush_and_out;
257         }
258
259         case PTRACE_GETREGS: {
260                 struct pt_regs32 __user *pregs =
261                         (struct pt_regs32 __user *) addr;
262                 struct pt_regs *cregs = child->thread_info->kregs;
263                 int rval;
264
265                 if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) ||
266                     __put_user(cregs->tpc, (&pregs->pc)) ||
267                     __put_user(cregs->tnpc, (&pregs->npc)) ||
268                     __put_user(cregs->y, (&pregs->y))) {
269                         pt_error_return(regs, EFAULT);
270                         goto out_tsk;
271                 }
272                 for (rval = 1; rval < 16; rval++)
273                         if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
274                                 pt_error_return(regs, EFAULT);
275                                 goto out_tsk;
276                         }
277                 pt_succ_return(regs, 0);
278 #ifdef DEBUG_PTRACE
279                 printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
280 #endif
281                 goto out_tsk;
282         }
283
284         case PTRACE_GETREGS64: {
285                 struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
286                 struct pt_regs *cregs = child->thread_info->kregs;
287                 unsigned long tpc = cregs->tpc;
288                 int rval;
289
290                 if ((child->thread_info->flags & _TIF_32BIT) != 0)
291                         tpc &= 0xffffffff;
292                 if (__put_user(cregs->tstate, (&pregs->tstate)) ||
293                     __put_user(tpc, (&pregs->tpc)) ||
294                     __put_user(cregs->tnpc, (&pregs->tnpc)) ||
295                     __put_user(cregs->y, (&pregs->y))) {
296                         pt_error_return(regs, EFAULT);
297                         goto out_tsk;
298                 }
299                 for (rval = 1; rval < 16; rval++)
300                         if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
301                                 pt_error_return(regs, EFAULT);
302                                 goto out_tsk;
303                         }
304                 pt_succ_return(regs, 0);
305 #ifdef DEBUG_PTRACE
306                 printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
307 #endif
308                 goto out_tsk;
309         }
310
311         case PTRACE_SETREGS: {
312                 struct pt_regs32 __user *pregs =
313                         (struct pt_regs32 __user *) addr;
314                 struct pt_regs *cregs = child->thread_info->kregs;
315                 unsigned int psr, pc, npc, y;
316                 int i;
317
318                 /* Must be careful, tracing process can only set certain
319                  * bits in the psr.
320                  */
321                 if (__get_user(psr, (&pregs->psr)) ||
322                     __get_user(pc, (&pregs->pc)) ||
323                     __get_user(npc, (&pregs->npc)) ||
324                     __get_user(y, (&pregs->y))) {
325                         pt_error_return(regs, EFAULT);
326                         goto out_tsk;
327                 }
328                 cregs->tstate &= ~(TSTATE_ICC);
329                 cregs->tstate |= psr_to_tstate_icc(psr);
330                 if (!((pc | npc) & 3)) {
331                         cregs->tpc = pc;
332                         cregs->tnpc = npc;
333                 }
334                 cregs->y = y;
335                 for (i = 1; i < 16; i++) {
336                         if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
337                                 pt_error_return(regs, EFAULT);
338                                 goto out_tsk;
339                         }
340                 }
341                 pt_succ_return(regs, 0);
342                 goto out_tsk;
343         }
344
345         case PTRACE_SETREGS64: {
346                 struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
347                 struct pt_regs *cregs = child->thread_info->kregs;
348                 unsigned long tstate, tpc, tnpc, y;
349                 int i;
350
351                 /* Must be careful, tracing process can only set certain
352                  * bits in the psr.
353                  */
354                 if (__get_user(tstate, (&pregs->tstate)) ||
355                     __get_user(tpc, (&pregs->tpc)) ||
356                     __get_user(tnpc, (&pregs->tnpc)) ||
357                     __get_user(y, (&pregs->y))) {
358                         pt_error_return(regs, EFAULT);
359                         goto out_tsk;
360                 }
361                 if ((child->thread_info->flags & _TIF_32BIT) != 0) {
362                         tpc &= 0xffffffff;
363                         tnpc &= 0xffffffff;
364                 }
365                 tstate &= (TSTATE_ICC | TSTATE_XCC);
366                 cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
367                 cregs->tstate |= tstate;
368                 if (!((tpc | tnpc) & 3)) {
369                         cregs->tpc = tpc;
370                         cregs->tnpc = tnpc;
371                 }
372                 cregs->y = y;
373                 for (i = 1; i < 16; i++) {
374                         if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
375                                 pt_error_return(regs, EFAULT);
376                                 goto out_tsk;
377                         }
378                 }
379                 pt_succ_return(regs, 0);
380                 goto out_tsk;
381         }
382
383         case PTRACE_GETFPREGS: {
384                 struct fps {
385                         unsigned int regs[32];
386                         unsigned int fsr;
387                         unsigned int flags;
388                         unsigned int extra;
389                         unsigned int fpqd;
390                         struct fq {
391                                 unsigned int insnaddr;
392                                 unsigned int insn;
393                         } fpq[16];
394                 };
395                 struct fps __user *fps = (struct fps __user *) addr;
396                 unsigned long *fpregs = child->thread_info->fpregs;
397
398                 if (copy_to_user(&fps->regs[0], fpregs,
399                                  (32 * sizeof(unsigned int))) ||
400                     __put_user(child->thread_info->xfsr[0], (&fps->fsr)) ||
401                     __put_user(0, (&fps->fpqd)) ||
402                     __put_user(0, (&fps->flags)) ||
403                     __put_user(0, (&fps->extra)) ||
404                     clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) {
405                         pt_error_return(regs, EFAULT);
406                         goto out_tsk;
407                 }
408                 pt_succ_return(regs, 0);
409                 goto out_tsk;
410         }
411
412         case PTRACE_GETFPREGS64: {
413                 struct fps {
414                         unsigned int regs[64];
415                         unsigned long fsr;
416                 };
417                 struct fps __user *fps = (struct fps __user *) addr;
418                 unsigned long *fpregs = child->thread_info->fpregs;
419
420                 if (copy_to_user(&fps->regs[0], fpregs,
421                                  (64 * sizeof(unsigned int))) ||
422                     __put_user(child->thread_info->xfsr[0], (&fps->fsr))) {
423                         pt_error_return(regs, EFAULT);
424                         goto out_tsk;
425                 }
426                 pt_succ_return(regs, 0);
427                 goto out_tsk;
428         }
429
430         case PTRACE_SETFPREGS: {
431                 struct fps {
432                         unsigned int regs[32];
433                         unsigned int fsr;
434                         unsigned int flags;
435                         unsigned int extra;
436                         unsigned int fpqd;
437                         struct fq {
438                                 unsigned int insnaddr;
439                                 unsigned int insn;
440                         } fpq[16];
441                 };
442                 struct fps __user *fps = (struct fps __user *) addr;
443                 unsigned long *fpregs = child->thread_info->fpregs;
444                 unsigned fsr;
445
446                 if (copy_from_user(fpregs, &fps->regs[0],
447                                    (32 * sizeof(unsigned int))) ||
448                     __get_user(fsr, (&fps->fsr))) {
449                         pt_error_return(regs, EFAULT);
450                         goto out_tsk;
451                 }
452                 child->thread_info->xfsr[0] &= 0xffffffff00000000UL;
453                 child->thread_info->xfsr[0] |= fsr;
454                 if (!(child->thread_info->fpsaved[0] & FPRS_FEF))
455                         child->thread_info->gsr[0] = 0;
456                 child->thread_info->fpsaved[0] |= (FPRS_FEF | FPRS_DL);
457                 pt_succ_return(regs, 0);
458                 goto out_tsk;
459         }
460
461         case PTRACE_SETFPREGS64: {
462                 struct fps {
463                         unsigned int regs[64];
464                         unsigned long fsr;
465                 };
466                 struct fps __user *fps = (struct fps __user *) addr;
467                 unsigned long *fpregs = child->thread_info->fpregs;
468
469                 if (copy_from_user(fpregs, &fps->regs[0],
470                                    (64 * sizeof(unsigned int))) ||
471                     __get_user(child->thread_info->xfsr[0], (&fps->fsr))) {
472                         pt_error_return(regs, EFAULT);
473                         goto out_tsk;
474                 }
475                 if (!(child->thread_info->fpsaved[0] & FPRS_FEF))
476                         child->thread_info->gsr[0] = 0;
477                 child->thread_info->fpsaved[0] |= (FPRS_FEF | FPRS_DL | FPRS_DU);
478                 pt_succ_return(regs, 0);
479                 goto out_tsk;
480         }
481
482         case PTRACE_READTEXT:
483         case PTRACE_READDATA: {
484                 int res = ptrace_readdata(child, addr,
485                                           (char __user *)addr2, data);
486                 if (res == data) {
487                         pt_succ_return(regs, 0);
488                         goto flush_and_out;
489                 }
490                 if (res >= 0)
491                         res = -EIO;
492                 pt_error_return(regs, -res);
493                 goto flush_and_out;
494         }
495
496         case PTRACE_WRITETEXT:
497         case PTRACE_WRITEDATA: {
498                 int res = ptrace_writedata(child, (char __user *) addr2,
499                                            addr, data);
500                 if (res == data) {
501                         pt_succ_return(regs, 0);
502                         goto flush_and_out;
503                 }
504                 if (res >= 0)
505                         res = -EIO;
506                 pt_error_return(regs, -res);
507                 goto flush_and_out;
508         }
509         case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
510                 addr = 1;
511
512         case PTRACE_CONT: { /* restart after signal. */
513                 if (data > _NSIG) {
514                         pt_error_return(regs, EIO);
515                         goto out_tsk;
516                 }
517                 if (addr != 1) {
518                         unsigned long pc_mask = ~0UL;
519
520                         if ((child->thread_info->flags & _TIF_32BIT) != 0)
521                                 pc_mask = 0xffffffff;
522
523                         if (addr & 3) {
524                                 pt_error_return(regs, EINVAL);
525                                 goto out_tsk;
526                         }
527 #ifdef DEBUG_PTRACE
528                         printk ("Original: %016lx %016lx\n",
529                                 child->thread_info->kregs->tpc,
530                                 child->thread_info->kregs->tnpc);
531                         printk ("Continuing with %016lx %016lx\n", addr, addr+4);
532 #endif
533                         child->thread_info->kregs->tpc = (addr & pc_mask);
534                         child->thread_info->kregs->tnpc = ((addr + 4) & pc_mask);
535                 }
536
537                 if (request == PTRACE_SYSCALL) {
538                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
539                 } else {
540                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
541                 }
542
543                 child->exit_code = data;
544 #ifdef DEBUG_PTRACE
545                 printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child->comm,
546                         child->pid, child->exit_code,
547                         child->thread_info->kregs->tpc,
548                         child->thread_info->kregs->tnpc);
549                        
550 #endif
551                 wake_up_process(child);
552                 pt_succ_return(regs, 0);
553                 goto out_tsk;
554         }
555
556 /*
557  * make the child exit.  Best I can do is send it a sigkill. 
558  * perhaps it should be put in the status that it wants to 
559  * exit.
560  */
561         case PTRACE_KILL: {
562                 if (child->state == TASK_ZOMBIE) {      /* already dead */
563                         pt_succ_return(regs, 0);
564                         goto out_tsk;
565                 }
566                 child->exit_code = SIGKILL;
567                 wake_up_process(child);
568                 pt_succ_return(regs, 0);
569                 goto out_tsk;
570         }
571
572         case PTRACE_SUNDETACH: { /* detach a process that was attached. */
573                 int error = ptrace_detach(child, data);
574                 if (error) {
575                         pt_error_return(regs, EIO);
576                         goto out_tsk;
577                 }
578                 pt_succ_return(regs, 0);
579                 goto out_tsk;
580         }
581
582         /* PTRACE_DUMPCORE unsupported... */
583
584         default: {
585                 int err = ptrace_request(child, request, addr, data);
586                 if (err)
587                         pt_error_return(regs, -err);
588                 else
589                         pt_succ_return(regs, 0);
590                 goto out_tsk;
591         }
592         }
593 flush_and_out:
594         {
595                 unsigned long va;
596
597                 if (tlb_type == cheetah || tlb_type == cheetah_plus) {
598                         for (va = 0; va < (1 << 16); va += (1 << 5))
599                                 spitfire_put_dcache_tag(va, 0x0);
600                         /* No need to mess with I-cache on Cheetah. */
601                 } else {
602                         for (va =  0; va < L1DCACHE_SIZE; va += 32)
603                                 spitfire_put_dcache_tag(va, 0x0);
604                         if (request == PTRACE_PEEKTEXT ||
605                             request == PTRACE_POKETEXT ||
606                             request == PTRACE_READTEXT ||
607                             request == PTRACE_WRITETEXT) {
608                                 for (va =  0; va < (PAGE_SIZE << 1); va += 32)
609                                         spitfire_put_icache_tag(va, 0x0);
610                                 __asm__ __volatile__("flush %g6");
611                         }
612                 }
613         }
614 out_tsk:
615         if (child)
616                 put_task_struct(child);
617 out:
618         unlock_kernel();
619 }
620
621 asmlinkage void syscall_trace(void)
622 {
623 #ifdef DEBUG_PTRACE
624         printk("%s [%d]: syscall_trace\n", current->comm, current->pid);
625 #endif
626         if (!test_thread_flag(TIF_SYSCALL_TRACE))
627                 return;
628         if (!(current->ptrace & PT_PTRACED))
629                 return;
630         current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
631                                         ? 0x80 : 0);
632         current->state = TASK_STOPPED;
633         notify_parent(current, SIGCHLD);
634         schedule();
635
636         /*
637          * this isn't the same as continuing with a signal, but it will do
638          * for normal use.  strace only continues with a signal if the
639          * stopping signal is not SIGTRAP.  -brl
640          */
641 #ifdef DEBUG_PTRACE
642         printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
643                 current->pid, current->exit_code);
644 #endif
645         if (current->exit_code) {
646                 send_sig (current->exit_code, current, 1);
647                 current->exit_code = 0;
648         }
649 }