/* * linux/arch/i386/kernel/setup.c * * Copyright (C) 1995 Linus Torvalds */ /* * This file handles the architecture-dependent parts of initialization */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_APM #include #endif #ifdef CONFIG_BLK_DEV_RAM #include #endif #include #include #include #include /* * Tell us the machine setup.. */ char hard_math = 0; /* set by kernel/head.S */ char x86 = 0; /* set by kernel/head.S to 3..6 */ char x86_model = 0; /* set by kernel/head.S */ char x86_mask = 0; /* set by kernel/head.S */ int x86_capability = 0; /* set by kernel/head.S */ int x86_ext_capability = 0; /* newer CPUs have this */ int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */ int have_cpuid = 0; /* set if CPUID instruction works */ int ext_cpuid = 0; /* if != 0, highest available CPUID value */ char x86_vendor_id[13] = "GenuineIntel";/* default */ static char *Cx86_step = "unknown"; /* stepping info for Cyrix CPUs */ static unsigned char Cx86_mult = 0; /* clock multiplier for Cyrix CPUs */ static const char *x86_clkmult[] = { "unknown", "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5", "5.5", "6", "6.5", "7", "7.5", "8" }; char ignore_irq13 = 0; /* set if exception 16 works */ char wp_works_ok = -1; /* set if paging hardware honours WP */ char hlt_works_ok = 1; /* set if the "hlt" instruction works */ /* * Bus types .. */ int EISA_bus = 0; /* * Setup options */ struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info; #ifdef CONFIG_APM struct apm_bios_info apm_bios_info; #endif unsigned char aux_device_present; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ extern int rd_image_start; /* starting block # of image */ #endif extern int root_mountflags; extern int _etext, _edata, _end; extern char empty_zero_page[PAGE_SIZE]; /* * This is set up by the setup-routine at boot-time */ #define PARAM empty_zero_page #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) #ifndef STANDARD_MEMORY_BIOS_CALL #define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) #endif #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+0x40)) #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) #define SCREEN_INFO (*(struct screen_info *) (PARAM+0)) #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) #define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) #define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) #define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) #define LOADER_TYPE (*(unsigned char *) (PARAM+0x210)) #define KERNEL_START (*(unsigned long *) (PARAM+0x214)) #define INITRD_START (*(unsigned long *) (PARAM+0x218)) #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) #define COMMAND_LINE ((char *) (PARAM+2048)) #define COMMAND_LINE_SIZE 256 #define RAMDISK_IMAGE_START_MASK 0x07FF #define RAMDISK_PROMPT_FLAG 0x8000 #define RAMDISK_LOAD_FLAG 0x4000 static char command_line[COMMAND_LINE_SIZE] = { 0, }; char saved_command_line[COMMAND_LINE_SIZE]; void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) { unsigned long memory_start, memory_end; #ifndef STANDARD_MEMORY_BIOS_CALL unsigned long memory_alt_end; #endif char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; static unsigned char smptrap=0; if(smptrap==1) { return; } smptrap=1; ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; #ifdef CONFIG_APM apm_bios_info = APM_BIOS_INFO; #endif aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); #ifndef STANDARD_MEMORY_BIOS_CALL memory_alt_end = (1<<20) + (ALT_MEM_K<<10); if (memory_alt_end > memory_end) { printk("Memory: sized by int13 0e801h\n"); memory_end = memory_alt_end; } else printk("Memory: sized by int13 088h\n"); #endif memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); #endif #ifdef CONFIG_MAX_16M if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; #endif /* * The CONFIG_MAX_MEMSIZE sanity checker. */ if (memory_end > (CONFIG_MAX_MEMSIZE-128)*1024*1024) { memory_end = (CONFIG_MAX_MEMSIZE-128)*1024*1024; printk(KERN_WARNING "ONLY %dMB RAM will be used, see Documentation/more-than-900MB-RAM.txt!.\n", CONFIG_MAX_MEMSIZE-128); udelay(3*1000*1000); } if (!MOUNT_ROOT_RDONLY) root_mountflags &= ~MS_RDONLY; memory_start = (unsigned long) &_end; init_task.mm->start_code = TASK_SIZE; init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext; init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata; init_task.mm->brk = TASK_SIZE + (unsigned long) &_end; /* Save unparsed command line copy for /proc/cmdline */ memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; for (;;) { /* * "mem=nopentium" disables the 4MB page tables. * "mem=XXX[kKmM]" overrides the BIOS-reported * memory size */ if (c == ' ' && *(const unsigned long *)from == *(const unsigned long *)"mem=") { if (to != command_line) to--; if (!memcmp(from+4, "nopentium", 9)) { from += 9+4; x86_capability &= ~8; } else { memory_end = simple_strtoul(from+4, &from, 0); if ( *from == 'K' || *from == 'k' ) { memory_end = memory_end << 10; from++; } else if ( *from == 'M' || *from == 'm' ) { memory_end = memory_end << 20; from++; } } } c = *(from++); if (!c) break; if (COMMAND_LINE_SIZE <= ++len) break; *(to++) = c; } *to = '\0'; *cmdline_p = command_line; *memory_start_p = memory_start; *memory_end_p = memory_end; #ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE) { initrd_start = INITRD_START; initrd_end = INITRD_START+INITRD_SIZE; if (initrd_end > memory_end) { printk("initrd extends beyond end of memory " "(0x%08lx > 0x%08lx)\ndisabling initrd\n", initrd_end,memory_end); initrd_start = 0; } } #endif /* request io space for devices used on all i[345]86 PC'S */ request_region(0x00,0x20,"dma1"); request_region(0x40,0x20,"timer"); request_region(0x80,0x20,"dma page reg"); request_region(0xc0,0x20,"dma2"); request_region(0xf0,0x10,"npu"); } static const char * IDTmodel(void) /* Right now IDT has a single CPU model in production: the C6. * Adjust this when IDT/Centaur comes out with a new CPU model. * Stepping information is correctly reported in x86_mask. */ { static const char *model[] = { "C6", "C6-3D" }; return model[0]; } static const char * Cx86model(void) /* We know our CPU is a Cyrix now (see bugs.h), so we can use the DIR0/DIR1 * mechanism to figure out the model, bus clock multiplier and stepping. * For the newest CPUs (GXm and MXi) we use the Extended CPUID function. */ { unsigned char nr6x86 = 0; unsigned char cx_dir0 = 0; /* Model and bus clock multiplier */ unsigned char cx_dir1 = 0; /* Stepping info */ unsigned int flags; static const char *model[] = { "unknown", "Cx486", "5x86", "MediaGX", "6x86", "6x86L", "6x86MX", "M II" }; if (x86_model == -1) { /* is this an old Cx486 without DIR0/DIR1? */ nr6x86 = 1; /* Cx486 */ Cx86_mult = 0; /* unknown multiplier */ } else { /* Get DIR0, DIR1 since all other Cyrix CPUs have them */ save_flags(flags); cli(); cx_dir0 = getCx86(CX86_DIR0); /* we use the access macros */ cx_dir1 = getCx86(CX86_DIR1); /* defined in processor.h */ restore_flags(flags); /* Now cook; the recipe is by Channing Corn, from Cyrix. * We do the same thing for each generation: we work out * the model, multiplier and stepping. */ if (cx_dir0 < 0x20) { nr6x86 = 1; /* Cx486 */ Cx86_mult = 0; /* unknown multiplier */ sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); } if ((cx_dir0 > 0x20) && (cx_dir0 < 0x30)) { nr6x86 = 2; /* 5x86 */ Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */ sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); } if ((cx_dir0 >= 0x30) && (cx_dir0 < 0x38)) { nr6x86 = ((x86_capability & (1 << 8)) ? 5 : 4); /* 6x86(L) */ Cx86_mult = ((cx_dir0 & 0x04) ? 5 : 3); /* either 3x or 2x */ sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 3), cx_dir1 & 0x0f); } if ((cx_dir0 >= 0x40) && (cx_dir0 < 0x50)) { if (x86 == 4) { /* MediaGX */ nr6x86 = 3; Cx86_mult = ((cx_dir0 & 0x01) ? 5 : 7); /* either 3x or 4x */ switch (cx_dir1 >> 4) { case (0) : case (1) : sprintf(Cx86_step, "2.%d", cx_dir1 & 0x0f); break; case (2) : sprintf(Cx86_step, "1.%d", cx_dir1 & 0x0f); break; default : break; } } /* endif MediaGX */ if (x86 == 5) { /* GXm */ char GXm_mult[8] = {7,11,7,11,13,15,13,9}; /* 4 to 8 */ ext_cpuid = 0x80000005; /* available */ Cx86_mult = GXm_mult[cx_dir0 & 0x0f]; sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) - 1, cx_dir1 & 0x0f); } /* endif GXm */ } if ((cx_dir0 >= 0x50) && (cx_dir0 < 0x60)) { nr6x86 = ((cx_dir1 > 7) ? 7 : 6); /* 6x86Mx or M II */ Cx86_mult = (cx_dir0 & 0x07) + 2; /* 2 to 5 in 0.5 steps */ if (((cx_dir1 & 0x0f) > 4) || ((cx_dir1 >> 4) == 2)) cx_dir1 += 0x10; sprintf(Cx86_step, "%d.%d", (cx_dir1 >> 4) + 1, cx_dir1 & 0x0f); } } x86_mask = 1; /* we don't use it, but has to be set to something */ return model[nr6x86]; } struct cpu_model_info { int cpu_x86; char *model_names[16]; }; static struct cpu_model_info amd_models[] = { { 4, { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4", "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }}, { 5, { "K5/SSA5 (PR-75, PR-90, PR-100)"}}, }; static const char * AMDmodel(void) { const char *p=NULL; int i; if ((x86_model == 0) || (x86 == 4)) { for (i=0; i= 6)) { len += sprintf(buffer+len, "stepping\t: %c\n", x86_mask + 'A'); } else if (strncmp(x86_vendor_id, "Cy", 2) == 0) { len += sprintf(buffer+len, "stepping\t: %s, core/bus clock ratio: %sx\n", Cx86_step, x86_clkmult[Cx86_mult]); } else { len += sprintf(buffer+len, "stepping\t: %d\n", CD(x86_mask)); } else len += sprintf(buffer+len, "stepping\t: unknown\n"); len += sprintf(buffer+len, "fdiv_bug\t: %s\n" "hlt_bug\t\t: %s\n" "f00f_bug\t: %s\n" "fpu\t\t: %s\n" "fpu_exception\t: %s\n" "cpuid\t\t: %s\n" "wp\t\t: %s\n" "flags\t\t:", CD(fdiv_bug) ? "yes" : "no", CD(hlt_works_ok) ? "no" : "yes", pentium_f00f_bug ? "yes" : "no", CD(hard_math) ? "yes" : "no", (CD(hard_math) && ignore_irq13) ? "yes" : "no", CD(have_cpuid) ? "yes" : "no", CD(wp_works_ok) ? "yes" : "no"); for ( i = 0 ; i < 32 ; i++ ) { if ( CD(x86_capability) & (1 << i) ) { len += sprintf(buffer+len, " %s", x86_cap_flags[i]); } else if ( CD(x86_ext_capability) & (1 << i) ) { len += sprintf(buffer+len, " %s", x86_ext_cap_flags[i]); } } len += sprintf(buffer+len, "\nbogomips\t: %lu.%02lu\n", CD(loops_per_sec+2500)/500000, (CD(loops_per_sec+2500)/5000) % 100); #ifdef __SMP__ } } #endif return len; }