linux-old/arch/i386/kernel/setup.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/i386/kernel/setup.c
   3 *
   4 *  Copyright (C) 1995  Linus Torvalds
   5 */
   6
   7/*
   8 * This file handles the architecture-dependent parts of initialization
   9 */
  10
  11#include <linux/errno.h>
  12#include <linux/sched.h>
  13#include <linux/kernel.h>
  14#include <linux/mm.h>
  15#include <linux/stddef.h>
  16#include <linux/unistd.h>
  17#include <linux/ptrace.h>
  18#include <linux/malloc.h>
  19#include <linux/ldt.h>
  20#include <linux/user.h>
  21#include <linux/a.out.h>
  22#include <linux/tty.h>
  23#include <linux/ioport.h>
  24#include <linux/delay.h>
  25#include <linux/config.h>
  26#ifdef CONFIG_APM
  27#include <linux/apm_bios.h>
  28#endif
  29#ifdef CONFIG_BLK_DEV_RAM
  30#include <linux/blk.h>
  31#endif
  32#include <asm/segment.h>
  33#include <asm/system.h>
  34#include <asm/smp.h>
  35#include <asm/io.h>
  36
  37/*
  38 * Tell us the machine setup..
  39 */
  40char hard_math = 0;             /* set by kernel/head.S */
  41char x86 = 0;                   /* set by kernel/head.S to 3..6 */
  42char x86_model = 0;             /* set by kernel/head.S */
  43char x86_mask = 0;              /* set by kernel/head.S */
  44int x86_capability = 0;         /* set by kernel/head.S */
  45int x86_ext_capability = 0;     /* newer CPUs have this */
  46int fdiv_bug = 0;               /* set if Pentium(TM) with FP bug */
  47int pentium_f00f_bug = 0;       /* set if Pentium(TM) with F00F bug */
  48int have_cpuid = 0;             /* set if CPUID instruction works */
  49int ext_cpuid = 0;              /* if != 0, highest available CPUID value */
  50
  51char x86_vendor_id[13] = "GenuineIntel";/* default */
  52
  53static char *Cx86_step = "unknown";     /* stepping info for Cyrix CPUs */
  54
  55static unsigned char Cx86_mult = 0;     /* clock multiplier for Cyrix CPUs */
  56
  57static const char *x86_clkmult[] = {
  58        "unknown", "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5", "5.5",
  59        "6", "6.5", "7", "7.5", "8"
  60        };
  61
  62char ignore_irq13 = 0;          /* set if exception 16 works */
  63char wp_works_ok = -1;          /* set if paging hardware honours WP */ 
  64char hlt_works_ok = 1;          /* set if the "hlt" instruction works */
  65
  66/*
  67 * Bus types ..
  68 */
  69int EISA_bus = 0;
  70
  71/*
  72 * Setup options
  73 */
  74struct drive_info_struct { char dummy[32]; } drive_info;
  75struct screen_info screen_info;
  76#ifdef CONFIG_APM
  77struct apm_bios_info apm_bios_info;
  78#endif
  79
  80unsigned char aux_device_present;
  81
  82#ifdef CONFIG_BLK_DEV_RAM
  83extern int rd_doload;           /* 1 = load ramdisk, 0 = don't load */
  84extern int rd_prompt;           /* 1 = prompt for ramdisk, 0 = don't prompt */
  85extern int rd_image_start;      /* starting block # of image */
  86#endif
  87
  88extern int root_mountflags;
  89extern int _etext, _edata, _end;
  90
  91extern char empty_zero_page[PAGE_SIZE];
  92
  93/*
  94 * This is set up by the setup-routine at boot-time
  95 */
  96#define PARAM   empty_zero_page
  97#define EXT_MEM_K (*(unsigned short *) (PARAM+2))
  98#ifndef STANDARD_MEMORY_BIOS_CALL
  99#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
 100#endif
 101#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40))
 102#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
 103#define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
 104#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
 105#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8))
 106#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC))
 107#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))
 108#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))
 109#define KERNEL_START (*(unsigned long *) (PARAM+0x214))
 110#define INITRD_START (*(unsigned long *) (PARAM+0x218))
 111#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
 112#define COMMAND_LINE ((char *) (PARAM+2048))
 113#define COMMAND_LINE_SIZE 256
 114
 115#define RAMDISK_IMAGE_START_MASK        0x07FF
 116#define RAMDISK_PROMPT_FLAG             0x8000
 117#define RAMDISK_LOAD_FLAG               0x4000  
 118
 119static char command_line[COMMAND_LINE_SIZE] = { 0, };
 120       char saved_command_line[COMMAND_LINE_SIZE];
 121
 122void setup_arch(char **cmdline_p,
 123        unsigned long * memory_start_p, unsigned long * memory_end_p)
 124{
 125        unsigned long memory_start, memory_end;
 126#ifndef STANDARD_MEMORY_BIOS_CALL
 127        unsigned long memory_alt_end;
 128#endif
 129        char c = ' ', *to = command_line, *from = COMMAND_LINE;
 130        int len = 0;
 131        static unsigned char smptrap=0;
 132
 133        if(smptrap==1)
 134        {
 135                return;
 136        }
 137        smptrap=1;
 138
 139        ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
 140        drive_info = DRIVE_INFO;
 141        screen_info = SCREEN_INFO;
 142#ifdef CONFIG_APM
 143        apm_bios_info = APM_BIOS_INFO;
 144#endif
 145        aux_device_present = AUX_DEVICE_INFO;
 146        memory_end = (1<<20) + (EXT_MEM_K<<10);
 147#ifndef STANDARD_MEMORY_BIOS_CALL
 148        memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
 149        if (memory_alt_end > memory_end) {
 150            printk("Memory: sized by int13 0e801h\n");
 151            memory_end = memory_alt_end;
 152        }
 153        else
 154            printk("Memory: sized by int13 088h\n");
 155#endif
 156        memory_end &= PAGE_MASK;
 157#ifdef CONFIG_BLK_DEV_RAM
 158        rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
 159        rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
 160        rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
 161#endif
 162#ifdef CONFIG_MAX_16M
 163        if (memory_end > 16*1024*1024)
 164                memory_end = 16*1024*1024;
 165#endif
 166
 167        /*
 168         *      The CONFIG_MAX_MEMSIZE sanity checker.
 169         */
 170         
 171        if (memory_end > (CONFIG_MAX_MEMSIZE-128)*1024*1024)
 172        {
 173                memory_end = (CONFIG_MAX_MEMSIZE-128)*1024*1024;
 174                printk(KERN_WARNING "ONLY %dMB RAM will be used, see Documentation/more-than-900MB-RAM.txt!.\n", CONFIG_MAX_MEMSIZE-128);
 175                udelay(3*1000*1000);
 176        }
 177        
 178        if (!MOUNT_ROOT_RDONLY)
 179                root_mountflags &= ~MS_RDONLY;
 180        memory_start = (unsigned long) &_end;
 181        init_task.mm->start_code = TASK_SIZE;
 182        init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
 183        init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata;
 184        init_task.mm->brk = TASK_SIZE + (unsigned long) &_end;
 185
 186        /* Save unparsed command line copy for /proc/cmdline */
 187        memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
 188        saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
 189
 190        for (;;) {
 191                /*
 192                 * "mem=nopentium" disables the 4MB page tables.
 193                 * "mem=XXX[kKmM]" overrides the BIOS-reported
 194                 * memory size
 195                 */
 196                if (c == ' ' && *(const unsigned long *)from == *(const unsigned long *)"mem=") {
 197                        if (to != command_line) to--;
 198                        if (!memcmp(from+4, "nopentium", 9)) {
 199                                from += 9+4;
 200                                x86_capability &= ~8;
 201                        } else {
 202                                memory_end = simple_strtoul(from+4, &from, 0);
 203                                if ( *from == 'K' || *from == 'k' ) {
 204                                        memory_end = memory_end << 10;
 205                                        from++;
 206                                } else if ( *from == 'M' || *from == 'm' ) {
 207                                        memory_end = memory_end << 20;
 208                                        from++;
 209                                }
 210                        }
 211                }
 212                c = *(from++);
 213                if (!c)
 214                        break;
 215                if (COMMAND_LINE_SIZE <= ++len)
 216                        break;
 217                *(to++) = c;
 218        }
 219        *to = '\0';
 220        *cmdline_p = command_line;
 221        *memory_start_p = memory_start;
 222        *memory_end_p = memory_end;
 223
 224#ifdef CONFIG_BLK_DEV_INITRD
 225        if (LOADER_TYPE) {
 226                initrd_start = INITRD_START;
 227                initrd_end = INITRD_START+INITRD_SIZE;
 228                if (initrd_end > memory_end) {
 229                        printk("initrd extends beyond end of memory "
 230                            "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
 231                            initrd_end,memory_end);
 232                        initrd_start = 0;
 233                }
 234        }
 235#endif
 236
 237        /* request io space for devices used on all i[345]86 PC'S */
 238        request_region(0x00,0x20,"dma1");
 239        request_region(0x40,0x20,"timer");
 240        request_region(0x80,0x20,"dma page reg");
 241        request_region(0xc0,0x20,"dma2");
 242        request_region(0xf0,0x10,"npu");
 243}
 244
 245static const char * IDTmodel(void)
 246/* Right now IDT has a single CPU model in production: the C6.
 247 * Adjust this when IDT/Centaur comes out with a new CPU model.
 248 * Stepping information is correctly reported in x86_mask.
 249 */
 250{
 251        static const char *model[] = {
 252                "C6", "C6-3D"
 253        };
 254        return model[0];
 255}
 256
 257static const char * Cx86model(void)
 258/* We know our CPU is a Cyrix now (see bugs.h), so we can use the DIR0/DIR1
 259 * mechanism to figure out the model, bus clock multiplier and stepping.
 260 * For the newest CPUs (GXm and MXi) we use the Extended CPUID function.
 261 */
 262{
 263    unsigned char nr6x86 = 0;
 264    unsigned char cx_dir0 = 0;  /* Model and bus clock multiplier */
 265    unsigned char cx_dir1 = 0;  /* Stepping info */
 266    unsigned int flags;
 267    static const char *model[] = {
 268        "unknown", "Cx486", "5x86", "MediaGX", "6x86", "6x86L", "6x86MX",
 269        "M II"
 270    };
 271    
 272    if (x86_model == -1) {      /* is this an old Cx486 without DIR0/DIR1? */
 273        nr6x86 = 1;             /* Cx486 */
 274        Cx86_mult = 0;          /* unknown multiplier */
 275    }
 276    else {
 277
 278        /* Get DIR0, DIR1 since all other Cyrix CPUs have them */
 279    
 280        save_flags(flags);
 281        cli();
 282        cx_dir0 = getCx86(CX86_DIR0);   /* we use the access macros */
 283        cx_dir1 = getCx86(CX86_DIR1);   /* defined in processor.h */
 284        restore_flags(flags);
 285                                        
 286        /* Now cook; the recipe is by Channing Corn, from Cyrix.
 287         * We do the same thing for each generation: we work out
 288         * the model, multiplier and stepping.
 289         */
 290         
 291        if (cx_dir0 < 0x20) {
 292                nr6x86 = 1;                             /* Cx486 */
 293                Cx86_mult = 0;                          /* unknown multiplier */
 294                sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
 295        }
 296        
 297        if ((cx_dir0 > 0x20) && (cx_dir0 < 0x30)) {
 298                nr6x86 = 2;                             /* 5x86 */
 299                Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */
 300                sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
 301        }
 302        
 303        if ((cx_dir0 >= 0x30) && (cx_dir0 < 0x38)) {
 304                nr6x86 = ((x86_capability & (1 << 8)) ? 5 : 4); /* 6x86(L) */
 305                Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */
 306                sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 3), cx_dir1 & 0x0f);
 307        }
 308        
 309        if ((cx_dir0 >= 0x40) && (cx_dir0 < 0x50)) {
 310            if (x86 == 4) { /* MediaGX */
 311                nr6x86 = 3;
 312                Cx86_mult = ((cx_dir0 & 0x01) ? 5 : 7); /* either 3x or 4x */
 313                switch (cx_dir1 >> 4) {
 314                        case (0) :
 315                        case (1) :
 316                                sprintf(Cx86_step, "2.%d", cx_dir1 & 0x0f);
 317                        break;
 318                        case (2) :
 319                                sprintf(Cx86_step, "1.%d", cx_dir1 & 0x0f);
 320                        break;
 321                        default  :
 322                        break;
 323                }
 324            } /* endif MediaGX */
 325            if (x86 == 5) { /* GXm */
 326                char GXm_mult[8] = {7,11,7,11,13,15,13,9}; /* 4 to 8 */
 327                ext_cpuid = 0x80000005; /* available */
 328                Cx86_mult = GXm_mult[cx_dir0 & 0x0f];
 329                sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) - 1, cx_dir1 & 0x0f);
 330            } /* endif GXm */
 331        }
 332        
 333        if ((cx_dir0 >= 0x50) && (cx_dir0 < 0x60)) {
 334                nr6x86 = ((cx_dir1 > 7) ? 7 : 6);       /* 6x86Mx or M II */
 335                Cx86_mult = (cx_dir0 & 0x07) + 2;       /* 2 to 5 in 0.5 steps */
 336                if (((cx_dir1 & 0x0f) > 4) || ((cx_dir1 >> 4) == 2)) cx_dir1 += 0x10;
 337                sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f);
 338        }
 339    }
 340    x86_mask = 1;       /* we don't use it, but has to be set to something */
 341    return model[nr6x86];
 342}
 343
 344struct cpu_model_info {
 345        int cpu_x86;
 346        char *model_names[16];
 347};
 348
 349static struct cpu_model_info amd_models[] = {
 350        { 4,
 351          { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4",
 352            "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }},
 353        { 5,
 354          { "K5/SSA5 (PR-75, PR-90, PR-100)"}},
 355};
 356
 357static const char * AMDmodel(void)
 358{
 359        const char *p=NULL;
 360        int i;
 361        
 362        if ((x86_model == 0) || (x86 == 4)) {
 363                for (i=0; i<sizeof(amd_models)/sizeof(struct cpu_model_info); i++)
 364                        if (amd_models[i].cpu_x86 == x86) {
 365                                p = amd_models[i].model_names[(int)x86_model];
 366                                break;
 367                        }
 368        }
 369        else ext_cpuid = 0x80000005;            /* available */
 370        return p;
 371}
 372
 373static struct cpu_model_info intel_models[] = {
 374        { 4,
 375          { "486 DX-25/33", "486 DX-50", "486 SX", "486 DX/2", "486 SL", 
 376            "486 SX/2", NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, 
 377            NULL, NULL, NULL, NULL, NULL }},
 378        { 5,
 379          { "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75+",
 380            "OverDrive PODP5V83", "Pentium MMX", NULL, NULL,
 381            "Mobile Pentium 75+", "Mobile Pentium MMX", NULL, NULL, NULL,
 382            NULL, NULL, NULL, NULL }},
 383        { 6,
 384          { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", 
 385            NULL, "Pentium II (Deschutes)", "Celeron (Mendocino)", NULL, NULL, NULL, NULL, NULL,
 386            NULL, NULL, NULL, NULL }},
 387};
 388
 389static const char * Intelmodel(void)
 390{
 391        const char *p = "386 SX/DX";    /* default to a 386 */
 392        int i;
 393        
 394        /*
 395         *      Old 486SX has no CPU ID. Set the model to 2 for this
 396         *      case.
 397         */
 398         
 399        if( x86==4 && x86_model == 0 && hard_math == 0)
 400                x86_model = 2;
 401        
 402        for (i=0; i<sizeof(intel_models)/sizeof(struct cpu_model_info); i++)
 403                if (intel_models[i].cpu_x86 == x86) {
 404                        p = intel_models[i].model_names[(int)x86_model];
 405                        break;
 406                }
 407        
 408        
 409        return p;
 410}
 411        
 412/* Recent Intel CPUs have an EEPROM and a ROM with CPU information. We'll use
 413 * this information in future versions of this code.
 414 * AMD and more recently Cyrix have decided to standardize on an extended
 415 * cpuid mechanism for their CPUs.
 416 */
 417 
 418static const char * get_cpu_mkt_name(void)
 419{
 420        static char mktbuf[48];
 421        int dummy;
 422        unsigned int *v;
 423        v = (unsigned int *) mktbuf;
 424        cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);  /* name, flags */
 425        cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
 426        cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
 427        cpuid(0x80000001, &dummy, &dummy, &dummy, &x86_ext_capability);
 428        return mktbuf;
 429}
 430
 431static const char * getmodel(void)
 432/* Default is Intel. We disregard Nexgen processors. */
 433{
 434        const char *p = NULL;
 435        if      (strcmp(x86_vendor_id, "AuthenticAMD") == 0)    /* AuthenticAMD */
 436                p = AMDmodel();
 437        else if (strcmp(x86_vendor_id, "CyrixInstead") == 0)    /* CyrixInstead */
 438                p = Cx86model();
 439        else if (strcmp(x86_vendor_id, "CentaurHauls") == 0)    /* CentaurHauls */
 440                p = IDTmodel();
 441        /* This isnt quite right */
 442        else if (strcmp(x86_vendor_id, "UMC UMC UMC ") == 0)    /* UMC */
 443                p = Intelmodel();
 444        else /* default - this could be anyone */
 445                p = Intelmodel();
 446        if (ext_cpuid)
 447                return get_cpu_mkt_name();
 448        else
 449                return p;
 450}
 451
 452int get_cpuinfo(char * buffer)
 453{
 454        int i, len = 0;
 455        static const char *x86_cap_flags[] = {
 456                "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
 457                "cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
 458                "16", "17", "18", "19", "20", "21", "22", "mmx",
 459                "24", "25", "26", "27", "28", "29", "30", "31"
 460        };
 461        static const char *x86_ext_cap_flags[] = {
 462                   "fpu","vme", "de",   "pse", "tsc", "msr",  "6",   "mce",
 463                   "cx8",  "9", "10", "syscr",  "12", "pge", "14",  "cmov",
 464                "fpcmov", "17", "psn",    "19",  "20",  "21", "22",   "mmx",
 465                  "emmx", "25", "26",    "27",  "28",  "29", "30", "3dnow"
 466        };
 467         
 468#ifdef __SMP__
 469        int n;
 470
 471#define CD(X)           (cpu_data[n].X)
 472/* SMP has the wrong name for loops_per_sec */
 473#define loops_per_sec   udelay_val
 474#define CPUN n
 475
 476        for ( n = 0 ; n < 32 ; n++ ) {
 477                if ( cpu_present_map & (1<<n) ) {
 478                        if (len) buffer[len++] = '\n'; 
 479
 480#else
 481#define CD(X) (X)
 482#define CPUN 0
 483#endif
 484
 485                        len += sprintf(buffer+len,"processor\t: %d\n"
 486                                       "cpu\t\t: %c86\n"
 487                                       "model\t\t: %s",
 488                                       CPUN,
 489                                       CD(x86)+'0',
 490                                       getmodel());
 491                        len += sprintf(buffer+len,
 492                                       "\nvendor_id\t: %s\n",
 493                                       x86_vendor_id);
 494        
 495                        if (CD(x86_mask) || have_cpuid)
 496                                if      ((strncmp(x86_vendor_id, "Au", 2) == 0)
 497                                        && (x86_model >= 6)) {
 498                                        len += sprintf(buffer+len,
 499                                                       "stepping\t: %c\n",
 500                                                       x86_mask + 'A');
 501                                }
 502                                else if (strncmp(x86_vendor_id, "Cy", 2) == 0) {
 503                                        len += sprintf(buffer+len,
 504                                                        "stepping\t: %s, core/bus clock ratio: %sx\n",
 505                                                        Cx86_step, x86_clkmult[Cx86_mult]);
 506                                }
 507                                else {  
 508                                        len += sprintf(buffer+len,
 509                                                       "stepping\t: %d\n",
 510                                                       CD(x86_mask));
 511                                }
 512                        else
 513                                len += sprintf(buffer+len, 
 514                                               "stepping\t: unknown\n");
 515        
 516                        len += sprintf(buffer+len,
 517                                       "fdiv_bug\t: %s\n"
 518                                       "hlt_bug\t\t: %s\n"
 519                                       "f00f_bug\t: %s\n"
 520                                       "fpu\t\t: %s\n"
 521                                       "fpu_exception\t: %s\n"
 522                                       "cpuid\t\t: %s\n"
 523                                       "wp\t\t: %s\n"
 524                                       "flags\t\t:",
 525                                       CD(fdiv_bug) ? "yes" : "no",
 526                                       CD(hlt_works_ok) ? "no" : "yes",
 527                                       pentium_f00f_bug ? "yes" : "no",
 528                                       CD(hard_math) ? "yes" : "no",
 529                                       (CD(hard_math) && ignore_irq13)
 530                                         ? "yes" : "no",
 531                                       CD(have_cpuid) ? "yes" : "no",
 532                                       CD(wp_works_ok) ? "yes" : "no");
 533        
 534                        for ( i = 0 ; i < 32 ; i++ ) {
 535                                if ( CD(x86_capability) & (1 << i) ) {
 536                                        len += sprintf(buffer+len, " %s",
 537                                                       x86_cap_flags[i]);
 538                                }
 539                                else if ( CD(x86_ext_capability) & (1 << i) ) {
 540                                        len += sprintf(buffer+len, " %s",
 541                                                       x86_ext_cap_flags[i]);
 542                                }
 543                        }
 544                        len += sprintf(buffer+len,
 545                                       "\nbogomips\t: %lu.%02lu\n",
 546                                       CD(loops_per_sec+2500)/500000,
 547                                       (CD(loops_per_sec+2500)/5000) % 100);
 548#ifdef __SMP__
 549                }
 550        }
 551#endif
 552        return len;
 553}
 554
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.