Print this page
de-linting of .s files

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/sun4v/cpu/common_asm.s
          +++ new/usr/src/uts/sun4v/cpu/common_asm.s
↓ open down ↓ 14 lines elided ↑ open up ↑
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23   23   */
  24   24  
  25      -#if !defined(lint)
  26   25  #include "assym.h"
  27      -#endif
  28   26  
  29   27  /*
  30   28   * General assembly language routines.
  31   29   * It is the intent of this file to contain routines that are
  32   30   * specific to cpu architecture.
  33   31   */
  34   32  
  35   33  /*
  36   34   * WARNING: If you add a fast trap handler which can be invoked by a
  37   35   * non-privileged user, you may have to use the FAST_TRAP_DONE macro
  38   36   * instead of "done" instruction to return back to the user mode. See
  39   37   * comments for the "fast_trap_done" entry point for more information.
  40   38   */
  41   39  #define FAST_TRAP_DONE  \
  42   40          ba,a    fast_trap_done
  43   41  
  44   42  #include <sys/machclock.h>
  45   43  #include <sys/clock.h>
  46   44  
  47      -#if defined(lint)
  48      -#include <sys/types.h>
  49      -#include <sys/scb.h>
  50      -#include <sys/systm.h>
  51      -#include <sys/regset.h>
  52      -#include <sys/sunddi.h>
  53      -#include <sys/lockstat.h>
  54      -#endif  /* lint */
  55   45  
  56      -
  57   46  #include <sys/asm_linkage.h>
  58   47  #include <sys/privregs.h>
  59   48  #include <vm/hat_sfmmu.h>
  60   49  #include <sys/machparam.h>      /* To get SYSBASE and PAGESIZE */
  61   50  #include <sys/machthread.h>
  62   51  #include <sys/clock.h>
  63   52  #include <sys/intreg.h>
  64   53  #include <sys/psr_compat.h>
  65   54  #include <sys/isa_defs.h>
  66   55  #include <sys/dditypes.h>
  67   56  #include <sys/intr.h>
  68   57  #include <sys/hypervisor_api.h>
  69   58  
  70      -#if !defined(lint)
  71   59  #include "assym.h"
  72      -#endif
  73   60  
  74   61  #define ICACHE_FLUSHSZ  0x20
  75   62  
  76      -#if defined(lint)
  77      -/*
  78      - * Softint generated when counter field of tick reg matches value field 
  79      - * of tick_cmpr reg
  80      - */
  81      -/*ARGSUSED*/
  82      -void
  83      -tickcmpr_set(uint64_t clock_cycles)
  84      -{}
  85      -
  86      -#else   /* lint */
  87      -
  88   63          ENTRY_NP(tickcmpr_set)
  89   64          ! get 64-bit clock_cycles interval
  90   65          mov     %o0, %o2
  91   66          mov     8, %o3                  ! A reasonable initial step size
  92   67  1:
  93   68          WR_TICKCMPR(%o2,%o4,%o5,__LINE__)       ! Write to TICK_CMPR
  94   69  
  95   70          GET_NATIVE_TIME(%o0,%o4,%o5,__LINE__)   ! Read %tick to confirm the
  96   71                                                  ! value we wrote was in the
  97   72                                                  ! future.
↓ open down ↓ 1 lines elided ↑ open up ↑
  99   74          cmp     %o2, %o0                ! If the value we wrote was in the
 100   75          bg,pt   %xcc, 2f                !   future, then blow out of here.
 101   76            sllx  %o3, 1, %o3             ! If not, then double our step size,
 102   77          ba,pt   %xcc, 1b                !   and take another lap.
 103   78            add   %o0, %o3, %o2           !
 104   79  2:
 105   80          retl
 106   81            nop
 107   82          SET_SIZE(tickcmpr_set)
 108   83  
 109      -#endif  /* lint */
 110      -
 111      -#if defined(lint)
 112      -
 113      -void
 114      -tickcmpr_disable(void)
 115      -{}
 116      -
 117      -#else
 118      -
 119   84          ENTRY_NP(tickcmpr_disable)
 120   85          mov     1, %g1
 121   86          sllx    %g1, TICKINT_DIS_SHFT, %o0
 122   87          WR_TICKCMPR(%o0,%o4,%o5,__LINE__)       ! Write to TICK_CMPR
 123   88          retl
 124   89            nop
 125   90          SET_SIZE(tickcmpr_disable)
 126   91  
 127      -#endif
 128      -
 129      -#if defined(lint)
 130      -
 131      -/*
 132      - * tick_write_delta() is intended to increment %stick by the specified delta,
 133      - * but %stick is only writeable in hyperprivileged mode and at present there
 134      - * is no provision for this. tick_write_delta is called by the cylic subsystem
 135      - * if a negative %stick delta is observed after cyclic processing is resumed
 136      - * after an event such as an OS suspend/resume. On sun4v, the suspend/resume
 137      - * routines should adjust the %stick offset preventing the cyclic subsystem
 138      - * from detecting a negative delta. If a negative delta is detected, panic the
 139      - * system. The negative delta could be caused by improper %stick
 140      - * synchronization after a suspend/resume.
 141      - */
 142      -
 143      -/*ARGSUSED*/
 144      -void
 145      -tick_write_delta(uint64_t delta)
 146      -{}
 147      -
 148      -#else   /* lint */
 149      -
 150   92          .seg    ".text"
 151   93  tick_write_delta_panic:
 152   94          .asciz  "tick_write_delta: not supported, delta: 0x%lx"
 153   95  
 154   96          ENTRY_NP(tick_write_delta)
 155   97          sethi   %hi(tick_write_delta_panic), %o1
 156   98          save    %sp, -SA(MINFRAME), %sp ! get a new window to preserve caller
 157   99          mov     %i0, %o1
 158  100          call    panic
 159  101            or    %i1, %lo(tick_write_delta_panic), %o0
 160  102          /*NOTREACHED*/
 161  103          retl
 162  104            nop
 163      -#endif
 164  105  
 165      -#if defined(lint)
 166      -/*
 167      - *  return 1 if disabled
 168      - */
 169      -
 170      -int
 171      -tickcmpr_disabled(void)
 172      -{ return (0); }
 173      -
 174      -#else   /* lint */
 175      -
 176  106          ENTRY_NP(tickcmpr_disabled)
 177  107          RD_TICKCMPR(%g1,%o0,%o1,__LINE__)
 178  108          retl
 179  109            srlx  %g1, TICKINT_DIS_SHFT, %o0
 180  110          SET_SIZE(tickcmpr_disabled)
 181  111  
 182      -#endif  /* lint */
 183      -
 184  112  /*
 185  113   * Get current tick
 186  114   */
 187      -#if defined(lint)
 188  115  
 189      -u_longlong_t
 190      -gettick(void)
 191      -{ return (0); }
 192      -
 193      -u_longlong_t
 194      -randtick(void)
 195      -{ return (0); }
 196      -
 197      -#else   /* lint */
 198      -
 199  116          ENTRY(gettick)
 200  117          ALTENTRY(randtick)
 201  118          GET_NATIVE_TIME(%o0,%o2,%o3,__LINE__)
 202  119          retl
 203  120            nop
 204  121          SET_SIZE(randtick)
 205  122          SET_SIZE(gettick)
 206  123  
 207      -#endif  /* lint */
 208      -
 209  124  /*
 210  125   * Get current tick. For trapstat use only.
 211  126   */
 212      -#if defined (lint)
 213      -
 214      -hrtime_t
 215      -rdtick()
 216      -{ return (0); }
 217      -
 218      -#else
 219  127          ENTRY(rdtick)
 220  128          retl
 221  129          RD_TICK_PHYSICAL(%o0)
 222  130          SET_SIZE(rdtick)
 223      -#endif /* lint */
 224  131  
 225  132  
 226  133  /*
 227  134   * Return the counter portion of the tick register.
 228  135   */
 229  136  
 230      -#if defined(lint)
 231      -
 232      -uint64_t
 233      -gettick_counter(void)
 234      -{ return(0); }
 235      -
 236      -uint64_t
 237      -gettick_npt(void)
 238      -{ return(0); }
 239      -
 240      -uint64_t
 241      -getstick_npt(void)
 242      -{ return(0); }
 243      -
 244      -#else   /* lint */
 245      -
 246  137          ENTRY_NP(gettick_counter)
 247  138          RD_TICK(%o0,%o1,%o2,__LINE__)
 248  139          retl
 249  140          nop
 250  141          SET_SIZE(gettick_counter)
 251  142  
 252  143          ENTRY_NP(gettick_npt)
 253  144          RD_TICK_PHYSICAL(%o0)
 254  145          retl
 255  146          srlx    %o0, 63, %o0
 256  147          SET_SIZE(gettick_npt)
 257  148  
 258  149          ENTRY_NP(getstick_npt)
 259  150          RD_STICK_PHYSICAL(%o0)
 260  151          retl
 261  152          srlx    %o0, 63, %o0
 262  153          SET_SIZE(getstick_npt)
 263      -#endif  /* lint */
 264  154  
 265  155  /*
 266  156   * Provide a C callable interface to the trap that reads the hi-res timer.
 267  157   * Returns 64-bit nanosecond timestamp in %o0 and %o1.
 268  158   */
 269  159  
 270      -#if defined(lint)
 271      -
 272      -hrtime_t
 273      -gethrtime(void)
 274      -{
 275      -        return ((hrtime_t)0);
 276      -}
 277      -
 278      -hrtime_t
 279      -gethrtime_unscaled(void)
 280      -{
 281      -        return ((hrtime_t)0);
 282      -}
 283      -
 284      -hrtime_t
 285      -gethrtime_max(void)
 286      -{
 287      -        return ((hrtime_t)0);
 288      -}
 289      -
 290      -void
 291      -scalehrtime(hrtime_t *hrt)
 292      -{
 293      -        *hrt = 0;
 294      -}
 295      -
 296      -void
 297      -gethrestime(timespec_t *tp)
 298      -{
 299      -        tp->tv_sec = 0;
 300      -        tp->tv_nsec = 0;
 301      -}
 302      -
 303      -time_t
 304      -gethrestime_sec(void)
 305      -{
 306      -        return (0);
 307      -}
 308      -
 309      -void
 310      -gethrestime_lasttick(timespec_t *tp)
 311      -{
 312      -        tp->tv_sec = 0;
 313      -        tp->tv_nsec = 0;
 314      -}
 315      -
 316      -/*ARGSUSED*/
 317      -void
 318      -hres_tick(void)
 319      -{
 320      -}
 321      -
 322      -void
 323      -panic_hres_tick(void)
 324      -{
 325      -}
 326      -
 327      -#else   /* lint */
 328      -
 329  160          ENTRY_NP(gethrtime)
 330  161          GET_HRTIME(%g1,%o0,%o1,%o2,%o3,%o4,%o5,%g2,__LINE__)
 331  162                                                          ! %g1 = hrtime
 332  163          retl
 333  164            mov   %g1, %o0
 334  165          SET_SIZE(gethrtime)
 335  166  
 336  167          ENTRY_NP(gethrtime_unscaled)
 337  168          GET_NATIVE_TIME(%g1,%o2,%o3,__LINE__)   ! %g1 = native time
 338  169          retl
↓ open down ↓ 272 lines elided ↑ open up ↑
 611  442          ld      [%l4 + %lo(hres_lock)], %i1
 612  443          inc     %i1
 613  444          st      %i1, [%l4 + %lo(hres_lock)]
 614  445  
 615  446          sethi   %hi(hrtime_base_panic), %o0
 616  447          call    panic
 617  448            or    %o0, %lo(hrtime_base_panic), %o0
 618  449  
 619  450          SET_SIZE(hres_tick)
 620  451  
 621      -#endif  /* lint */
 622      -
 623      -#if !defined(lint) && !defined(__lint)
 624      -
 625  452          .seg    ".text"
 626  453  kstat_q_panic_msg:
 627  454          .asciz  "kstat_q_exit: qlen == 0"
 628  455  
 629  456          ENTRY(kstat_q_panic)
 630  457          save    %sp, -SA(MINFRAME), %sp
 631  458          sethi   %hi(kstat_q_panic_msg), %o0
 632  459          call    panic
 633  460            or    %o0, %lo(kstat_q_panic_msg), %o0
 634  461          /*NOTREACHED*/
↓ open down ↓ 89 lines elided ↑ open up ↑
 724  551          ENTRY(kstat_runq_back_to_waitq)
 725  552          GET_NATIVE_TIME(%g1,%g2,%g3,__LINE__)
 726  553  #if defined(DEBUG)
 727  554          KSTAT_Q_UPDATE(sub, BRZPN, kstat_q_panic, 1:, KSTAT_IO_R)
 728  555  #else
 729  556          KSTAT_Q_UPDATE_ND(sub, 1:, KSTAT_IO_R)
 730  557  #endif
 731  558          KSTAT_Q_UPDATE(add, BRZPT, 1f, 1:retl, KSTAT_IO_W)
 732  559          SET_SIZE(kstat_runq_back_to_waitq)
 733  560  
 734      -#endif /* lint */
 735      -
 736      -#ifdef lint     
 737      -
 738      -int64_t timedelta;
 739      -hrtime_t hres_last_tick;
 740      -volatile timestruc_t hrestime;
 741      -int64_t hrestime_adj;
 742      -volatile int hres_lock;
 743      -uint_t nsec_scale;
 744      -hrtime_t hrtime_base;
 745      -int traptrace_use_stick;
 746      -
 747      -#else
 748  561          /*
 749  562           *  -- WARNING --
 750  563           *
 751  564           * The following variables MUST be together on a 128-byte boundary.
 752  565           * In addition to the primary performance motivation (having them all
 753  566           * on the same cache line(s)), code here and in the GET*TIME() macros
 754  567           * assumes that they all have the same high 22 address bits (so
 755  568           * there's only one sethi).
 756  569           */
 757  570          .seg    ".data"
↓ open down ↓ 23 lines elided ↑ open up ↑
 781  594          .word   NSEC_SHIFT
 782  595  adj_shift:
 783  596          .word   ADJ_SHIFT
 784  597          .align  8
 785  598  native_tick_offset:
 786  599          .word   0, 0
 787  600          .align  8
 788  601  native_stick_offset:
 789  602          .word   0, 0
 790  603  
 791      -#endif
 792  604  
 793      -
 794  605  /*
 795  606   * drv_usecwait(clock_t n)      [DDI/DKI - section 9F]
 796  607   * usec_delay(int n)            [compatibility - should go one day]
 797  608   * Delay by spinning.
 798  609   *
 799  610   * delay for n microseconds.  numbers <= 0 delay 1 usec
 800  611   *
 801  612   * With UltraSPARC-III the combination of supporting mixed-speed CPUs
 802  613   * and variable clock rate for power management requires that we
 803  614   * use %stick to implement this routine.
 804  615   */
 805  616  
 806      -#if defined(lint)
 807      -
 808      -/*ARGSUSED*/
 809      -void
 810      -drv_usecwait(clock_t n)
 811      -{}
 812      -
 813      -/*ARGSUSED*/
 814      -void
 815      -usec_delay(int n)
 816      -{}
 817      -
 818      -#else   /* lint */
 819      -
 820  617          ENTRY(drv_usecwait)
 821  618          ALTENTRY(usec_delay)
 822  619          brlez,a,pn %o0, 0f
 823  620            mov   1, %o0
 824  621  0:
 825  622          sethi   %hi(sticks_per_usec), %o1
 826  623          lduw    [%o1 + %lo(sticks_per_usec)], %o1
 827  624          mulx    %o1, %o0, %o1           ! Scale usec to ticks
 828  625          inc     %o1                     ! We don't start on a tick edge
 829  626          GET_NATIVE_TIME(%o2,%o3,%o4,__LINE__)
 830  627          add     %o1, %o2, %o1
 831  628  
 832  629  1:      cmp     %o1, %o2
 833  630          GET_NATIVE_TIME(%o2,%o3,%o4,__LINE__)
 834  631          bgeu,pt %xcc, 1b
 835  632            nop
 836  633          retl
 837  634            nop
 838  635          SET_SIZE(usec_delay)
 839  636          SET_SIZE(drv_usecwait)
 840      -#endif  /* lint */
 841  637  
 842      -#if defined(lint)
 843      -
 844      -/* ARGSUSED */
 845      -void
 846      -pil14_interrupt(int level)
 847      -{}
 848      -
 849      -#else
 850      -
 851  638  /*
 852  639   * Level-14 interrupt prologue.
 853  640   */
 854  641          ENTRY_NP(pil14_interrupt)
 855  642          CPU_ADDR(%g1, %g2)
 856  643          rdpr    %pil, %g6                       ! %g6 = interrupted PIL
 857  644          stn     %g6, [%g1 + CPU_PROFILE_PIL]    ! record interrupted PIL
 858  645          rdpr    %tstate, %g6
 859  646          rdpr    %tpc, %g5
 860  647          btst    TSTATE_PRIV, %g6                ! trap from supervisor mode?
↓ open down ↓ 65 lines elided ↑ open up ↑
 926  713          cmp     %o5, %o0                        ! In the future?
 927  714          bg,a,pt %xcc, 2f                        ! Yes, drive on.
 928  715            wrpr  %g0, %g5, %pstate               !    delay: enable vec intr
 929  716          ba      1b                              ! No, try again.
 930  717            sllx  %o4, 1, %o4                     !    delay: double step size
 931  718  
 932  719  2:      ba      current_thread_complete
 933  720            nop
 934  721          SET_SIZE(tick_rtt)
 935  722  
 936      -#endif /* lint */
 937      -
 938      -#if defined(lint)
 939      -
 940      -/* ARGSUSED */
 941      -void
 942      -pil15_interrupt(int level)
 943      -{}
 944      -
 945      -#else   /* lint */
 946      -
 947  723  /*
 948  724   * Level-15 interrupt prologue.
 949  725   */
 950  726         ENTRY_NP(pil15_interrupt)
 951  727         CPU_ADDR(%g1, %g2)
 952  728         rdpr    %tstate, %g6
 953  729         rdpr    %tpc, %g5
 954  730         btst    TSTATE_PRIV, %g6                ! trap from supervisor mode?
 955  731         bnz,a,pt %xcc, 1f
 956  732         stn     %g5, [%g1 + CPU_CPCPROFILE_PC]  ! if so, record kernel PC
 957  733         stn     %g5, [%g1 + CPU_CPCPROFILE_UPC] ! if not, record user PC
 958  734         ba      pil15_epilogue                  ! must be large-disp branch
 959  735         stn     %g0, [%g1 + CPU_CPCPROFILE_PC]  ! zero kernel PC
 960  736  1:     ba      pil15_epilogue                  ! must be large-disp branch
 961  737         stn     %g0, [%g1 + CPU_CPCPROFILE_UPC] ! zero user PC
 962  738         SET_SIZE(pil15_interrupt)
 963  739  
 964      -#endif  /* lint */
 965      -
 966      -#if defined(lint)
 967      -/*
 968      - * Prefetch a page_t for write or read, this assumes a linear
 969      - * scan of sequential page_t's.
 970      - */
 971      -/*ARGSUSED*/
 972      -void
 973      -prefetch_page_w(void *pp)
 974      -{}
 975      -
 976      -/*ARGSUSED*/
 977      -void
 978      -prefetch_page_r(void *pp)
 979      -{}
 980      -#else   /* lint */
 981      -
 982  740  /* XXXQ These should be inline templates, not functions */
 983  741          ENTRY(prefetch_page_w)
 984  742          retl
 985  743            nop
 986  744          SET_SIZE(prefetch_page_w)
 987  745  
 988  746          ENTRY(prefetch_page_r)
 989  747          retl
 990  748            nop
 991  749          SET_SIZE(prefetch_page_r)
 992  750  
 993      -#endif  /* lint */
 994      -
 995      -#if defined(lint)
 996      -/*
 997      - * Prefetch struct smap for write. 
 998      - */
 999      -/*ARGSUSED*/
1000      -void
1001      -prefetch_smap_w(void *smp)
1002      -{}
1003      -#else   /* lint */
1004      -
1005  751  /* XXXQ These should be inline templates, not functions */
1006  752          ENTRY(prefetch_smap_w)
1007  753          retl
1008  754            nop
1009  755          SET_SIZE(prefetch_smap_w)
1010  756  
1011      -#endif  /* lint */
1012      -
1013  757  /*
1014  758   * Generic sun4v MMU and Cache operations.
1015  759   */
1016  760  
1017      -#if defined(lint)
1018      -
1019      -/*ARGSUSED*/
1020      -void
1021      -vtag_flushpage(caddr_t vaddr, uint64_t sfmmup)
1022      -{}
1023      -
1024      -/*ARGSUSED*/
1025      -void
1026      -vtag_flushall(void)
1027      -{}
1028      -
1029      -/*ARGSUSED*/
1030      -void
1031      -vtag_unmap_perm_tl1(uint64_t vaddr, uint64_t ctxnum)
1032      -{}
1033      -
1034      -/*ARGSUSED*/
1035      -void
1036      -vtag_flushpage_tl1(uint64_t vaddr, uint64_t sfmmup)
1037      -{}
1038      -
1039      -/*ARGSUSED*/
1040      -void
1041      -vtag_flush_pgcnt_tl1(uint64_t vaddr, uint64_t sfmmup_pgcnt)
1042      -{}
1043      -
1044      -/*ARGSUSED*/
1045      -void
1046      -vtag_flushall_tl1(uint64_t dummy1, uint64_t dummy2)
1047      -{}
1048      -
1049      -/*ARGSUSED*/
1050      -void
1051      -vac_flushpage(pfn_t pfnum, int vcolor)
1052      -{}
1053      -
1054      -/*ARGSUSED*/
1055      -void
1056      -vac_flushpage_tl1(uint64_t pfnum, uint64_t vcolor)
1057      -{}
1058      -
1059      -/*ARGSUSED*/
1060      -void
1061      -flush_instr_mem(caddr_t vaddr, size_t len)
1062      -{}
1063      -
1064      -#else   /* lint */
1065      -
1066  761          ENTRY_NP(vtag_flushpage)
1067  762          /*
1068  763           * flush page from the tlb
1069  764           *
1070  765           * %o0 = vaddr
1071  766           * %o1 = sfmmup
1072  767           */
1073  768          SFMMU_CPU_CNUM(%o1, %g1, %g2)   /* %g1 = sfmmu cnum on this CPU */
1074  769  
1075  770          mov     %g1, %o1 
↓ open down ↓ 179 lines elided ↑ open up ↑
1255  950  1:
1256  951          flush   %o0
1257  952          subcc   %o1, ICACHE_FLUSHSZ, %o1                ! bytes = bytes-0x20
1258  953          bgu,pt  %ncc, 1b
1259  954            add   %o0, ICACHE_FLUSHSZ, %o0                ! vaddr = vaddr+0x20
1260  955  
1261  956          retl
1262  957            nop
1263  958          SET_SIZE(flush_instr_mem)
1264  959  
1265      -#endif /* !lint */
1266      -
1267  960  #if !defined(CUSTOM_FPZERO)
1268  961  
1269  962  /*
1270  963   * fp_zero() - clear all fp data registers and the fsr
1271  964   */
1272  965  
1273      -#if defined(lint) || defined(__lint)
1274      -
1275      -void
1276      -fp_zero(void)
1277      -{}
1278      -
1279      -#else   /* lint */
1280      -
1281  966  .global fp_zero_zero
1282  967  .align 8
1283  968  fp_zero_zero:
1284  969          .xword  0
1285  970  
1286  971          ENTRY_NP(fp_zero)
1287  972          sethi   %hi(fp_zero_zero), %o0
1288  973          ldx     [%o0 + %lo(fp_zero_zero)], %fsr
1289  974          ldd     [%o0 + %lo(fp_zero_zero)], %f0
1290  975          fmovd   %f0, %f2
↓ open down ↓ 23 lines elided ↑ open up ↑
1314  999          fmovd   %f0, %f50
1315 1000          fmovd   %f0, %f52
1316 1001          fmovd   %f0, %f54
1317 1002          fmovd   %f0, %f56
1318 1003          fmovd   %f0, %f58
1319 1004          fmovd   %f0, %f60
1320 1005          retl
1321 1006          fmovd   %f0, %f62
1322 1007          SET_SIZE(fp_zero)
1323 1008  
1324      -#endif  /* lint */
1325 1009  #endif  /* CUSTOM_FPZERO */
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX