diff options
| -rw-r--r-- | drivers/s390/cio/chsc.c | 454 | ||||
| -rw-r--r-- | drivers/s390/cio/chsc.h | 22 | ||||
| -rw-r--r-- | drivers/s390/cio/css.c | 41 | ||||
| -rw-r--r-- | drivers/s390/cio/css.h | 6 |
4 files changed, 500 insertions, 23 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index f4183d660258..8b57fe62a9f9 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
| @@ -98,10 +98,8 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page) | |||
| 98 | 98 | ||
| 99 | ssd_area = page; | 99 | ssd_area = page; |
| 100 | 100 | ||
| 101 | ssd_area->request = (struct chsc_header) { | 101 | ssd_area->request.length = 0x0010; |
| 102 | .length = 0x0010, | 102 | ssd_area->request.code = 0x0004; |
| 103 | .code = 0x0004, | ||
| 104 | }; | ||
| 105 | 103 | ||
| 106 | ssd_area->ssid = sch->schid.ssid; | 104 | ssd_area->ssid = sch->schid.ssid; |
| 107 | ssd_area->f_sch = sch->schid.sch_no; | 105 | ssd_area->f_sch = sch->schid.sch_no; |
| @@ -517,10 +515,8 @@ chsc_process_crw(void) | |||
| 517 | struct device *dev; | 515 | struct device *dev; |
| 518 | memset(sei_area, 0, sizeof(*sei_area)); | 516 | memset(sei_area, 0, sizeof(*sei_area)); |
| 519 | memset(&res_data, 0, sizeof(struct res_acc_data)); | 517 | memset(&res_data, 0, sizeof(struct res_acc_data)); |
| 520 | sei_area->request = (struct chsc_header) { | 518 | sei_area->request.length = 0x0010; |
| 521 | .length = 0x0010, | 519 | sei_area->request.code = 0x000e; |
| 522 | .code = 0x000e, | ||
| 523 | }; | ||
| 524 | 520 | ||
| 525 | ccode = chsc(sei_area); | 521 | ccode = chsc(sei_area); |
| 526 | if (ccode > 0) | 522 | if (ccode > 0) |
| @@ -875,6 +871,264 @@ s390_vary_chpid( __u8 chpid, int on) | |||
| 875 | } | 871 | } |
| 876 | 872 | ||
| 877 | /* | 873 | /* |
| 874 | * Channel measurement related functions | ||
| 875 | */ | ||
| 876 | static ssize_t | ||
| 877 | chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off, | ||
| 878 | size_t count) | ||
| 879 | { | ||
| 880 | struct channel_path *chp; | ||
| 881 | unsigned int size; | ||
| 882 | |||
| 883 | chp = to_channelpath(container_of(kobj, struct device, kobj)); | ||
| 884 | if (!chp->cmg_chars) | ||
| 885 | return 0; | ||
| 886 | |||
| 887 | size = sizeof(struct cmg_chars); | ||
| 888 | |||
| 889 | if (off > size) | ||
| 890 | return 0; | ||
| 891 | if (off + count > size) | ||
| 892 | count = size - off; | ||
| 893 | memcpy(buf, chp->cmg_chars + off, count); | ||
| 894 | return count; | ||
| 895 | } | ||
| 896 | |||
| 897 | static struct bin_attribute chp_measurement_chars_attr = { | ||
| 898 | .attr = { | ||
| 899 | .name = "measurement_chars", | ||
| 900 | .mode = S_IRUSR, | ||
| 901 | .owner = THIS_MODULE, | ||
| 902 | }, | ||
| 903 | .size = sizeof(struct cmg_chars), | ||
| 904 | .read = chp_measurement_chars_read, | ||
| 905 | }; | ||
| 906 | |||
| 907 | static void | ||
| 908 | chp_measurement_copy_block(struct cmg_entry *buf, | ||
| 909 | struct channel_subsystem *css, int chpid) | ||
| 910 | { | ||
| 911 | void *area; | ||
| 912 | struct cmg_entry *entry, reference_buf; | ||
| 913 | int idx; | ||
| 914 | |||
| 915 | if (chpid < 128) { | ||
| 916 | area = css->cub_addr1; | ||
| 917 | idx = chpid; | ||
| 918 | } else { | ||
| 919 | area = css->cub_addr2; | ||
| 920 | idx = chpid - 128; | ||
| 921 | } | ||
| 922 | entry = area + (idx * sizeof(struct cmg_entry)); | ||
| 923 | do { | ||
| 924 | memcpy(buf, entry, sizeof(*entry)); | ||
| 925 | memcpy(&reference_buf, entry, sizeof(*entry)); | ||
| 926 | } while (reference_buf.values[0] != buf->values[0]); | ||
| 927 | } | ||
| 928 | |||
| 929 | static ssize_t | ||
| 930 | chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count) | ||
| 931 | { | ||
| 932 | struct channel_path *chp; | ||
| 933 | struct channel_subsystem *css; | ||
| 934 | unsigned int size; | ||
| 935 | |||
| 936 | chp = to_channelpath(container_of(kobj, struct device, kobj)); | ||
| 937 | css = to_css(chp->dev.parent); | ||
| 938 | |||
| 939 | size = sizeof(struct cmg_chars); | ||
| 940 | |||
| 941 | /* Only allow single reads. */ | ||
| 942 | if (off || count < size) | ||
| 943 | return 0; | ||
| 944 | chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); | ||
| 945 | return count; | ||
| 946 | } | ||
| 947 | |||
| 948 | static struct bin_attribute chp_measurement_attr = { | ||
| 949 | .attr = { | ||
| 950 | .name = "measurement", | ||
| 951 | .mode = S_IRUSR, | ||
| 952 | .owner = THIS_MODULE, | ||
| 953 | }, | ||
| 954 | .size = sizeof(struct cmg_entry), | ||
| 955 | .read = chp_measurement_read, | ||
| 956 | }; | ||
| 957 | |||
| 958 | static void | ||
| 959 | chsc_remove_chp_cmg_attr(struct channel_path *chp) | ||
| 960 | { | ||
| 961 | sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_chars_attr); | ||
| 962 | sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_attr); | ||
| 963 | } | ||
| 964 | |||
| 965 | static int | ||
| 966 | chsc_add_chp_cmg_attr(struct channel_path *chp) | ||
| 967 | { | ||
| 968 | int ret; | ||
| 969 | |||
| 970 | ret = sysfs_create_bin_file(&chp->dev.kobj, | ||
| 971 | &chp_measurement_chars_attr); | ||
| 972 | if (ret) | ||
| 973 | return ret; | ||
| 974 | ret = sysfs_create_bin_file(&chp->dev.kobj, &chp_measurement_attr); | ||
| 975 | if (ret) | ||
| 976 | sysfs_remove_bin_file(&chp->dev.kobj, | ||
| 977 | &chp_measurement_chars_attr); | ||
| 978 | return ret; | ||
| 979 | } | ||
| 980 | |||
| 981 | static void | ||
| 982 | chsc_remove_cmg_attr(struct channel_subsystem *css) | ||
| 983 | { | ||
| 984 | int i; | ||
| 985 | |||
| 986 | for (i = 0; i <= __MAX_CHPID; i++) { | ||
| 987 | if (!css->chps[i]) | ||
| 988 | continue; | ||
| 989 | chsc_remove_chp_cmg_attr(css->chps[i]); | ||
| 990 | } | ||
| 991 | } | ||
| 992 | |||
| 993 | static int | ||
| 994 | chsc_add_cmg_attr(struct channel_subsystem *css) | ||
| 995 | { | ||
| 996 | int i, ret; | ||
| 997 | |||
| 998 | ret = 0; | ||
| 999 | for (i = 0; i <= __MAX_CHPID; i++) { | ||
| 1000 | if (!css->chps[i]) | ||
| 1001 | continue; | ||
| 1002 | ret = chsc_add_chp_cmg_attr(css->chps[i]); | ||
| 1003 | if (ret) | ||
| 1004 | goto cleanup; | ||
| 1005 | } | ||
| 1006 | return ret; | ||
| 1007 | cleanup: | ||
| 1008 | for (--i; i >= 0; i--) { | ||
| 1009 | if (!css->chps[i]) | ||
| 1010 | continue; | ||
| 1011 | chsc_remove_chp_cmg_attr(css->chps[i]); | ||
| 1012 | } | ||
| 1013 | return ret; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | |||
| 1017 | static int | ||
| 1018 | __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | ||
| 1019 | { | ||
| 1020 | struct { | ||
| 1021 | struct chsc_header request; | ||
| 1022 | u32 operation_code : 2; | ||
| 1023 | u32 : 30; | ||
| 1024 | u32 key : 4; | ||
| 1025 | u32 : 28; | ||
| 1026 | u32 zeroes1; | ||
| 1027 | u32 cub_addr1; | ||
| 1028 | u32 zeroes2; | ||
| 1029 | u32 cub_addr2; | ||
| 1030 | u32 reserved[13]; | ||
| 1031 | struct chsc_header response; | ||
| 1032 | u32 status : 8; | ||
| 1033 | u32 : 4; | ||
| 1034 | u32 fmt : 4; | ||
| 1035 | u32 : 16; | ||
| 1036 | } *secm_area; | ||
| 1037 | int ret, ccode; | ||
| 1038 | |||
| 1039 | secm_area = page; | ||
| 1040 | secm_area->request.length = 0x0050; | ||
| 1041 | secm_area->request.code = 0x0016; | ||
| 1042 | |||
| 1043 | secm_area->key = PAGE_DEFAULT_KEY; | ||
| 1044 | secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1; | ||
| 1045 | secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2; | ||
| 1046 | |||
| 1047 | secm_area->operation_code = enable ? 0 : 1; | ||
| 1048 | |||
| 1049 | ccode = chsc(secm_area); | ||
| 1050 | if (ccode > 0) | ||
| 1051 | return (ccode == 3) ? -ENODEV : -EBUSY; | ||
| 1052 | |||
| 1053 | switch (secm_area->response.code) { | ||
| 1054 | case 0x0001: /* Success. */ | ||
| 1055 | ret = 0; | ||
| 1056 | break; | ||
| 1057 | case 0x0003: /* Invalid block. */ | ||
| 1058 | case 0x0007: /* Invalid format. */ | ||
| 1059 | case 0x0008: /* Other invalid block. */ | ||
| 1060 | CIO_CRW_EVENT(2, "Error in chsc request block!\n"); | ||
| 1061 | ret = -EINVAL; | ||
| 1062 | break; | ||
| 1063 | case 0x0004: /* Command not provided in model. */ | ||
| 1064 | CIO_CRW_EVENT(2, "Model does not provide secm\n"); | ||
| 1065 | ret = -EOPNOTSUPP; | ||
| 1066 | break; | ||
| 1067 | case 0x0102: /* cub adresses incorrect */ | ||
| 1068 | CIO_CRW_EVENT(2, "Invalid addresses in chsc request block\n"); | ||
| 1069 | ret = -EINVAL; | ||
| 1070 | break; | ||
| 1071 | case 0x0103: /* key error */ | ||
| 1072 | CIO_CRW_EVENT(2, "Access key error in secm\n"); | ||
| 1073 | ret = -EINVAL; | ||
| 1074 | break; | ||
| 1075 | case 0x0105: /* error while starting */ | ||
| 1076 | CIO_CRW_EVENT(2, "Error while starting channel measurement\n"); | ||
| 1077 | ret = -EIO; | ||
| 1078 | break; | ||
| 1079 | default: | ||
| 1080 | CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", | ||
| 1081 | secm_area->response.code); | ||
| 1082 | ret = -EIO; | ||
| 1083 | } | ||
| 1084 | return ret; | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | int | ||
| 1088 | chsc_secm(struct channel_subsystem *css, int enable) | ||
| 1089 | { | ||
| 1090 | void *secm_area; | ||
| 1091 | int ret; | ||
| 1092 | |||
| 1093 | secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
| 1094 | if (!secm_area) | ||
| 1095 | return -ENOMEM; | ||
| 1096 | |||
| 1097 | mutex_lock(&css->mutex); | ||
| 1098 | if (enable && !css->cm_enabled) { | ||
| 1099 | css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
| 1100 | css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
| 1101 | if (!css->cub_addr1 || !css->cub_addr2) { | ||
| 1102 | free_page((unsigned long)css->cub_addr1); | ||
| 1103 | free_page((unsigned long)css->cub_addr2); | ||
| 1104 | free_page((unsigned long)secm_area); | ||
| 1105 | mutex_unlock(&css->mutex); | ||
| 1106 | return -ENOMEM; | ||
| 1107 | } | ||
| 1108 | } | ||
| 1109 | ret = __chsc_do_secm(css, enable, secm_area); | ||
| 1110 | if (!ret) { | ||
| 1111 | css->cm_enabled = enable; | ||
| 1112 | if (css->cm_enabled) { | ||
| 1113 | ret = chsc_add_cmg_attr(css); | ||
| 1114 | if (ret) { | ||
| 1115 | memset(secm_area, 0, PAGE_SIZE); | ||
| 1116 | __chsc_do_secm(css, 0, secm_area); | ||
| 1117 | css->cm_enabled = 0; | ||
| 1118 | } | ||
| 1119 | } else | ||
| 1120 | chsc_remove_cmg_attr(css); | ||
| 1121 | } | ||
| 1122 | if (enable && !css->cm_enabled) { | ||
| 1123 | free_page((unsigned long)css->cub_addr1); | ||
| 1124 | free_page((unsigned long)css->cub_addr2); | ||
| 1125 | } | ||
| 1126 | mutex_unlock(&css->mutex); | ||
| 1127 | free_page((unsigned long)secm_area); | ||
| 1128 | return ret; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | /* | ||
| 878 | * Files for the channel path entries. | 1132 | * Files for the channel path entries. |
| 879 | */ | 1133 | */ |
| 880 | static ssize_t | 1134 | static ssize_t |
| @@ -925,9 +1179,39 @@ chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) | |||
| 925 | 1179 | ||
| 926 | static DEVICE_ATTR(type, 0444, chp_type_show, NULL); | 1180 | static DEVICE_ATTR(type, 0444, chp_type_show, NULL); |
| 927 | 1181 | ||
| 1182 | static ssize_t | ||
| 1183 | chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 1184 | { | ||
| 1185 | struct channel_path *chp = to_channelpath(dev); | ||
| 1186 | |||
| 1187 | if (!chp) | ||
| 1188 | return 0; | ||
| 1189 | if (chp->cmg == -1) /* channel measurements not available */ | ||
| 1190 | return sprintf(buf, "unknown\n"); | ||
| 1191 | return sprintf(buf, "%x\n", chp->cmg); | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); | ||
| 1195 | |||
| 1196 | static ssize_t | ||
| 1197 | chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
| 1198 | { | ||
| 1199 | struct channel_path *chp = to_channelpath(dev); | ||
| 1200 | |||
| 1201 | if (!chp) | ||
| 1202 | return 0; | ||
| 1203 | if (chp->shared == -1) /* channel measurements not available */ | ||
| 1204 | return sprintf(buf, "unknown\n"); | ||
| 1205 | return sprintf(buf, "%x\n", chp->shared); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); | ||
| 1209 | |||
| 928 | static struct attribute * chp_attrs[] = { | 1210 | static struct attribute * chp_attrs[] = { |
| 929 | &dev_attr_status.attr, | 1211 | &dev_attr_status.attr, |
| 930 | &dev_attr_type.attr, | 1212 | &dev_attr_type.attr, |
| 1213 | &dev_attr_cmg.attr, | ||
| 1214 | &dev_attr_shared.attr, | ||
| 931 | NULL, | 1215 | NULL, |
| 932 | }; | 1216 | }; |
| 933 | 1217 | ||
| @@ -966,10 +1250,8 @@ chsc_determine_channel_path_description(int chpid, | |||
| 966 | if (!scpd_area) | 1250 | if (!scpd_area) |
| 967 | return -ENOMEM; | 1251 | return -ENOMEM; |
| 968 | 1252 | ||
| 969 | scpd_area->request = (struct chsc_header) { | 1253 | scpd_area->request.length = 0x0010; |
| 970 | .length = 0x0010, | 1254 | scpd_area->request.code = 0x0002; |
| 971 | .code = 0x0002, | ||
| 972 | }; | ||
| 973 | 1255 | ||
| 974 | scpd_area->first_chpid = chpid; | 1256 | scpd_area->first_chpid = chpid; |
| 975 | scpd_area->last_chpid = chpid; | 1257 | scpd_area->last_chpid = chpid; |
| @@ -1006,6 +1288,111 @@ out: | |||
| 1006 | return ret; | 1288 | return ret; |
| 1007 | } | 1289 | } |
| 1008 | 1290 | ||
| 1291 | static void | ||
| 1292 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, | ||
| 1293 | struct cmg_chars *chars) | ||
| 1294 | { | ||
| 1295 | switch (chp->cmg) { | ||
| 1296 | case 2: | ||
| 1297 | case 3: | ||
| 1298 | chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), | ||
| 1299 | GFP_KERNEL); | ||
| 1300 | if (chp->cmg_chars) { | ||
| 1301 | int i, mask; | ||
| 1302 | struct cmg_chars *cmg_chars; | ||
| 1303 | |||
| 1304 | cmg_chars = chp->cmg_chars; | ||
| 1305 | for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { | ||
| 1306 | mask = 0x80 >> (i + 3); | ||
| 1307 | if (cmcv & mask) | ||
| 1308 | cmg_chars->values[i] = chars->values[i]; | ||
| 1309 | else | ||
| 1310 | cmg_chars->values[i] = 0; | ||
| 1311 | } | ||
| 1312 | } | ||
| 1313 | break; | ||
| 1314 | default: | ||
| 1315 | /* No cmg-dependent data. */ | ||
| 1316 | break; | ||
| 1317 | } | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | static int | ||
| 1321 | chsc_get_channel_measurement_chars(struct channel_path *chp) | ||
| 1322 | { | ||
| 1323 | int ccode, ret; | ||
| 1324 | |||
| 1325 | struct { | ||
| 1326 | struct chsc_header request; | ||
| 1327 | u32 : 24; | ||
| 1328 | u32 first_chpid : 8; | ||
| 1329 | u32 : 24; | ||
| 1330 | u32 last_chpid : 8; | ||
| 1331 | u32 zeroes1; | ||
| 1332 | struct chsc_header response; | ||
| 1333 | u32 zeroes2; | ||
| 1334 | u32 not_valid : 1; | ||
| 1335 | u32 shared : 1; | ||
| 1336 | u32 : 22; | ||
| 1337 | u32 chpid : 8; | ||
| 1338 | u32 cmcv : 5; | ||
| 1339 | u32 : 11; | ||
| 1340 | u32 cmgq : 8; | ||
| 1341 | u32 cmg : 8; | ||
| 1342 | u32 zeroes3; | ||
| 1343 | u32 data[NR_MEASUREMENT_CHARS]; | ||
| 1344 | } *scmc_area; | ||
| 1345 | |||
| 1346 | scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
| 1347 | if (!scmc_area) | ||
| 1348 | return -ENOMEM; | ||
| 1349 | |||
| 1350 | scmc_area->request.length = 0x0010; | ||
| 1351 | scmc_area->request.code = 0x0022; | ||
| 1352 | |||
| 1353 | scmc_area->first_chpid = chp->id; | ||
| 1354 | scmc_area->last_chpid = chp->id; | ||
| 1355 | |||
| 1356 | ccode = chsc(scmc_area); | ||
| 1357 | if (ccode > 0) { | ||
| 1358 | ret = (ccode == 3) ? -ENODEV : -EBUSY; | ||
| 1359 | goto out; | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | switch (scmc_area->response.code) { | ||
| 1363 | case 0x0001: /* Success. */ | ||
| 1364 | if (!scmc_area->not_valid) { | ||
| 1365 | chp->cmg = scmc_area->cmg; | ||
| 1366 | chp->shared = scmc_area->shared; | ||
| 1367 | chsc_initialize_cmg_chars(chp, scmc_area->cmcv, | ||
| 1368 | (struct cmg_chars *) | ||
| 1369 | &scmc_area->data); | ||
| 1370 | } else { | ||
| 1371 | chp->cmg = -1; | ||
| 1372 | chp->shared = -1; | ||
| 1373 | } | ||
| 1374 | ret = 0; | ||
| 1375 | break; | ||
| 1376 | case 0x0003: /* Invalid block. */ | ||
| 1377 | case 0x0007: /* Invalid format. */ | ||
| 1378 | case 0x0008: /* Invalid bit combination. */ | ||
| 1379 | CIO_CRW_EVENT(2, "Error in chsc request block!\n"); | ||
| 1380 | ret = -EINVAL; | ||
| 1381 | break; | ||
| 1382 | case 0x0004: /* Command not provided. */ | ||
| 1383 | CIO_CRW_EVENT(2, "Model does not provide scmc\n"); | ||
| 1384 | ret = -EOPNOTSUPP; | ||
| 1385 | break; | ||
| 1386 | default: | ||
| 1387 | CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", | ||
| 1388 | scmc_area->response.code); | ||
| 1389 | ret = -EIO; | ||
| 1390 | } | ||
| 1391 | out: | ||
| 1392 | free_page((unsigned long)scmc_area); | ||
| 1393 | return ret; | ||
| 1394 | } | ||
| 1395 | |||
| 1009 | /* | 1396 | /* |
| 1010 | * Entries for chpids on the system bus. | 1397 | * Entries for chpids on the system bus. |
| 1011 | * This replaces /proc/chpids. | 1398 | * This replaces /proc/chpids. |
| @@ -1034,6 +1421,22 @@ new_channel_path(int chpid) | |||
| 1034 | ret = chsc_determine_channel_path_description(chpid, &chp->desc); | 1421 | ret = chsc_determine_channel_path_description(chpid, &chp->desc); |
| 1035 | if (ret) | 1422 | if (ret) |
| 1036 | goto out_free; | 1423 | goto out_free; |
| 1424 | /* Get channel-measurement characteristics. */ | ||
| 1425 | if (css_characteristics_avail && css_chsc_characteristics.scmc | ||
| 1426 | && css_chsc_characteristics.secm) { | ||
| 1427 | ret = chsc_get_channel_measurement_chars(chp); | ||
| 1428 | if (ret) | ||
| 1429 | goto out_free; | ||
| 1430 | } else { | ||
| 1431 | static int msg_done; | ||
| 1432 | |||
| 1433 | if (!msg_done) { | ||
| 1434 | printk(KERN_WARNING "cio: Channel measurements not " | ||
| 1435 | "available, continuing.\n"); | ||
| 1436 | msg_done = 1; | ||
| 1437 | } | ||
| 1438 | chp->cmg = -1; | ||
| 1439 | } | ||
| 1037 | 1440 | ||
| 1038 | /* make it known to the system */ | 1441 | /* make it known to the system */ |
| 1039 | ret = device_register(&chp->dev); | 1442 | ret = device_register(&chp->dev); |
| @@ -1046,8 +1449,19 @@ new_channel_path(int chpid) | |||
| 1046 | if (ret) { | 1449 | if (ret) { |
| 1047 | device_unregister(&chp->dev); | 1450 | device_unregister(&chp->dev); |
| 1048 | goto out_free; | 1451 | goto out_free; |
| 1049 | } else | 1452 | } |
| 1050 | css[0]->chps[chpid] = chp; | 1453 | mutex_lock(&css[0]->mutex); |
| 1454 | if (css[0]->cm_enabled) { | ||
| 1455 | ret = chsc_add_chp_cmg_attr(chp); | ||
| 1456 | if (ret) { | ||
| 1457 | sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); | ||
| 1458 | device_unregister(&chp->dev); | ||
| 1459 | mutex_unlock(&css[0]->mutex); | ||
| 1460 | goto out_free; | ||
| 1461 | } | ||
| 1462 | } | ||
| 1463 | css[0]->chps[chpid] = chp; | ||
| 1464 | mutex_unlock(&css[0]->mutex); | ||
| 1051 | return ret; | 1465 | return ret; |
| 1052 | out_free: | 1466 | out_free: |
| 1053 | kfree(chp); | 1467 | kfree(chp); |
| @@ -1103,10 +1517,8 @@ chsc_enable_facility(int operation_code) | |||
| 1103 | sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA); | 1517 | sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA); |
| 1104 | if (!sda_area) | 1518 | if (!sda_area) |
| 1105 | return -ENOMEM; | 1519 | return -ENOMEM; |
| 1106 | sda_area->request = (struct chsc_header) { | 1520 | sda_area->request.length = 0x0400; |
| 1107 | .length = 0x0400, | 1521 | sda_area->request.code = 0x0031; |
| 1108 | .code = 0x0031, | ||
| 1109 | }; | ||
| 1110 | sda_area->operation_code = operation_code; | 1522 | sda_area->operation_code = operation_code; |
| 1111 | 1523 | ||
| 1112 | ret = chsc(sda_area); | 1524 | ret = chsc(sda_area); |
| @@ -1161,10 +1573,8 @@ chsc_determine_css_characteristics(void) | |||
| 1161 | return -ENOMEM; | 1573 | return -ENOMEM; |
| 1162 | } | 1574 | } |
| 1163 | 1575 | ||
| 1164 | scsc_area->request = (struct chsc_header) { | 1576 | scsc_area->request.length = 0x0010; |
| 1165 | .length = 0x0010, | 1577 | scsc_area->request.code = 0x0010; |
| 1166 | .code = 0x0010, | ||
| 1167 | }; | ||
| 1168 | 1578 | ||
| 1169 | result = chsc(scsc_area); | 1579 | result = chsc(scsc_area); |
| 1170 | if (result) { | 1580 | if (result) { |
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 3e75095f35d0..a259245780ae 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h | |||
| @@ -12,6 +12,16 @@ struct chsc_header { | |||
| 12 | u16 code; | 12 | u16 code; |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| 15 | #define NR_MEASUREMENT_CHARS 5 | ||
| 16 | struct cmg_chars { | ||
| 17 | u32 values[NR_MEASUREMENT_CHARS]; | ||
| 18 | }; | ||
| 19 | |||
| 20 | #define NR_MEASUREMENT_ENTRIES 8 | ||
| 21 | struct cmg_entry { | ||
| 22 | u32 values[NR_MEASUREMENT_ENTRIES]; | ||
| 23 | }; | ||
| 24 | |||
| 15 | struct channel_path_desc { | 25 | struct channel_path_desc { |
| 16 | u8 flags; | 26 | u8 flags; |
| 17 | u8 lsn; | 27 | u8 lsn; |
| @@ -27,6 +37,10 @@ struct channel_path { | |||
| 27 | int id; | 37 | int id; |
| 28 | int state; | 38 | int state; |
| 29 | struct channel_path_desc desc; | 39 | struct channel_path_desc desc; |
| 40 | /* Channel-measurement related stuff: */ | ||
| 41 | int cmg; | ||
| 42 | int shared; | ||
| 43 | void *cmg_chars; | ||
| 30 | struct device dev; | 44 | struct device dev; |
| 31 | }; | 45 | }; |
| 32 | 46 | ||
| @@ -52,7 +66,11 @@ struct css_general_char { | |||
| 52 | 66 | ||
| 53 | struct css_chsc_char { | 67 | struct css_chsc_char { |
| 54 | u64 res; | 68 | u64 res; |
| 55 | u64 : 43; | 69 | u64 : 20; |
| 70 | u32 secm : 1; /* bit 84 */ | ||
| 71 | u32 : 1; | ||
| 72 | u32 scmc : 1; /* bit 86 */ | ||
| 73 | u32 : 20; | ||
| 56 | u32 scssc : 1; /* bit 107 */ | 74 | u32 scssc : 1; /* bit 107 */ |
| 57 | u32 scsscf : 1; /* bit 108 */ | 75 | u32 scsscf : 1; /* bit 108 */ |
| 58 | u32 : 19; | 76 | u32 : 19; |
| @@ -67,6 +85,8 @@ extern int css_characteristics_avail; | |||
| 67 | extern void *chsc_get_chp_desc(struct subchannel*, int); | 85 | extern void *chsc_get_chp_desc(struct subchannel*, int); |
| 68 | 86 | ||
| 69 | extern int chsc_enable_facility(int); | 87 | extern int chsc_enable_facility(int); |
| 88 | struct channel_subsystem; | ||
| 89 | extern int chsc_secm(struct channel_subsystem *, int); | ||
| 70 | 90 | ||
| 71 | #define to_channelpath(device) container_of(device, struct channel_path, dev) | 91 | #define to_channelpath(device) container_of(device, struct channel_path, dev) |
| 72 | 92 | ||
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 3c77d65960db..5b304dbacaeb 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
| @@ -452,15 +452,50 @@ channel_subsystem_release(struct device *dev) | |||
| 452 | struct channel_subsystem *css; | 452 | struct channel_subsystem *css; |
| 453 | 453 | ||
| 454 | css = to_css(dev); | 454 | css = to_css(dev); |
| 455 | mutex_destroy(&css->mutex); | ||
| 455 | kfree(css); | 456 | kfree(css); |
| 456 | } | 457 | } |
| 457 | 458 | ||
| 459 | static ssize_t | ||
| 460 | css_cm_enable_show(struct device *dev, struct device_attribute *attr, | ||
| 461 | char *buf) | ||
| 462 | { | ||
| 463 | struct channel_subsystem *css = to_css(dev); | ||
| 464 | |||
| 465 | if (!css) | ||
| 466 | return 0; | ||
| 467 | return sprintf(buf, "%x\n", css->cm_enabled); | ||
| 468 | } | ||
| 469 | |||
| 470 | static ssize_t | ||
| 471 | css_cm_enable_store(struct device *dev, struct device_attribute *attr, | ||
| 472 | const char *buf, size_t count) | ||
| 473 | { | ||
| 474 | struct channel_subsystem *css = to_css(dev); | ||
| 475 | int ret; | ||
| 476 | |||
| 477 | switch (buf[0]) { | ||
| 478 | case '0': | ||
| 479 | ret = css->cm_enabled ? chsc_secm(css, 0) : 0; | ||
| 480 | break; | ||
| 481 | case '1': | ||
| 482 | ret = css->cm_enabled ? 0 : chsc_secm(css, 1); | ||
| 483 | break; | ||
| 484 | default: | ||
| 485 | ret = -EINVAL; | ||
| 486 | } | ||
| 487 | return ret < 0 ? ret : count; | ||
| 488 | } | ||
| 489 | |||
| 490 | static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store); | ||
| 491 | |||
| 458 | static inline void __init | 492 | static inline void __init |
| 459 | setup_css(int nr) | 493 | setup_css(int nr) |
| 460 | { | 494 | { |
| 461 | u32 tod_high; | 495 | u32 tod_high; |
| 462 | 496 | ||
| 463 | memset(css[nr], 0, sizeof(struct channel_subsystem)); | 497 | memset(css[nr], 0, sizeof(struct channel_subsystem)); |
| 498 | mutex_init(&css[nr]->mutex); | ||
| 464 | css[nr]->valid = 1; | 499 | css[nr]->valid = 1; |
| 465 | css[nr]->cssid = nr; | 500 | css[nr]->cssid = nr; |
| 466 | sprintf(css[nr]->device.bus_id, "css%x", nr); | 501 | sprintf(css[nr]->device.bus_id, "css%x", nr); |
| @@ -507,6 +542,9 @@ init_channel_subsystem (void) | |||
| 507 | ret = device_register(&css[i]->device); | 542 | ret = device_register(&css[i]->device); |
| 508 | if (ret) | 543 | if (ret) |
| 509 | goto out_free; | 544 | goto out_free; |
| 545 | if (css_characteristics_avail && css_chsc_characteristics.secm) | ||
| 546 | device_create_file(&css[i]->device, | ||
| 547 | &dev_attr_cm_enable); | ||
| 510 | } | 548 | } |
| 511 | css_init_done = 1; | 549 | css_init_done = 1; |
| 512 | 550 | ||
| @@ -519,6 +557,9 @@ out_free: | |||
| 519 | out_unregister: | 557 | out_unregister: |
| 520 | while (i > 0) { | 558 | while (i > 0) { |
| 521 | i--; | 559 | i--; |
| 560 | if (css_characteristics_avail && css_chsc_characteristics.secm) | ||
| 561 | device_remove_file(&css[i]->device, | ||
| 562 | &dev_attr_cm_enable); | ||
| 522 | device_unregister(&css[i]->device); | 563 | device_unregister(&css[i]->device); |
| 523 | } | 564 | } |
| 524 | out_bus: | 565 | out_bus: |
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index b6375861cb37..74a257b23383 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #ifndef _CSS_H | 1 | #ifndef _CSS_H |
| 2 | #define _CSS_H | 2 | #define _CSS_H |
| 3 | 3 | ||
| 4 | #include <linux/mutex.h> | ||
| 4 | #include <linux/wait.h> | 5 | #include <linux/wait.h> |
| 5 | #include <linux/workqueue.h> | 6 | #include <linux/workqueue.h> |
| 6 | 7 | ||
| @@ -150,6 +151,11 @@ struct channel_subsystem { | |||
| 150 | struct channel_path *chps[__MAX_CHPID + 1]; | 151 | struct channel_path *chps[__MAX_CHPID + 1]; |
| 151 | struct device device; | 152 | struct device device; |
| 152 | struct pgid global_pgid; | 153 | struct pgid global_pgid; |
| 154 | struct mutex mutex; | ||
| 155 | /* channel measurement related */ | ||
| 156 | int cm_enabled; | ||
| 157 | void *cub_addr1; | ||
| 158 | void *cub_addr2; | ||
| 153 | }; | 159 | }; |
| 154 | #define to_css(dev) container_of(dev, struct channel_subsystem, device) | 160 | #define to_css(dev) container_of(dev, struct channel_subsystem, device) |
| 155 | 161 | ||
