18282cd64SVasily Gorbik // SPDX-License-Identifier: GPL-2.0 28282cd64SVasily Gorbik /* 38282cd64SVasily Gorbik * Copyright IBM Corp. 2016 48282cd64SVasily Gorbik */ 58282cd64SVasily Gorbik #include <linux/kernel.h> 68282cd64SVasily Gorbik #include <linux/init.h> 78282cd64SVasily Gorbik #include <asm/processor.h> 88282cd64SVasily Gorbik #include <asm/facility.h> 98282cd64SVasily Gorbik #include <asm/lowcore.h> 108282cd64SVasily Gorbik #include <asm/sclp.h> 118282cd64SVasily Gorbik 128282cd64SVasily Gorbik /* 138282cd64SVasily Gorbik * The code within this file will be called very early. It may _not_ 148282cd64SVasily Gorbik * access anything within the bss section, since that is not cleared 158282cd64SVasily Gorbik * yet and may contain data (e.g. initrd) that must be saved by other 168282cd64SVasily Gorbik * code. 178282cd64SVasily Gorbik * For temporary objects the stack (16k) should be used. 188282cd64SVasily Gorbik */ 198282cd64SVasily Gorbik 208282cd64SVasily Gorbik static unsigned long als[] __initdata = { FACILITIES_ALS }; 218282cd64SVasily Gorbik 228282cd64SVasily Gorbik static void __init u16_to_hex(char *str, u16 val) 238282cd64SVasily Gorbik { 248282cd64SVasily Gorbik int i, num; 258282cd64SVasily Gorbik 268282cd64SVasily Gorbik for (i = 1; i <= 4; i++) { 278282cd64SVasily Gorbik num = (val >> (16 - 4 * i)) & 0xf; 288282cd64SVasily Gorbik if (num >= 10) 298282cd64SVasily Gorbik num += 7; 308282cd64SVasily Gorbik *str++ = '0' + num; 318282cd64SVasily Gorbik } 328282cd64SVasily Gorbik *str = '\0'; 338282cd64SVasily Gorbik } 348282cd64SVasily Gorbik 358282cd64SVasily Gorbik static void __init print_machine_type(void) 368282cd64SVasily Gorbik { 378282cd64SVasily Gorbik static char mach_str[80] __initdata = "Detected machine-type number: "; 388282cd64SVasily Gorbik char type_str[5]; 398282cd64SVasily Gorbik struct cpuid id; 408282cd64SVasily Gorbik 418282cd64SVasily Gorbik get_cpu_id(&id); 428282cd64SVasily Gorbik u16_to_hex(type_str, id.machine); 438282cd64SVasily Gorbik strcat(mach_str, type_str); 448282cd64SVasily Gorbik strcat(mach_str, "\n"); 458282cd64SVasily Gorbik sclp_early_printk(mach_str); 468282cd64SVasily Gorbik } 478282cd64SVasily Gorbik 488282cd64SVasily Gorbik static void __init u16_to_decimal(char *str, u16 val) 498282cd64SVasily Gorbik { 508282cd64SVasily Gorbik int div = 1; 518282cd64SVasily Gorbik 528282cd64SVasily Gorbik while (div * 10 <= val) 538282cd64SVasily Gorbik div *= 10; 548282cd64SVasily Gorbik while (div) { 558282cd64SVasily Gorbik *str++ = '0' + val / div; 568282cd64SVasily Gorbik val %= div; 578282cd64SVasily Gorbik div /= 10; 588282cd64SVasily Gorbik } 598282cd64SVasily Gorbik *str = '\0'; 608282cd64SVasily Gorbik } 618282cd64SVasily Gorbik 628282cd64SVasily Gorbik static void __init print_missing_facilities(void) 638282cd64SVasily Gorbik { 648282cd64SVasily Gorbik static char als_str[80] __initdata = "Missing facilities: "; 658282cd64SVasily Gorbik unsigned long val; 668282cd64SVasily Gorbik char val_str[6]; 678282cd64SVasily Gorbik int i, j, first; 688282cd64SVasily Gorbik 698282cd64SVasily Gorbik first = 1; 708282cd64SVasily Gorbik for (i = 0; i < ARRAY_SIZE(als); i++) { 718282cd64SVasily Gorbik val = ~S390_lowcore.stfle_fac_list[i] & als[i]; 728282cd64SVasily Gorbik for (j = 0; j < BITS_PER_LONG; j++) { 738282cd64SVasily Gorbik if (!(val & (1UL << (BITS_PER_LONG - 1 - j)))) 748282cd64SVasily Gorbik continue; 758282cd64SVasily Gorbik if (!first) 768282cd64SVasily Gorbik strcat(als_str, ","); 778282cd64SVasily Gorbik /* 788282cd64SVasily Gorbik * Make sure we stay within one line. Consider that 798282cd64SVasily Gorbik * each facility bit adds up to five characters and 808282cd64SVasily Gorbik * z/VM adds a four character prefix. 818282cd64SVasily Gorbik */ 828282cd64SVasily Gorbik if (strlen(als_str) > 70) { 838282cd64SVasily Gorbik strcat(als_str, "\n"); 848282cd64SVasily Gorbik sclp_early_printk(als_str); 858282cd64SVasily Gorbik *als_str = '\0'; 868282cd64SVasily Gorbik } 878282cd64SVasily Gorbik u16_to_decimal(val_str, i * BITS_PER_LONG + j); 888282cd64SVasily Gorbik strcat(als_str, val_str); 898282cd64SVasily Gorbik first = 0; 908282cd64SVasily Gorbik } 918282cd64SVasily Gorbik } 928282cd64SVasily Gorbik strcat(als_str, "\n"); 938282cd64SVasily Gorbik sclp_early_printk(als_str); 948282cd64SVasily Gorbik sclp_early_printk("See Principles of Operations for facility bits\n"); 958282cd64SVasily Gorbik } 968282cd64SVasily Gorbik 978282cd64SVasily Gorbik static void __init facility_mismatch(void) 988282cd64SVasily Gorbik { 998282cd64SVasily Gorbik sclp_early_printk("The Linux kernel requires more recent processor hardware\n"); 1008282cd64SVasily Gorbik print_machine_type(); 1018282cd64SVasily Gorbik print_missing_facilities(); 1028282cd64SVasily Gorbik disabled_wait(0x8badcccc); 1038282cd64SVasily Gorbik } 1048282cd64SVasily Gorbik 1058282cd64SVasily Gorbik void __init verify_facilities(void) 1068282cd64SVasily Gorbik { 1078282cd64SVasily Gorbik int i; 1088282cd64SVasily Gorbik 1098282cd64SVasily Gorbik for (i = 0; i < ARRAY_SIZE(S390_lowcore.stfle_fac_list); i++) 1108282cd64SVasily Gorbik S390_lowcore.stfle_fac_list[i] = 0; 1118282cd64SVasily Gorbik asm volatile( 1128282cd64SVasily Gorbik " stfl 0(0)\n" 1138282cd64SVasily Gorbik : "=m" (S390_lowcore.stfl_fac_list)); 1148282cd64SVasily Gorbik S390_lowcore.stfle_fac_list[0] = (u64)S390_lowcore.stfl_fac_list << 32; 1158282cd64SVasily Gorbik if (S390_lowcore.stfl_fac_list & 0x01000000) { 1168282cd64SVasily Gorbik register unsigned long reg0 asm("0") = ARRAY_SIZE(als) - 1; 1178282cd64SVasily Gorbik 1188282cd64SVasily Gorbik asm volatile(".insn s,0xb2b00000,0(%1)" /* stfle */ 1198282cd64SVasily Gorbik : "+d" (reg0) 1208282cd64SVasily Gorbik : "a" (&S390_lowcore.stfle_fac_list) 1218282cd64SVasily Gorbik : "memory", "cc"); 1228282cd64SVasily Gorbik } 1238282cd64SVasily Gorbik for (i = 0; i < ARRAY_SIZE(als); i++) { 1248282cd64SVasily Gorbik if ((S390_lowcore.stfle_fac_list[i] & als[i]) != als[i]) 1258282cd64SVasily Gorbik facility_mismatch(); 1268282cd64SVasily Gorbik } 1278282cd64SVasily Gorbik } 128