1 /* 2 * arch/s390/kernel/early.c 3 * 4 * Copyright IBM Corp. 2007 5 * Author(s): Hongjie Yang <hongjie@us.ibm.com>, 6 * Heiko Carstens <heiko.carstens@de.ibm.com> 7 */ 8 9 #include <linux/init.h> 10 #include <linux/errno.h> 11 #include <linux/string.h> 12 #include <linux/ctype.h> 13 #include <linux/lockdep.h> 14 #include <linux/module.h> 15 #include <linux/pfn.h> 16 #include <linux/uaccess.h> 17 #include <asm/ipl.h> 18 #include <asm/lowcore.h> 19 #include <asm/processor.h> 20 #include <asm/sections.h> 21 #include <asm/setup.h> 22 #include <asm/cpcmd.h> 23 #include <asm/sclp.h> 24 25 /* 26 * Create a Kernel NSS if the SAVESYS= parameter is defined 27 */ 28 #define DEFSYS_CMD_SIZE 96 29 #define SAVESYS_CMD_SIZE 32 30 31 char kernel_nss_name[NSS_NAME_SIZE + 1]; 32 33 #ifdef CONFIG_SHARED_KERNEL 34 static noinline __init void create_kernel_nss(void) 35 { 36 unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size; 37 #ifdef CONFIG_BLK_DEV_INITRD 38 unsigned int sinitrd_pfn, einitrd_pfn; 39 #endif 40 int response; 41 char *savesys_ptr; 42 char upper_command_line[COMMAND_LINE_SIZE]; 43 char defsys_cmd[DEFSYS_CMD_SIZE]; 44 char savesys_cmd[SAVESYS_CMD_SIZE]; 45 46 /* Do nothing if we are not running under VM */ 47 if (!MACHINE_IS_VM) 48 return; 49 50 /* Convert COMMAND_LINE to upper case */ 51 for (i = 0; i < strlen(COMMAND_LINE); i++) 52 upper_command_line[i] = toupper(COMMAND_LINE[i]); 53 54 savesys_ptr = strstr(upper_command_line, "SAVESYS="); 55 56 if (!savesys_ptr) 57 return; 58 59 savesys_ptr += 8; /* Point to the beginning of the NSS name */ 60 for (i = 0; i < NSS_NAME_SIZE; i++) { 61 if (savesys_ptr[i] == ' ' || savesys_ptr[i] == '\0') 62 break; 63 kernel_nss_name[i] = savesys_ptr[i]; 64 } 65 66 stext_pfn = PFN_DOWN(__pa(&_stext)); 67 eshared_pfn = PFN_DOWN(__pa(&_eshared)); 68 end_pfn = PFN_UP(__pa(&_end)); 69 min_size = end_pfn << 2; 70 71 sprintf(defsys_cmd, "DEFSYS %s 00000-%.5X EW %.5X-%.5X SR %.5X-%.5X", 72 kernel_nss_name, stext_pfn - 1, stext_pfn, eshared_pfn - 1, 73 eshared_pfn, end_pfn); 74 75 #ifdef CONFIG_BLK_DEV_INITRD 76 if (INITRD_START && INITRD_SIZE) { 77 sinitrd_pfn = PFN_DOWN(__pa(INITRD_START)); 78 einitrd_pfn = PFN_UP(__pa(INITRD_START + INITRD_SIZE)); 79 min_size = einitrd_pfn << 2; 80 sprintf(defsys_cmd, "%s EW %.5X-%.5X", defsys_cmd, 81 sinitrd_pfn, einitrd_pfn); 82 } 83 #endif 84 85 sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK", defsys_cmd, min_size); 86 sprintf(savesys_cmd, "SAVESYS %s \n IPL %s", 87 kernel_nss_name, kernel_nss_name); 88 89 __cpcmd(defsys_cmd, NULL, 0, &response); 90 91 if (response != 0) { 92 kernel_nss_name[0] = '\0'; 93 return; 94 } 95 96 __cpcmd(savesys_cmd, NULL, 0, &response); 97 98 if (response != strlen(savesys_cmd)) { 99 kernel_nss_name[0] = '\0'; 100 return; 101 } 102 103 ipl_flags = IPL_NSS_VALID; 104 } 105 106 #else /* CONFIG_SHARED_KERNEL */ 107 108 static inline void create_kernel_nss(void) { } 109 110 #endif /* CONFIG_SHARED_KERNEL */ 111 112 /* 113 * Clear bss memory 114 */ 115 static noinline __init void clear_bss_section(void) 116 { 117 memset(__bss_start, 0, __bss_stop - __bss_start); 118 } 119 120 /* 121 * Initialize storage key for kernel pages 122 */ 123 static noinline __init void init_kernel_storage_key(void) 124 { 125 unsigned long end_pfn, init_pfn; 126 127 end_pfn = PFN_UP(__pa(&_end)); 128 129 for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++) 130 page_set_storage_key(init_pfn << PAGE_SHIFT, PAGE_DEFAULT_KEY); 131 } 132 133 static noinline __init void detect_machine_type(void) 134 { 135 struct cpuinfo_S390 *cpuinfo = &S390_lowcore.cpu_data; 136 137 get_cpu_id(&S390_lowcore.cpu_data.cpu_id); 138 139 /* Running under z/VM ? */ 140 if (cpuinfo->cpu_id.version == 0xff) 141 machine_flags |= 1; 142 143 /* Running on a P/390 ? */ 144 if (cpuinfo->cpu_id.machine == 0x7490) 145 machine_flags |= 4; 146 } 147 148 #ifdef CONFIG_64BIT 149 static noinline __init int memory_fast_detect(void) 150 { 151 unsigned long val0 = 0; 152 unsigned long val1 = 0xc; 153 int ret = -ENOSYS; 154 155 if (ipl_flags & IPL_NSS_VALID) 156 return -ENOSYS; 157 158 asm volatile( 159 " diag %1,%2,0x260\n" 160 "0: lhi %0,0\n" 161 "1:\n" 162 EX_TABLE(0b,1b) 163 : "+d" (ret), "+d" (val0), "+d" (val1) : : "cc"); 164 165 if (ret || val0 != val1) 166 return -ENOSYS; 167 168 memory_chunk[0].size = val0 + 1; 169 return 0; 170 } 171 #else 172 static inline int memory_fast_detect(void) 173 { 174 return -ENOSYS; 175 } 176 #endif 177 178 static inline __init unsigned long __tprot(unsigned long addr) 179 { 180 int cc = -1; 181 182 asm volatile( 183 " tprot 0(%1),0\n" 184 "0: ipm %0\n" 185 " srl %0,28\n" 186 "1:\n" 187 EX_TABLE(0b,1b) 188 : "+d" (cc) : "a" (addr) : "cc"); 189 return (unsigned long)cc; 190 } 191 192 /* Checking memory in 128KB increments. */ 193 #define CHUNK_INCR (1UL << 17) 194 #define ADDR2G (1UL << 31) 195 196 static noinline __init void find_memory_chunks(unsigned long memsize) 197 { 198 unsigned long addr = 0, old_addr = 0; 199 unsigned long old_cc = CHUNK_READ_WRITE; 200 unsigned long cc; 201 int chunk = 0; 202 203 while (chunk < MEMORY_CHUNKS) { 204 cc = __tprot(addr); 205 while (cc == old_cc) { 206 addr += CHUNK_INCR; 207 if (memsize && addr >= memsize) 208 break; 209 #ifndef CONFIG_64BIT 210 if (addr == ADDR2G) 211 break; 212 #endif 213 cc = __tprot(addr); 214 } 215 216 if (old_addr != addr && 217 (old_cc == CHUNK_READ_WRITE || old_cc == CHUNK_READ_ONLY)) { 218 memory_chunk[chunk].addr = old_addr; 219 memory_chunk[chunk].size = addr - old_addr; 220 memory_chunk[chunk].type = old_cc; 221 chunk++; 222 } 223 224 old_addr = addr; 225 old_cc = cc; 226 227 #ifndef CONFIG_64BIT 228 if (addr == ADDR2G) 229 break; 230 #endif 231 /* 232 * Finish memory detection at the first hole 233 * if storage size is unknown. 234 */ 235 if (cc == -1UL && !memsize) 236 break; 237 if (memsize && addr >= memsize) 238 break; 239 } 240 } 241 242 static __init void early_pgm_check_handler(void) 243 { 244 unsigned long addr; 245 const struct exception_table_entry *fixup; 246 247 addr = S390_lowcore.program_old_psw.addr; 248 fixup = search_exception_tables(addr & PSW_ADDR_INSN); 249 if (!fixup) 250 disabled_wait(0); 251 S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE; 252 } 253 254 static noinline __init void setup_lowcore_early(void) 255 { 256 psw_t psw; 257 258 psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; 259 psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_ext_handler; 260 S390_lowcore.external_new_psw = psw; 261 psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler; 262 S390_lowcore.program_new_psw = psw; 263 s390_base_pgm_handler_fn = early_pgm_check_handler; 264 } 265 266 /* 267 * Save ipl parameters, clear bss memory, initialize storage keys 268 * and create a kernel NSS at startup if the SAVESYS= parm is defined 269 */ 270 void __init startup_init(void) 271 { 272 unsigned long long memsize; 273 274 ipl_save_parameters(); 275 clear_bss_section(); 276 init_kernel_storage_key(); 277 lockdep_init(); 278 lockdep_off(); 279 detect_machine_type(); 280 create_kernel_nss(); 281 sort_main_extable(); 282 setup_lowcore_early(); 283 sclp_read_info_early(); 284 sclp_facilities_detect(); 285 memsize = sclp_memory_detect(); 286 #ifndef CONFIG_64BIT 287 /* 288 * Can't deal with more than 2G in 31 bit addressing mode, so 289 * limit the value in order to avoid strange side effects. 290 */ 291 if (memsize > ADDR2G) 292 memsize = ADDR2G; 293 #endif 294 if (memory_fast_detect() < 0) 295 find_memory_chunks((unsigned long) memsize); 296 lockdep_on(); 297 } 298