]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm64/arm64/debug_monitor.c
MFV r361280:
[FreeBSD/FreeBSD.git] / sys / arm64 / arm64 / debug_monitor.c
1 /*-
2  * Copyright (c) 2014 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Semihalf under
6  * the sponsorship of the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include "opt_ddb.h"
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/kdb.h>
38 #include <sys/pcpu.h>
39 #include <sys/proc.h>
40 #include <sys/systm.h>
41 #include <sys/sysent.h>
42
43 #include <machine/armreg.h>
44 #include <machine/cpu.h>
45 #include <machine/debug_monitor.h>
46 #include <machine/kdb.h>
47
48 #ifdef DDB
49 #include <ddb/ddb.h>
50 #include <ddb/db_sym.h>
51 #endif
52
53 enum dbg_t {
54         DBG_TYPE_BREAKPOINT = 0,
55         DBG_TYPE_WATCHPOINT = 1,
56 };
57
58 static int dbg_watchpoint_num;
59 static int dbg_breakpoint_num;
60 static struct debug_monitor_state kernel_monitor = {
61         .dbg_flags = DBGMON_KERNEL
62 };
63
64 /* Called from the exception handlers */
65 void dbg_monitor_enter(struct thread *);
66 void dbg_monitor_exit(struct thread *, struct trapframe *);
67
68 /* Watchpoints/breakpoints control register bitfields */
69 #define DBG_WATCH_CTRL_LEN_1            (0x1 << 5)
70 #define DBG_WATCH_CTRL_LEN_2            (0x3 << 5)
71 #define DBG_WATCH_CTRL_LEN_4            (0xf << 5)
72 #define DBG_WATCH_CTRL_LEN_8            (0xff << 5)
73 #define DBG_WATCH_CTRL_LEN_MASK(x)      ((x) & (0xff << 5))
74 #define DBG_WATCH_CTRL_EXEC             (0x0 << 3)
75 #define DBG_WATCH_CTRL_LOAD             (0x1 << 3)
76 #define DBG_WATCH_CTRL_STORE            (0x2 << 3)
77 #define DBG_WATCH_CTRL_ACCESS_MASK(x)   ((x) & (0x3 << 3))
78
79 /* Common for breakpoint and watchpoint */
80 #define DBG_WB_CTRL_EL1         (0x1 << 1)
81 #define DBG_WB_CTRL_EL0         (0x2 << 1)
82 #define DBG_WB_CTRL_ELX_MASK(x) ((x) & (0x3 << 1))
83 #define DBG_WB_CTRL_E           (0x1 << 0)
84
85 #define DBG_REG_BASE_BVR        0
86 #define DBG_REG_BASE_BCR        (DBG_REG_BASE_BVR + 16)
87 #define DBG_REG_BASE_WVR        (DBG_REG_BASE_BCR + 16)
88 #define DBG_REG_BASE_WCR        (DBG_REG_BASE_WVR + 16)
89
90 /* Watchpoint/breakpoint helpers */
91 #define DBG_WB_WVR      "wvr"
92 #define DBG_WB_WCR      "wcr"
93 #define DBG_WB_BVR      "bvr"
94 #define DBG_WB_BCR      "bcr"
95
96 #define DBG_WB_READ(reg, num, val) do {                                 \
97         __asm __volatile("mrs %0, dbg" reg #num "_el1" : "=r" (val));   \
98 } while (0)
99
100 #define DBG_WB_WRITE(reg, num, val) do {                                \
101         __asm __volatile("msr dbg" reg #num "_el1, %0" :: "r" (val));   \
102 } while (0)
103
104 #define READ_WB_REG_CASE(reg, num, offset, val)         \
105         case (num + offset):                            \
106                 DBG_WB_READ(reg, num, val);             \
107                 break
108
109 #define WRITE_WB_REG_CASE(reg, num, offset, val)        \
110         case (num + offset):                            \
111                 DBG_WB_WRITE(reg, num, val);            \
112                 break
113
114 #define SWITCH_CASES_READ_WB_REG(reg, offset, val)      \
115         READ_WB_REG_CASE(reg,  0, offset, val);         \
116         READ_WB_REG_CASE(reg,  1, offset, val);         \
117         READ_WB_REG_CASE(reg,  2, offset, val);         \
118         READ_WB_REG_CASE(reg,  3, offset, val);         \
119         READ_WB_REG_CASE(reg,  4, offset, val);         \
120         READ_WB_REG_CASE(reg,  5, offset, val);         \
121         READ_WB_REG_CASE(reg,  6, offset, val);         \
122         READ_WB_REG_CASE(reg,  7, offset, val);         \
123         READ_WB_REG_CASE(reg,  8, offset, val);         \
124         READ_WB_REG_CASE(reg,  9, offset, val);         \
125         READ_WB_REG_CASE(reg, 10, offset, val);         \
126         READ_WB_REG_CASE(reg, 11, offset, val);         \
127         READ_WB_REG_CASE(reg, 12, offset, val);         \
128         READ_WB_REG_CASE(reg, 13, offset, val);         \
129         READ_WB_REG_CASE(reg, 14, offset, val);         \
130         READ_WB_REG_CASE(reg, 15, offset, val)
131
132 #define SWITCH_CASES_WRITE_WB_REG(reg, offset, val)     \
133         WRITE_WB_REG_CASE(reg,  0, offset, val);        \
134         WRITE_WB_REG_CASE(reg,  1, offset, val);        \
135         WRITE_WB_REG_CASE(reg,  2, offset, val);        \
136         WRITE_WB_REG_CASE(reg,  3, offset, val);        \
137         WRITE_WB_REG_CASE(reg,  4, offset, val);        \
138         WRITE_WB_REG_CASE(reg,  5, offset, val);        \
139         WRITE_WB_REG_CASE(reg,  6, offset, val);        \
140         WRITE_WB_REG_CASE(reg,  7, offset, val);        \
141         WRITE_WB_REG_CASE(reg,  8, offset, val);        \
142         WRITE_WB_REG_CASE(reg,  9, offset, val);        \
143         WRITE_WB_REG_CASE(reg, 10, offset, val);        \
144         WRITE_WB_REG_CASE(reg, 11, offset, val);        \
145         WRITE_WB_REG_CASE(reg, 12, offset, val);        \
146         WRITE_WB_REG_CASE(reg, 13, offset, val);        \
147         WRITE_WB_REG_CASE(reg, 14, offset, val);        \
148         WRITE_WB_REG_CASE(reg, 15, offset, val)
149
150 #ifdef DDB
151 static uint64_t
152 dbg_wb_read_reg(int reg, int n)
153 {
154         uint64_t val = 0;
155
156         switch (reg + n) {
157         SWITCH_CASES_READ_WB_REG(DBG_WB_WVR, DBG_REG_BASE_WVR, val);
158         SWITCH_CASES_READ_WB_REG(DBG_WB_WCR, DBG_REG_BASE_WCR, val);
159         SWITCH_CASES_READ_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val);
160         SWITCH_CASES_READ_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val);
161         default:
162                 printf("trying to read from wrong debug register %d\n", n);
163         }
164
165         return val;
166 }
167 #endif /* DDB */
168
169 static void
170 dbg_wb_write_reg(int reg, int n, uint64_t val)
171 {
172         switch (reg + n) {
173         SWITCH_CASES_WRITE_WB_REG(DBG_WB_WVR, DBG_REG_BASE_WVR, val);
174         SWITCH_CASES_WRITE_WB_REG(DBG_WB_WCR, DBG_REG_BASE_WCR, val);
175         SWITCH_CASES_WRITE_WB_REG(DBG_WB_BVR, DBG_REG_BASE_BVR, val);
176         SWITCH_CASES_WRITE_WB_REG(DBG_WB_BCR, DBG_REG_BASE_BCR, val);
177         default:
178                 printf("trying to write to wrong debug register %d\n", n);
179                 return;
180         }
181         isb();
182 }
183
184 #ifdef DDB
185 void
186 kdb_cpu_set_singlestep(void)
187 {
188
189         kdb_frame->tf_spsr |= DBG_SPSR_SS;
190         WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) |
191             DBG_MDSCR_SS | DBG_MDSCR_KDE);
192
193         /*
194          * Disable breakpoints and watchpoints, e.g. stepping
195          * over watched instruction will trigger break exception instead of
196          * single-step exception and locks CPU on that instruction for ever.
197          */
198         if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
199                 WRITE_SPECIALREG(mdscr_el1,
200                     READ_SPECIALREG(mdscr_el1) & ~DBG_MDSCR_MDE);
201         }
202 }
203
204 void
205 kdb_cpu_clear_singlestep(void)
206 {
207
208         WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) &
209             ~(DBG_MDSCR_SS | DBG_MDSCR_KDE));
210
211         /* Restore breakpoints and watchpoints */
212         if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
213                 WRITE_SPECIALREG(mdscr_el1,
214                     READ_SPECIALREG(mdscr_el1) | DBG_MDSCR_MDE);
215
216                 if ((kernel_monitor.dbg_flags & DBGMON_KERNEL) != 0) {
217                         WRITE_SPECIALREG(mdscr_el1,
218                             READ_SPECIALREG(mdscr_el1) | DBG_MDSCR_KDE);
219                 }
220         }
221 }
222
223 static const char *
224 dbg_watchtype_str(uint32_t type)
225 {
226         switch (type) {
227                 case DBG_WATCH_CTRL_EXEC:
228                         return ("execute");
229                 case DBG_WATCH_CTRL_STORE:
230                         return ("write");
231                 case DBG_WATCH_CTRL_LOAD:
232                         return ("read");
233                 case DBG_WATCH_CTRL_LOAD | DBG_WATCH_CTRL_STORE:
234                         return ("read/write");
235                 default:
236                         return ("invalid");
237         }
238 }
239
240 static int
241 dbg_watchtype_len(uint32_t len)
242 {
243         switch (len) {
244         case DBG_WATCH_CTRL_LEN_1:
245                 return (1);
246         case DBG_WATCH_CTRL_LEN_2:
247                 return (2);
248         case DBG_WATCH_CTRL_LEN_4:
249                 return (4);
250         case DBG_WATCH_CTRL_LEN_8:
251                 return (8);
252         default:
253                 return (0);
254         }
255 }
256
257 void
258 dbg_show_watchpoint(void)
259 {
260         uint32_t wcr, len, type;
261         uint64_t addr;
262         int i;
263
264         db_printf("\nhardware watchpoints:\n");
265         db_printf("  watch    status        type  len             address              symbol\n");
266         db_printf("  -----  --------  ----------  ---  ------------------  ------------------\n");
267         for (i = 0; i < dbg_watchpoint_num; i++) {
268                 wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i);
269                 if ((wcr & DBG_WB_CTRL_E) != 0) {
270                         type = DBG_WATCH_CTRL_ACCESS_MASK(wcr);
271                         len = DBG_WATCH_CTRL_LEN_MASK(wcr);
272                         addr = dbg_wb_read_reg(DBG_REG_BASE_WVR, i);
273                         db_printf("  %-5d  %-8s  %10s  %3d  0x%16lx  ",
274                             i, "enabled", dbg_watchtype_str(type),
275                             dbg_watchtype_len(len), addr);
276                         db_printsym((db_addr_t)addr, DB_STGY_ANY);
277                         db_printf("\n");
278                 } else {
279                         db_printf("  %-5d  disabled\n", i);
280                 }
281         }
282 }
283 #endif /* DDB */
284
285 static int
286 dbg_find_free_slot(struct debug_monitor_state *monitor, enum dbg_t type)
287 {
288         uint64_t *reg;
289         u_int max, i;
290
291         switch(type) {
292         case DBG_TYPE_BREAKPOINT:
293                 max = dbg_breakpoint_num;
294                 reg = monitor->dbg_bcr;
295                 break;
296         case DBG_TYPE_WATCHPOINT:
297                 max = dbg_watchpoint_num;
298                 reg = monitor->dbg_wcr;
299                 break;
300         default:
301                 printf("Unsupported debug type\n");
302                 return (i);
303         }
304
305         for (i = 0; i < max; i++) {
306                 if ((reg[i] & DBG_WB_CTRL_E) == 0)
307                         return (i);
308         }
309
310         return (-1);
311 }
312
313 static int
314 dbg_find_slot(struct debug_monitor_state *monitor, enum dbg_t type,
315     vm_offset_t addr)
316 {
317         uint64_t *reg_addr, *reg_ctrl;
318         u_int max, i;
319
320         switch(type) {
321         case DBG_TYPE_BREAKPOINT:
322                 max = dbg_breakpoint_num;
323                 reg_addr = monitor->dbg_bvr;
324                 reg_ctrl = monitor->dbg_bcr;
325                 break;
326         case DBG_TYPE_WATCHPOINT:
327                 max = dbg_watchpoint_num;
328                 reg_addr = monitor->dbg_wvr;
329                 reg_ctrl = monitor->dbg_wcr;
330                 break;
331         default:
332                 printf("Unsupported debug type\n");
333                 return (i);
334         }
335
336         for (i = 0; i < max; i++) {
337                 if (reg_addr[i] == addr &&
338                     (reg_ctrl[i] & DBG_WB_CTRL_E) != 0)
339                         return (i);
340         }
341
342         return (-1);
343 }
344
345 int
346 dbg_setup_watchpoint(struct debug_monitor_state *monitor, vm_offset_t addr,
347     vm_size_t size, enum dbg_access_t access)
348 {
349         uint64_t wcr_size, wcr_priv, wcr_access;
350         u_int i;
351
352         if (monitor == NULL)
353                 monitor = &kernel_monitor;
354
355         i = dbg_find_free_slot(monitor, DBG_TYPE_WATCHPOINT);
356         if (i == -1) {
357                 printf("Can not find slot for watchpoint, max %d"
358                     " watchpoints supported\n", dbg_watchpoint_num);
359                 return (i);
360         }
361
362         switch(size) {
363         case 1:
364                 wcr_size = DBG_WATCH_CTRL_LEN_1;
365                 break;
366         case 2:
367                 wcr_size = DBG_WATCH_CTRL_LEN_2;
368                 break;
369         case 4:
370                 wcr_size = DBG_WATCH_CTRL_LEN_4;
371                 break;
372         case 8:
373                 wcr_size = DBG_WATCH_CTRL_LEN_8;
374                 break;
375         default:
376                 printf("Unsupported address size for watchpoint\n");
377                 return (-1);
378         }
379
380         if ((monitor->dbg_flags & DBGMON_KERNEL) == 0)
381                 wcr_priv = DBG_WB_CTRL_EL0;
382         else
383                 wcr_priv = DBG_WB_CTRL_EL1;
384
385         switch(access) {
386         case HW_BREAKPOINT_X:
387                 wcr_access = DBG_WATCH_CTRL_EXEC;
388                 break;
389         case HW_BREAKPOINT_R:
390                 wcr_access = DBG_WATCH_CTRL_LOAD;
391                 break;
392         case HW_BREAKPOINT_W:
393                 wcr_access = DBG_WATCH_CTRL_STORE;
394                 break;
395         case HW_BREAKPOINT_RW:
396                 wcr_access = DBG_WATCH_CTRL_LOAD | DBG_WATCH_CTRL_STORE;
397                 break;
398         default:
399                 printf("Unsupported exception level for watchpoint\n");
400                 return (-1);
401         }
402
403         monitor->dbg_wvr[i] = addr;
404         monitor->dbg_wcr[i] = wcr_size | wcr_access | wcr_priv | DBG_WB_CTRL_E;
405         monitor->dbg_enable_count++;
406         monitor->dbg_flags |= DBGMON_ENABLED;
407
408         dbg_register_sync(monitor);
409         return (0);
410 }
411
412 int
413 dbg_remove_watchpoint(struct debug_monitor_state *monitor, vm_offset_t addr,
414     vm_size_t size)
415 {
416         u_int i;
417
418         if (monitor == NULL)
419                 monitor = &kernel_monitor;
420
421         i = dbg_find_slot(monitor, DBG_TYPE_WATCHPOINT, addr);
422         if (i == -1) {
423                 printf("Can not find watchpoint for address 0%lx\n", addr);
424                 return (i);
425         }
426
427         monitor->dbg_wvr[i] = 0;
428         monitor->dbg_wcr[i] = 0;
429         monitor->dbg_enable_count--;
430         if (monitor->dbg_enable_count == 0)
431                 monitor->dbg_flags &= ~DBGMON_ENABLED;
432
433         dbg_register_sync(monitor);
434         return (0);
435 }
436
437 void
438 dbg_register_sync(struct debug_monitor_state *monitor)
439 {
440         uint64_t mdscr;
441         int i;
442
443         if (monitor == NULL)
444                 monitor = &kernel_monitor;
445
446         mdscr = READ_SPECIALREG(mdscr_el1);
447         if ((monitor->dbg_flags & DBGMON_ENABLED) == 0) {
448                 mdscr &= ~(DBG_MDSCR_MDE | DBG_MDSCR_KDE);
449         } else {
450                 for (i = 0; i < dbg_breakpoint_num; i++) {
451                         dbg_wb_write_reg(DBG_REG_BASE_BCR, i,
452                             monitor->dbg_bcr[i]);
453                         dbg_wb_write_reg(DBG_REG_BASE_BVR, i,
454                             monitor->dbg_bvr[i]);
455                 }
456
457                 for (i = 0; i < dbg_watchpoint_num; i++) {
458                         dbg_wb_write_reg(DBG_REG_BASE_WCR, i,
459                             monitor->dbg_wcr[i]);
460                         dbg_wb_write_reg(DBG_REG_BASE_WVR, i,
461                             monitor->dbg_wvr[i]);
462                 }
463                 mdscr |= DBG_MDSCR_MDE;
464                 if ((monitor->dbg_flags & DBGMON_KERNEL) == DBGMON_KERNEL)
465                         mdscr |= DBG_MDSCR_KDE;
466         }
467         WRITE_SPECIALREG(mdscr_el1, mdscr);
468         isb();
469 }
470
471 void
472 dbg_monitor_init(void)
473 {
474         u_int i;
475
476         /* Find out many breakpoints and watchpoints we can use */
477         dbg_watchpoint_num = ((READ_SPECIALREG(id_aa64dfr0_el1) >> 20) & 0xf) + 1;
478         dbg_breakpoint_num = ((READ_SPECIALREG(id_aa64dfr0_el1) >> 12) & 0xf) + 1;
479
480         if (bootverbose && PCPU_GET(cpuid) == 0) {
481                 printf("%d watchpoints and %d breakpoints supported\n",
482                     dbg_watchpoint_num, dbg_breakpoint_num);
483         }
484
485         /*
486          * We have limited number of {watch,break}points, each consists of
487          * two registers:
488          * - wcr/bcr regsiter configurates corresponding {watch,break}point
489          *   behaviour
490          * - wvr/bvr register keeps address we are hunting for
491          *
492          * Reset all breakpoints and watchpoints.
493          */
494         for (i = 0; i < dbg_watchpoint_num; i++) {
495                 dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
496                 dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
497         }
498
499         for (i = 0; i < dbg_breakpoint_num; i++) {
500                 dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
501                 dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
502         }
503
504         dbg_enable();
505 }
506
507 void
508 dbg_monitor_enter(struct thread *thread)
509 {
510         int i;
511
512         if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
513                 /* Install the kernel version of the registers */
514                 dbg_register_sync(&kernel_monitor);
515         } else if ((thread->td_pcb->pcb_dbg_regs.dbg_flags & DBGMON_ENABLED) != 0) {
516                 /* Disable the user breakpoints until we return to userspace */
517                 for (i = 0; i < dbg_watchpoint_num; i++) {
518                         dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
519                         dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
520                 }
521
522                 for (i = 0; i < dbg_breakpoint_num; ++i) {
523                         dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
524                         dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
525                 }
526                 WRITE_SPECIALREG(mdscr_el1,
527                     READ_SPECIALREG(mdscr_el1) &
528                     ~(DBG_MDSCR_MDE | DBG_MDSCR_KDE));
529                 isb();
530         }
531 }
532
533 void
534 dbg_monitor_exit(struct thread *thread, struct trapframe *frame)
535 {
536         int i;
537
538         /*
539          * PSR_D is an aarch64-only flag. On aarch32, it switches
540          * the processor to big-endian, so avoid setting it for
541          * 32bits binaries.
542          */
543         if (!(SV_PROC_FLAG(thread->td_proc, SV_ILP32)))
544                 frame->tf_spsr |= PSR_D;
545         if ((thread->td_pcb->pcb_dbg_regs.dbg_flags & DBGMON_ENABLED) != 0) {
546                 /* Install the kernel version of the registers */
547                 dbg_register_sync(&thread->td_pcb->pcb_dbg_regs);
548                 frame->tf_spsr &= ~PSR_D;
549         } else if ((kernel_monitor.dbg_flags & DBGMON_ENABLED) != 0) {
550                 /* Disable the user breakpoints until we return to userspace */
551                 for (i = 0; i < dbg_watchpoint_num; i++) {
552                         dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0);
553                         dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0);
554                 }
555
556                 for (i = 0; i < dbg_breakpoint_num; ++i) {
557                         dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0);
558                         dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0);
559                 }
560                 WRITE_SPECIALREG(mdscr_el1,
561                     READ_SPECIALREG(mdscr_el1) &
562                     ~(DBG_MDSCR_MDE | DBG_MDSCR_KDE));
563                 isb();
564         }
565 }