The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/alphapc/main.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 #include        "u.h"
    2 #include        "../port/lib.h"
    3 #include        "mem.h"
    4 #include        "dat.h"
    5 #include        "fns.h"
    6 #include        "io.h"
    7 #include        "init.h"
    8 #include        "pool.h"
    9 #include        "/sys/src/boot/alphapc/conf.h"
   10 #include        "axp.h"
   11 
   12 char argbuf[128];       /* arguments passed to initcode and /boot */
   13 
   14 Hwrpb *hwrpb;
   15 Bootconf *bootconf;
   16 Conf    conf;
   17 FPsave  initfp;
   18         /* setfcr(FPPDBL|FPRNR|FPINVAL|FPZDIV|FPOVFL) */
   19 uvlong initfpcr = (1LL<62)|(1LL<61)|(1LL<60)|(2LL<<58)|(1LL<48);
   20 
   21 char bootargs[BOOTARGSLEN];
   22 char *confname[MAXCONF];
   23 char *confval[MAXCONF];
   24 int     nconf;
   25 
   26 static void
   27 options(void)
   28 {
   29         long i, n;
   30         char *cp, *line[MAXCONF], *p, *q;
   31 
   32         cp = bootargs;
   33         strncpy(cp, bootconf->bootargs, BOOTARGSLEN);
   34         cp[BOOTARGSLEN-1] = 0;
   35         /* can't print in this routine, see below in main() */
   36 
   37         /*
   38          * Strip out '\r', change '\t' -> ' '.
   39          */
   40         p = cp;
   41         for(q = cp; *q; q++){
   42                 if(*q == '\r')
   43                         continue;
   44                 if(*q == '\t')
   45                         *q = ' ';
   46                 *p++ = *q;
   47         }
   48         *p = 0;
   49 
   50         n = getfields(cp, line, MAXCONF, 1, "\n");
   51         for(i = 0; i < n; i++){
   52                 if(*line[i] == '#')
   53                         continue;
   54                 cp = strchr(line[i], '=');
   55                 if(cp == nil)
   56                         continue;
   57                 *cp++ = '\0';
   58                 confname[nconf] = line[i];
   59                 confval[nconf] = cp;
   60                 nconf++;
   61         }
   62 }
   63 
   64 /* debugging only */
   65 static void
   66 dumpopts(void)
   67 {
   68         int i;
   69 
   70         print("dumpopts: found /alpha/conf options at %#p\n",
   71                 bootconf->bootargs);
   72         for(i = 0; i < nconf; i++)
   73                 print("dumpopts: read %s=%s\n", confname[i], confval[i]);
   74 }
   75 
   76 extern void (*i8237alloc)(void);
   77 
   78 void
   79 main(void)
   80 {
   81         hwrpb = (Hwrpb*)0x10000000;
   82         hwrpb = (Hwrpb*)(KZERO|hwrpb->phys);
   83         arginit();
   84         machinit();
   85         options();
   86         ioinit();
   87         clockinit();
   88         confinit();
   89         archinit();
   90         xinit();
   91         memholes();
   92         if(i8237alloc != nil)
   93                 i8237alloc();
   94         mmuinit();
   95         if(arch->coreinit)
   96                 arch->coreinit();
   97         trapinit();
   98         screeninit();
   99         printinit();
  100         /* it's now safe to print */
  101         /* dumpopts();                  /* DEBUG */
  102         kbdinit();
  103         i8250console();
  104         quotefmtinstall();
  105         print("\nPlan 9\n");
  106 
  107         cpuidprint();
  108         if(arch->corehello)
  109                 arch->corehello();
  110 
  111         procinit0();
  112         initseg();
  113         timersinit();
  114         links();
  115         chandevreset();
  116         pageinit();
  117         swapinit();
  118         savefpregs(&initfp);
  119 initfp.fpstatus = 0x68028000;
  120         userinit();
  121         schedinit();
  122 }
  123 
  124 /* cpu->state bits */
  125 enum {
  126         Cpubootinprog   = 1,    /* boot in progress */
  127         Cpucanrestart   = 2,    /* restart possible */
  128         Cpuavail        = 4,    /* processor available */
  129         Cpuexists       = 8,    /* processor present */
  130         Cpuuserhalted   = 0x10, /* user halted */
  131         Cpuctxtokay     = 0x20, /* context valid */
  132         Cpupalokay      = 0x40, /* PALcode valid */
  133         Cpupalmemokay   = 0x80, /* PALcode memory valid */
  134         Cpupalloaded    = 0x100, /* PALcode loaded */
  135         Cpuhaltmask     = 0xff0000, /* halt request mask */
  136         Cpuhaltdflt     = 0,
  137         Cpuhaltsaveexit = 0x10000,
  138         Cpuhaltcoldboot = 0x20000,
  139         Cpuhaltwarmboot = 0x30000,
  140         Cpuhaltstayhalted = 0x40000,
  141         Cpumustbezero = 0xffffffffff000000ULL,  /* 24:63 -- must be zero */
  142 };
  143 
  144 /*
  145  *  initialize a processor's mach structure.  each processor does this
  146  *  for itself.
  147  */
  148 void
  149 machinit(void)
  150 {
  151         int n;
  152         Hwcpu *cpu;
  153 
  154         icflush();
  155         n = m->machno;
  156         memset(m, 0, sizeof(Mach));
  157         m->machno = n;
  158 
  159         active.exiting = 0;
  160         active.machs = 1;
  161 
  162         cpu = (Hwcpu*) ((ulong)hwrpb + hwrpb->cpuoff + n*hwrpb->cpulen);
  163         cpu->state &= ~Cpubootinprog;
  164         if (0)
  165                 cpu->state |= Cpuhaltstayhalted;
  166 }
  167 
  168 void
  169 init0(void)
  170 {
  171         int i;
  172         char buf[2*KNAMELEN];
  173 
  174         up->nerrlab = 0;
  175 
  176         spllo();
  177 
  178         /*
  179          * These are o.k. because rootinit is null.
  180          * Then early kproc's will have a root and dot.
  181          */
  182         up->slash = namec("#/", Atodir, 0, 0);
  183         pathclose(up->slash->path);
  184         up->slash->path = newpath("/");
  185         up->dot = cclone(up->slash);
  186 
  187         chandevinit();
  188 
  189         if(!waserror()){
  190                 snprint(buf, sizeof(buf), "alpha %s alphapc", conffile);
  191                 ksetenv("terminal", buf, 0);
  192                 ksetenv("cputype", "alpha", 0);
  193                 if(cpuserver)
  194                         ksetenv("service", "cpu", 0);
  195                 else
  196                         ksetenv("service", "terminal", 0);
  197                 for(i = 0; i < nconf; i++)
  198                         if(confname[i]){
  199                                 if(confname[i][0] != '*')
  200                                         ksetenv(confname[i], confval[i], 0);
  201                                 ksetenv(confname[i], confval[i], 1);
  202                         }
  203                 poperror();
  204         }
  205 
  206         kproc("alarm", alarmkproc, 0);
  207         touser((uchar*)(USTKTOP - sizeof(argbuf)));
  208 }
  209 
  210 void
  211 userinit(void)
  212 {
  213         Proc *p;
  214         Segment *s;
  215         KMap *k;
  216         char **av;
  217         Page *pg;
  218 
  219         p = newproc();
  220         p->pgrp = newpgrp();
  221         p->egrp = smalloc(sizeof(Egrp));
  222         p->egrp->ref = 1;
  223         p->fgrp = dupfgrp(nil);
  224         p->rgrp = newrgrp();
  225         p->procmode = 0640;
  226 
  227         kstrdup(&eve, "");
  228         kstrdup(&p->text, "*init*");
  229         kstrdup(&p->user, eve);
  230 
  231         procsetup(p);
  232 
  233         /*
  234          * Kernel Stack
  235          */
  236         p->sched.pc = (ulong)init0;
  237         p->sched.sp = (ulong)p->kstack+KSTACK-MAXSYSARG*BY2WD;
  238         /*
  239          * User Stack, pass input arguments to boot process
  240          */
  241         s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
  242         p->seg[SSEG] = s;
  243         pg = newpage(1, 0, USTKTOP-BY2PG);
  244         segpage(s, pg);
  245         k = kmap(pg);
  246         for(av = (char**)argbuf; *av; av++)
  247                 *av += (USTKTOP - sizeof(argbuf)) - (ulong)argbuf;
  248 
  249         memmove((uchar*)VA(k) + BY2PG - sizeof(argbuf), argbuf, sizeof argbuf);
  250         kunmap(k);
  251 
  252         /*
  253          * Text
  254          */
  255         s = newseg(SG_TEXT, UTZERO, 1);
  256         s->flushme++;
  257         p->seg[TSEG] = s;
  258         pg = newpage(1, 0, UTZERO);
  259         memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
  260         segpage(s, pg);
  261         k = kmap(s->map[0]->pages[0]);
  262         memmove((uchar*)VA(k), initcode, sizeof initcode);
  263         kunmap(k);
  264 
  265         ready(p);
  266 }
  267 
  268 void
  269 procsetup(Proc *p)
  270 {
  271         p->fpstate = FPinit;
  272         fpenab(0);
  273 }
  274 
  275 void
  276 procsave(Proc *p)
  277 {
  278         if(p->fpstate == FPactive){
  279                 if(p->state == Moribund)
  280                         fpenab(0);
  281                 else
  282                         savefpregs(&up->fpsave);
  283                 p->fpstate = FPinactive;
  284         }
  285 
  286         /*
  287          * Switch to the prototype page tables for this processor.
  288          * While this processor is in the scheduler, the process could run
  289          * on another processor and exit, returning the page tables to
  290          * the free list where they could be reallocated and overwritten.
  291          * When this processor eventually has to get an entry from the
  292          * trashed page tables it will crash.
  293          */
  294         mmupark();
  295 }
  296 
  297 void
  298 setupboot(int halt)
  299 {
  300         int n = 0;              // cpu id of primary cpu, not just m->machno
  301         Hwcpu *cpu = (Hwcpu*)((ulong)hwrpb + hwrpb->cpuoff + n*hwrpb->cpulen);
  302 
  303         cpu->state &= ~(Cpucanrestart | Cpuhaltmask);
  304         cpu->state |= (halt? Cpuhaltstayhalted: Cpuhaltwarmboot);
  305 }
  306 
  307 /* from ../pc */
  308 static void
  309 shutdown(int ispanic)
  310 {
  311         int ms, once;
  312 
  313         lock(&active);
  314         if(ispanic)
  315                 active.ispanic = ispanic;
  316         else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
  317                 active.ispanic = 0;
  318         once = active.machs & (1<<m->machno);
  319         active.machs &= ~(1<<m->machno);
  320         active.exiting = 1;
  321         unlock(&active);
  322 
  323         if(once)
  324                 print("cpu%d: exiting\n", m->machno);
  325         spllo();
  326         for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
  327                 delay(TK2MS(2));
  328                 if(active.machs == 0 && consactive() == 0)
  329                         break;
  330         }
  331 
  332         if(active.ispanic && m->machno == 0) {
  333                 if(cpuserver)
  334                         delay(10000);
  335                 else
  336                         for (;;)
  337                                 continue;
  338         } else
  339                 delay(1000);
  340 }
  341 
  342 /* from ../pc: */
  343 void
  344 reboot(void *entry, void *code, ulong size)
  345 {
  346         // writeconf();         // pass kernel environment to next kernel
  347         shutdown(0);
  348 
  349         /*
  350          * should be the only processor running now
  351          */
  352         print("shutting down...\n");
  353         delay(200);
  354 
  355         splhi();
  356 
  357         /* turn off buffered serial console */
  358         serialoq = nil;
  359 
  360         /* shutdown devices */
  361         chandevshutdown();
  362 
  363 #ifdef FUTURE
  364 {
  365         ulong *pdb;
  366         /*
  367          * Modify the machine page table to directly map the low 4MB of memory
  368          * This allows the reboot code to turn off the page mapping
  369          */
  370         pdb = m->pdb;
  371         pdb[PDX(0)] = pdb[PDX(KZERO)];
  372         mmuflushtlb(PADDR(pdb));
  373 }
  374         /* setup reboot trampoline function */
  375 {
  376         void (*f)(ulong, ulong, ulong) = (void*)REBOOTADDR;
  377 
  378         memmove(f, rebootcode, sizeof(rebootcode));
  379 #else
  380         USED(entry, code, size);
  381 #endif
  382 
  383         print("rebooting...\n");
  384 #ifdef FUTURE
  385         /* off we go - never to return */
  386         (*f)(PADDR(entry), PADDR(code), size);
  387 }
  388 #endif
  389         setupboot(0);           // reboot, don't halt
  390         exit(0);
  391 }
  392 
  393 void
  394 exit(int ispanic)
  395 {
  396         canlock(&active);
  397         active.machs &= ~(1<<m->machno);
  398         active.exiting = 1;
  399         unlock(&active);
  400 
  401         spllo();
  402         print("cpu %d exiting\n", m->machno);
  403         do
  404                 delay(100);
  405         while(consactive());
  406 
  407         splhi();
  408         delay(1000);    /* give serial fifo time to finish flushing */
  409         if (getconf("*debug") != nil) {
  410                 USED(ispanic);
  411                 delay(60*1000);         /* give us time to read the screen */
  412         }
  413         if(arch->coredetach)
  414                 arch->coredetach();
  415         setupboot(1);                   // set up to halt
  416         for (; ; )
  417                 firmware();
  418 
  419         // on PC is just:
  420         //if (0) {
  421         //      shutdown(ispanic);
  422         //      arch->reset();
  423         //}
  424 }
  425 
  426 void
  427 confinit(void)
  428 {
  429         ulong ktop, kpages;
  430         Bank *b, *eb;
  431         extern void _main(void);
  432         int userpcnt;
  433         char *p;
  434 
  435         if(p = getconf("*kernelpercent"))
  436                 userpcnt = 100 - strtol(p, 0, 0);
  437         else
  438                 userpcnt = 0;
  439 
  440         /*
  441          * The console firmware divides memory into 1 or more banks.
  442          * FInd the bank with the kernel in it.
  443          */
  444         b = bootconf->bank;
  445         eb = b+bootconf->nbank;
  446         ktop = PGROUND((ulong)end);
  447         ktop = PADDR(ktop);
  448         while(b < eb) {
  449                 if(b->min < ktop && ktop < b->max)
  450                         break;
  451                 b++;
  452         }
  453         if(b == eb)
  454                 panic("confinit");
  455 
  456         /*
  457          * Split the bank of memory into 2 banks to fool the allocator into
  458          * allocating low memory pages from bank 0 for any peripherals
  459          * which only have a 24bit address counter.
  460          */
  461         conf.mem[0].npage = (8*1024*1024)/BY2PG;
  462         conf.mem[0].base = 0;
  463 
  464         conf.mem[1].npage = (b->max-8*1024*1024)/BY2PG;
  465         conf.mem[1].base = 8*1024*1024;
  466 
  467         conf.npage = conf.mem[0].npage+conf.mem[1].npage;
  468         conf.upages = (conf.npage*70)/100;
  469 
  470         conf.mem[0].npage -= ktop/BY2PG;
  471         conf.mem[0].base += ktop;
  472         conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;
  473 
  474         /*
  475          * Fix up the bank we found to be the remnant, below the kernel.
  476          * This, and the other banks, will be passed to xhole() later.
  477          * BUG: conf.upages needs to be adjusted, but how?  In practice,
  478          * we only have 1 bank, and the remnant is small.
  479          */
  480         b->max = (uvlong)_main & ~(BY2PG-1);
  481 
  482         conf.nmach = 1;
  483         conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
  484         if(cpuserver)
  485                 conf.nproc *= 3;
  486         if(conf.nproc > 2000)
  487                 conf.nproc = 2000;
  488         conf.nimage = 200;
  489         conf.nswap = conf.nproc*80;
  490         conf.nswppo = 4096;
  491         conf.copymode = 0;                      /* copy on write */
  492 
  493         if(cpuserver) {
  494                 if(userpcnt < 10)
  495                         userpcnt = 70;
  496                 kpages = conf.npage - (conf.npage*userpcnt)/100;
  497 
  498                 /*
  499                  * Hack for the big boys. Only good while physmem < 4GB.
  500                  * Give the kernel a max. of 16MB + enough to allocate the
  501                  * page pool.
  502                  * This is an overestimate as conf.upages < conf.npages.
  503                  * The patch of nimage is a band-aid, scanning the whole
  504                  * page list in imagereclaim just takes too long.
  505                  */
  506                 if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){
  507                         kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG;
  508                         conf.nimage = 2000;
  509                         kpages += (conf.nproc*KSTACK)/BY2PG;
  510                 }
  511         } else {
  512                 if(userpcnt < 10) {
  513                         if(conf.npage*BY2PG < 16*MB)
  514                                 userpcnt = 40;
  515                         else
  516                                 userpcnt = 60;
  517                 }
  518                 kpages = conf.npage - (conf.npage*userpcnt)/100;
  519 
  520                 /*
  521                  * Make sure terminals with low memory get at least
  522                  * 4MB on the first Image chunk allocation.
  523                  */
  524                 if(conf.npage*BY2PG < 16*MB)
  525                         imagmem->minarena = 4*1024*1024;
  526         }
  527         conf.upages = conf.npage - kpages;
  528         conf.ialloc = (kpages/2)*BY2PG;
  529 
  530         /*
  531          * Guess how much is taken by the large permanent
  532          * datastructures. Mntcache and Mntrpc are not accounted for
  533          * (probably ~300KB).
  534          */
  535         kpages *= BY2PG;
  536         kpages -= conf.upages*sizeof(Page)
  537                 + conf.nproc*sizeof(Proc)
  538                 + conf.nimage*sizeof(Image)
  539                 + conf.nswap
  540                 + conf.nswppo*sizeof(Page);
  541         mainmem->maxsize = kpages;
  542         if(!cpuserver){
  543                 /*
  544                  * give terminals lots of image memory, too; the dynamic
  545                  * allocation will balance the load properly, hopefully.
  546                  * be careful with 32-bit overflow.
  547                  */
  548                 imagmem->maxsize = kpages;
  549         }
  550 
  551 //      conf.monitor = 1;       /* BUG */
  552 }
  553 
  554 void
  555 memholes(void)
  556 {
  557         Bank *b, *eb;
  558 
  559         b = bootconf->bank;
  560         eb = b+bootconf->nbank;
  561         while(b < eb) {
  562                 if(b->min < (1LL<<32) && b->max < (1LL<<32))
  563                         xhole(b->min, b->max-b->min);
  564                 b++;
  565         }
  566 }
  567 
  568 char *sp;
  569 
  570 char *
  571 pusharg(char *p)
  572 {
  573         int n;
  574 
  575         n = strlen(p)+1;
  576         sp -= n;
  577         memmove(sp, p, n);
  578         return sp;
  579 }
  580 
  581 void
  582 arginit(void)
  583 {
  584         char **av;
  585 
  586         av = (char**)argbuf;
  587         sp = argbuf + sizeof(argbuf);
  588         *av++ = pusharg("boot");
  589         *av = 0;
  590 }
  591 
  592 char *
  593 getconf(char *name)
  594 {
  595         int n;
  596 
  597         for(n = 0; n < nconf; n++)
  598                 if(cistrcmp(confname[n], name) == 0) {
  599                         return confval[n];
  600                 }
  601         return 0;
  602 }
  603 
  604 int
  605 isaconfig(char *class, int ctlrno, ISAConf *isa)
  606 {
  607         char cc[32], *p;
  608         int i, n;
  609 
  610         snprint(cc, sizeof cc, "%s%d", class, ctlrno);
  611         for(n = 0; n < nconf; n++){
  612                 if(cistrcmp(confname[n], cc) != 0)
  613                         continue;
  614                 isa->nopt = tokenize(confval[n], isa->opt, NISAOPT);
  615                 for(i = 0; i < isa->nopt; i++){
  616                         p = isa->opt[i];
  617                         if(cistrncmp(p, "type=", 5) == 0)
  618                                 isa->type = p + 5;
  619                         else if(cistrncmp(p, "port=", 5) == 0)
  620                                 isa->port = strtoul(p+5, &p, 0);
  621                         else if(cistrncmp(p, "irq=", 4) == 0)
  622                                 isa->irq = strtoul(p+4, &p, 0);
  623                         else if(cistrncmp(p, "dma=", 4) == 0)
  624                                 isa->dma = strtoul(p+4, &p, 0);
  625                         else if(cistrncmp(p, "mem=", 4) == 0)
  626                                 isa->mem = strtoul(p+4, &p, 0);
  627                         else if(cistrncmp(p, "size=", 5) == 0)
  628                                 isa->size = strtoul(p+5, &p, 0);
  629                         else if(cistrncmp(p, "freq=", 5) == 0)
  630                                 isa->freq = strtoul(p+5, &p, 0);
  631                 }
  632                 return 1;
  633         }
  634         return 0;
  635 }
  636 
  637 int
  638 cistrcmp(char *a, char *b)
  639 {
  640         int ac, bc;
  641 
  642         for(;;){
  643                 ac = *a++;
  644                 bc = *b++;
  645 
  646                 if(ac >= 'A' && ac <= 'Z')
  647                         ac = 'a' + (ac - 'A');
  648                 if(bc >= 'A' && bc <= 'Z')
  649                         bc = 'a' + (bc - 'A');
  650                 ac -= bc;
  651                 if(ac)
  652                         return ac;
  653                 if(bc == 0)
  654                         break;
  655         }
  656         return 0;
  657 }
  658 
  659 int
  660 cistrncmp(char *a, char *b, int n)
  661 {
  662         unsigned ac, bc;
  663 
  664         while(n > 0){
  665                 ac = *a++;
  666                 bc = *b++;
  667                 n--;
  668 
  669                 if(ac >= 'A' && ac <= 'Z')
  670                         ac = 'a' + (ac - 'A');
  671                 if(bc >= 'A' && bc <= 'Z')
  672                         bc = 'a' + (bc - 'A');
  673 
  674                 ac -= bc;
  675                 if(ac)
  676                         return ac;
  677                 if(bc == 0)
  678                         break;
  679         }
  680 
  681         return 0;
  682 }
  683 
  684 int
  685 getcfields(char* lp, char** fields, int n, char* sep)
  686 {
  687         int i;
  688 
  689         for(i = 0; lp && *lp && i < n; i++){
  690                 while(*lp && strchr(sep, *lp) != 0)
  691                         *lp++ = 0;
  692                 if(*lp == 0)
  693                         break;
  694                 fields[i] = lp;
  695                 while(*lp && strchr(sep, *lp) == 0){
  696                         if(*lp == '\\' && *(lp+1) == '\n')
  697                                 *lp++ = ' ';
  698                         lp++;
  699                 }
  700         }
  701 
  702         return i;
  703 }

Cache object: 6ce7b0fe2309eedafd10c7d8294426d1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.