18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2016 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/kernel.h> 68c2ecf20Sopenharmony_ci#include <asm/processor.h> 78c2ecf20Sopenharmony_ci#include <asm/facility.h> 88c2ecf20Sopenharmony_ci#include <asm/lowcore.h> 98c2ecf20Sopenharmony_ci#include <asm/sclp.h> 108c2ecf20Sopenharmony_ci#include "boot.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci * The code within this file will be called very early. It may _not_ 148c2ecf20Sopenharmony_ci * access anything within the bss section, since that is not cleared 158c2ecf20Sopenharmony_ci * yet and may contain data (e.g. initrd) that must be saved by other 168c2ecf20Sopenharmony_ci * code. 178c2ecf20Sopenharmony_ci * For temporary objects the stack (16k) should be used. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic unsigned long als[] = { FACILITIES_ALS }; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void u16_to_hex(char *str, u16 val) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci int i, num; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci for (i = 1; i <= 4; i++) { 278c2ecf20Sopenharmony_ci num = (val >> (16 - 4 * i)) & 0xf; 288c2ecf20Sopenharmony_ci if (num >= 10) 298c2ecf20Sopenharmony_ci num += 7; 308c2ecf20Sopenharmony_ci *str++ = '0' + num; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci *str = '\0'; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void print_machine_type(void) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci static char mach_str[80] = "Detected machine-type number: "; 388c2ecf20Sopenharmony_ci char type_str[5]; 398c2ecf20Sopenharmony_ci struct cpuid id; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci get_cpu_id(&id); 428c2ecf20Sopenharmony_ci u16_to_hex(type_str, id.machine); 438c2ecf20Sopenharmony_ci strcat(mach_str, type_str); 448c2ecf20Sopenharmony_ci strcat(mach_str, "\n"); 458c2ecf20Sopenharmony_ci sclp_early_printk(mach_str); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void u16_to_decimal(char *str, u16 val) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci int div = 1; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci while (div * 10 <= val) 538c2ecf20Sopenharmony_ci div *= 10; 548c2ecf20Sopenharmony_ci while (div) { 558c2ecf20Sopenharmony_ci *str++ = '0' + val / div; 568c2ecf20Sopenharmony_ci val %= div; 578c2ecf20Sopenharmony_ci div /= 10; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci *str = '\0'; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid print_missing_facilities(void) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci static char als_str[80] = "Missing facilities: "; 658c2ecf20Sopenharmony_ci unsigned long val; 668c2ecf20Sopenharmony_ci char val_str[6]; 678c2ecf20Sopenharmony_ci int i, j, first; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci first = 1; 708c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(als); i++) { 718c2ecf20Sopenharmony_ci val = ~S390_lowcore.stfle_fac_list[i] & als[i]; 728c2ecf20Sopenharmony_ci for (j = 0; j < BITS_PER_LONG; j++) { 738c2ecf20Sopenharmony_ci if (!(val & (1UL << (BITS_PER_LONG - 1 - j)))) 748c2ecf20Sopenharmony_ci continue; 758c2ecf20Sopenharmony_ci if (!first) 768c2ecf20Sopenharmony_ci strcat(als_str, ","); 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Make sure we stay within one line. Consider that 798c2ecf20Sopenharmony_ci * each facility bit adds up to five characters and 808c2ecf20Sopenharmony_ci * z/VM adds a four character prefix. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci if (strlen(als_str) > 70) { 838c2ecf20Sopenharmony_ci strcat(als_str, "\n"); 848c2ecf20Sopenharmony_ci sclp_early_printk(als_str); 858c2ecf20Sopenharmony_ci *als_str = '\0'; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci u16_to_decimal(val_str, i * BITS_PER_LONG + j); 888c2ecf20Sopenharmony_ci strcat(als_str, val_str); 898c2ecf20Sopenharmony_ci first = 0; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci strcat(als_str, "\n"); 938c2ecf20Sopenharmony_ci sclp_early_printk(als_str); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void facility_mismatch(void) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci sclp_early_printk("The Linux kernel requires more recent processor hardware\n"); 998c2ecf20Sopenharmony_ci print_machine_type(); 1008c2ecf20Sopenharmony_ci print_missing_facilities(); 1018c2ecf20Sopenharmony_ci sclp_early_printk("See Principles of Operations for facility bits\n"); 1028c2ecf20Sopenharmony_ci disabled_wait(); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_civoid verify_facilities(void) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int i; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci __stfle(S390_lowcore.stfle_fac_list, ARRAY_SIZE(S390_lowcore.stfle_fac_list)); 1108c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(als); i++) { 1118c2ecf20Sopenharmony_ci if ((S390_lowcore.stfle_fac_list[i] & als[i]) != als[i]) 1128c2ecf20Sopenharmony_ci facility_mismatch(); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci} 115