diff options
| author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2008-07-15 01:44:51 -0400 |
|---|---|---|
| committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2008-07-15 01:44:51 -0400 |
| commit | 43d2548bb2ef7e6d753f91468a746784041e522d (patch) | |
| tree | 77d13fcd48fd998393abb825ec36e2b732684a73 /arch/s390 | |
| parent | 585583d95c5660973bc0cf64add517b040acd8a4 (diff) | |
| parent | 85082fd7cbe3173198aac0eb5e85ab1edcc6352c (diff) | |
Merge commit '85082fd7cbe3173198aac0eb5e85ab1edcc6352c' into test-build
Manual fixup of:
arch/powerpc/Kconfig
Diffstat (limited to 'arch/s390')
26 files changed, 1480 insertions, 968 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 107e492cb47e..5dc8f8028d52 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
| @@ -146,6 +146,7 @@ config MATHEMU | |||
| 146 | config COMPAT | 146 | config COMPAT |
| 147 | bool "Kernel support for 31 bit emulation" | 147 | bool "Kernel support for 31 bit emulation" |
| 148 | depends on 64BIT | 148 | depends on 64BIT |
| 149 | select COMPAT_BINFMT_ELF | ||
| 149 | help | 150 | help |
| 150 | Select this option if you want to enable your system kernel to | 151 | Select this option if you want to enable your system kernel to |
| 151 | handle system-calls from ELF binaries for 31 bit ESA. This option | 152 | handle system-calls from ELF binaries for 31 bit ESA. This option |
| @@ -312,6 +313,10 @@ config ARCH_SPARSEMEM_DEFAULT | |||
| 312 | config ARCH_SELECT_MEMORY_MODEL | 313 | config ARCH_SELECT_MEMORY_MODEL |
| 313 | def_bool y | 314 | def_bool y |
| 314 | 315 | ||
| 316 | config ARCH_ENABLE_MEMORY_HOTPLUG | ||
| 317 | def_bool y | ||
| 318 | depends on SPARSEMEM | ||
| 319 | |||
| 315 | source "mm/Kconfig" | 320 | source "mm/Kconfig" |
| 316 | 321 | ||
| 317 | comment "I/O subsystem configuration" | 322 | comment "I/O subsystem configuration" |
| @@ -344,6 +349,22 @@ config QDIO_DEBUG | |||
| 344 | 349 | ||
| 345 | If unsure, say N. | 350 | If unsure, say N. |
| 346 | 351 | ||
| 352 | config CHSC_SCH | ||
| 353 | tristate "Support for CHSC subchannels" | ||
| 354 | help | ||
| 355 | This driver allows usage of CHSC subchannels. A CHSC subchannel | ||
| 356 | is usually present on LPAR only. | ||
| 357 | The driver creates a device /dev/chsc, which may be used to | ||
| 358 | obtain I/O configuration information about the machine and | ||
| 359 | to issue asynchronous chsc commands (DANGEROUS). | ||
| 360 | You will usually only want to use this interface on a special | ||
| 361 | LPAR designated for system management. | ||
| 362 | |||
| 363 | To compile this driver as a module, choose M here: the | ||
| 364 | module will be called chsc_sch. | ||
| 365 | |||
| 366 | If unsure, say N. | ||
| 367 | |||
| 347 | comment "Misc" | 368 | comment "Misc" |
| 348 | 369 | ||
| 349 | config IPL | 370 | config IPL |
diff --git a/arch/s390/appldata/appldata.h b/arch/s390/appldata/appldata.h index db3ae8505103..17a2636fec0a 100644 --- a/arch/s390/appldata/appldata.h +++ b/arch/s390/appldata/appldata.h | |||
| @@ -3,13 +3,11 @@ | |||
| 3 | * | 3 | * |
| 4 | * Definitions and interface for Linux - z/VM Monitor Stream. | 4 | * Definitions and interface for Linux - z/VM Monitor Stream. |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH. | 6 | * Copyright IBM Corp. 2003, 2008 |
| 7 | * | 7 | * |
| 8 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> | 8 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> |
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | //#define APPLDATA_DEBUG /* Debug messages on/off */ | ||
| 12 | |||
| 13 | #define APPLDATA_MAX_REC_SIZE 4024 /* Maximum size of the */ | 11 | #define APPLDATA_MAX_REC_SIZE 4024 /* Maximum size of the */ |
| 14 | /* data buffer */ | 12 | /* data buffer */ |
| 15 | #define APPLDATA_MAX_PROCS 100 | 13 | #define APPLDATA_MAX_PROCS 100 |
| @@ -32,12 +30,6 @@ | |||
| 32 | #define P_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x) | 30 | #define P_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x) |
| 33 | #define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x) | 31 | #define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x) |
| 34 | 32 | ||
| 35 | #ifdef APPLDATA_DEBUG | ||
| 36 | #define P_DEBUG(x...) printk(KERN_DEBUG MY_PRINT_NAME " debug: " x) | ||
| 37 | #else | ||
| 38 | #define P_DEBUG(x...) do {} while (0) | ||
| 39 | #endif | ||
| 40 | |||
| 41 | struct appldata_ops { | 33 | struct appldata_ops { |
| 42 | struct list_head list; | 34 | struct list_head list; |
| 43 | struct ctl_table_header *sysctl_header; | 35 | struct ctl_table_header *sysctl_header; |
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index ad40729bec3d..9cb3d92447a3 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | * Exports appldata_register_ops() and appldata_unregister_ops() for the | 5 | * Exports appldata_register_ops() and appldata_unregister_ops() for the |
| 6 | * data gathering modules. | 6 | * data gathering modules. |
| 7 | * | 7 | * |
| 8 | * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH. | 8 | * Copyright IBM Corp. 2003, 2008 |
| 9 | * | 9 | * |
| 10 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> | 10 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> |
| 11 | */ | 11 | */ |
| @@ -108,9 +108,6 @@ static LIST_HEAD(appldata_ops_list); | |||
| 108 | */ | 108 | */ |
| 109 | static void appldata_timer_function(unsigned long data) | 109 | static void appldata_timer_function(unsigned long data) |
| 110 | { | 110 | { |
| 111 | P_DEBUG(" -= Timer =-\n"); | ||
| 112 | P_DEBUG("CPU: %i, expire_count: %i\n", smp_processor_id(), | ||
| 113 | atomic_read(&appldata_expire_count)); | ||
| 114 | if (atomic_dec_and_test(&appldata_expire_count)) { | 111 | if (atomic_dec_and_test(&appldata_expire_count)) { |
| 115 | atomic_set(&appldata_expire_count, num_online_cpus()); | 112 | atomic_set(&appldata_expire_count, num_online_cpus()); |
| 116 | queue_work(appldata_wq, (struct work_struct *) data); | 113 | queue_work(appldata_wq, (struct work_struct *) data); |
| @@ -128,14 +125,11 @@ static void appldata_work_fn(struct work_struct *work) | |||
| 128 | struct appldata_ops *ops; | 125 | struct appldata_ops *ops; |
| 129 | int i; | 126 | int i; |
| 130 | 127 | ||
| 131 | P_DEBUG(" -= Work Queue =-\n"); | ||
| 132 | i = 0; | 128 | i = 0; |
| 133 | get_online_cpus(); | 129 | get_online_cpus(); |
| 134 | spin_lock(&appldata_ops_lock); | 130 | spin_lock(&appldata_ops_lock); |
| 135 | list_for_each(lh, &appldata_ops_list) { | 131 | list_for_each(lh, &appldata_ops_list) { |
| 136 | ops = list_entry(lh, struct appldata_ops, list); | 132 | ops = list_entry(lh, struct appldata_ops, list); |
| 137 | P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n", | ||
| 138 | ++i, ops->active, ops->name); | ||
| 139 | if (ops->active == 1) { | 133 | if (ops->active == 1) { |
| 140 | ops->callback(ops->data); | 134 | ops->callback(ops->data); |
| 141 | } | 135 | } |
| @@ -212,7 +206,6 @@ __appldata_vtimer_setup(int cmd) | |||
| 212 | 0, 1); | 206 | 0, 1); |
| 213 | } | 207 | } |
| 214 | appldata_timer_active = 1; | 208 | appldata_timer_active = 1; |
| 215 | P_INFO("Monitoring timer started.\n"); | ||
| 216 | break; | 209 | break; |
| 217 | case APPLDATA_DEL_TIMER: | 210 | case APPLDATA_DEL_TIMER: |
| 218 | for_each_online_cpu(i) | 211 | for_each_online_cpu(i) |
| @@ -221,7 +214,6 @@ __appldata_vtimer_setup(int cmd) | |||
| 221 | break; | 214 | break; |
| 222 | appldata_timer_active = 0; | 215 | appldata_timer_active = 0; |
| 223 | atomic_set(&appldata_expire_count, num_online_cpus()); | 216 | atomic_set(&appldata_expire_count, num_online_cpus()); |
| 224 | P_INFO("Monitoring timer stopped.\n"); | ||
| 225 | break; | 217 | break; |
| 226 | case APPLDATA_MOD_TIMER: | 218 | case APPLDATA_MOD_TIMER: |
| 227 | per_cpu_interval = (u64) (appldata_interval*1000 / | 219 | per_cpu_interval = (u64) (appldata_interval*1000 / |
| @@ -313,10 +305,8 @@ appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, | |||
| 313 | } | 305 | } |
| 314 | interval = 0; | 306 | interval = 0; |
| 315 | sscanf(buf, "%i", &interval); | 307 | sscanf(buf, "%i", &interval); |
| 316 | if (interval <= 0) { | 308 | if (interval <= 0) |
| 317 | P_ERROR("Timer CPU interval has to be > 0!\n"); | ||
| 318 | return -EINVAL; | 309 | return -EINVAL; |
| 319 | } | ||
| 320 | 310 | ||
| 321 | get_online_cpus(); | 311 | get_online_cpus(); |
| 322 | spin_lock(&appldata_timer_lock); | 312 | spin_lock(&appldata_timer_lock); |
| @@ -324,9 +314,6 @@ appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, | |||
| 324 | __appldata_vtimer_setup(APPLDATA_MOD_TIMER); | 314 | __appldata_vtimer_setup(APPLDATA_MOD_TIMER); |
| 325 | spin_unlock(&appldata_timer_lock); | 315 | spin_unlock(&appldata_timer_lock); |
| 326 | put_online_cpus(); | 316 | put_online_cpus(); |
| 327 | |||
| 328 | P_INFO("Monitoring CPU interval set to %u milliseconds.\n", | ||
| 329 | interval); | ||
| 330 | out: | 317 | out: |
| 331 | *lenp = len; | 318 | *lenp = len; |
| 332 | *ppos += len; | 319 | *ppos += len; |
| @@ -406,23 +393,16 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp, | |||
| 406 | P_ERROR("START DIAG 0xDC for %s failed, " | 393 | P_ERROR("START DIAG 0xDC for %s failed, " |
| 407 | "return code: %d\n", ops->name, rc); | 394 | "return code: %d\n", ops->name, rc); |
| 408 | module_put(ops->owner); | 395 | module_put(ops->owner); |
| 409 | } else { | 396 | } else |
| 410 | P_INFO("Monitoring %s data enabled, " | ||
| 411 | "DIAG 0xDC started.\n", ops->name); | ||
| 412 | ops->active = 1; | 397 | ops->active = 1; |
| 413 | } | ||
| 414 | } else if ((buf[0] == '0') && (ops->active == 1)) { | 398 | } else if ((buf[0] == '0') && (ops->active == 1)) { |
| 415 | ops->active = 0; | 399 | ops->active = 0; |
| 416 | rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, | 400 | rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, |
| 417 | (unsigned long) ops->data, ops->size, | 401 | (unsigned long) ops->data, ops->size, |
| 418 | ops->mod_lvl); | 402 | ops->mod_lvl); |
| 419 | if (rc != 0) { | 403 | if (rc != 0) |
| 420 | P_ERROR("STOP DIAG 0xDC for %s failed, " | 404 | P_ERROR("STOP DIAG 0xDC for %s failed, " |
| 421 | "return code: %d\n", ops->name, rc); | 405 | "return code: %d\n", ops->name, rc); |
| 422 | } else { | ||
| 423 | P_INFO("Monitoring %s data disabled, " | ||
| 424 | "DIAG 0xDC stopped.\n", ops->name); | ||
| 425 | } | ||
| 426 | module_put(ops->owner); | 406 | module_put(ops->owner); |
| 427 | } | 407 | } |
| 428 | spin_unlock(&appldata_ops_lock); | 408 | spin_unlock(&appldata_ops_lock); |
| @@ -468,7 +448,6 @@ int appldata_register_ops(struct appldata_ops *ops) | |||
| 468 | ops->sysctl_header = register_sysctl_table(ops->ctl_table); | 448 | ops->sysctl_header = register_sysctl_table(ops->ctl_table); |
| 469 | if (!ops->sysctl_header) | 449 | if (!ops->sysctl_header) |
| 470 | goto out; | 450 | goto out; |
| 471 | P_INFO("%s-ops registered!\n", ops->name); | ||
| 472 | return 0; | 451 | return 0; |
| 473 | out: | 452 | out: |
| 474 | spin_lock(&appldata_ops_lock); | 453 | spin_lock(&appldata_ops_lock); |
| @@ -490,7 +469,6 @@ void appldata_unregister_ops(struct appldata_ops *ops) | |||
| 490 | spin_unlock(&appldata_ops_lock); | 469 | spin_unlock(&appldata_ops_lock); |
| 491 | unregister_sysctl_table(ops->sysctl_header); | 470 | unregister_sysctl_table(ops->sysctl_header); |
| 492 | kfree(ops->ctl_table); | 471 | kfree(ops->ctl_table); |
| 493 | P_INFO("%s-ops unregistered!\n", ops->name); | ||
| 494 | } | 472 | } |
| 495 | /********************** module-ops management <END> **************************/ | 473 | /********************** module-ops management <END> **************************/ |
| 496 | 474 | ||
| @@ -553,14 +531,9 @@ static int __init appldata_init(void) | |||
| 553 | { | 531 | { |
| 554 | int i; | 532 | int i; |
| 555 | 533 | ||
| 556 | P_DEBUG("sizeof(parameter_list) = %lu\n", | ||
| 557 | sizeof(struct appldata_parameter_list)); | ||
| 558 | |||
| 559 | appldata_wq = create_singlethread_workqueue("appldata"); | 534 | appldata_wq = create_singlethread_workqueue("appldata"); |
| 560 | if (!appldata_wq) { | 535 | if (!appldata_wq) |
| 561 | P_ERROR("Could not create work queue\n"); | ||
| 562 | return -ENOMEM; | 536 | return -ENOMEM; |
| 563 | } | ||
| 564 | 537 | ||
| 565 | get_online_cpus(); | 538 | get_online_cpus(); |
| 566 | for_each_online_cpu(i) | 539 | for_each_online_cpu(i) |
| @@ -571,8 +544,6 @@ static int __init appldata_init(void) | |||
| 571 | register_hotcpu_notifier(&appldata_nb); | 544 | register_hotcpu_notifier(&appldata_nb); |
| 572 | 545 | ||
| 573 | appldata_sysctl_header = register_sysctl_table(appldata_dir_table); | 546 | appldata_sysctl_header = register_sysctl_table(appldata_dir_table); |
| 574 | |||
| 575 | P_DEBUG("Base interface initialized.\n"); | ||
| 576 | return 0; | 547 | return 0; |
| 577 | } | 548 | } |
| 578 | 549 | ||
| @@ -584,7 +555,9 @@ EXPORT_SYMBOL_GPL(appldata_register_ops); | |||
| 584 | EXPORT_SYMBOL_GPL(appldata_unregister_ops); | 555 | EXPORT_SYMBOL_GPL(appldata_unregister_ops); |
| 585 | EXPORT_SYMBOL_GPL(appldata_diag); | 556 | EXPORT_SYMBOL_GPL(appldata_diag); |
| 586 | 557 | ||
| 558 | #ifdef CONFIG_SWAP | ||
| 587 | EXPORT_SYMBOL_GPL(si_swapinfo); | 559 | EXPORT_SYMBOL_GPL(si_swapinfo); |
| 560 | #endif | ||
| 588 | EXPORT_SYMBOL_GPL(nr_threads); | 561 | EXPORT_SYMBOL_GPL(nr_threads); |
| 589 | EXPORT_SYMBOL_GPL(nr_running); | 562 | EXPORT_SYMBOL_GPL(nr_running); |
| 590 | EXPORT_SYMBOL_GPL(nr_iowait); | 563 | EXPORT_SYMBOL_GPL(nr_iowait); |
diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c index 51181ccdb87b..3ed56b7d1b2f 100644 --- a/arch/s390/appldata/appldata_mem.c +++ b/arch/s390/appldata/appldata_mem.c | |||
| @@ -14,14 +14,13 @@ | |||
| 14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
| 15 | #include <linux/errno.h> | 15 | #include <linux/errno.h> |
| 16 | #include <linux/kernel_stat.h> | 16 | #include <linux/kernel_stat.h> |
| 17 | #include <asm/io.h> | ||
| 18 | #include <linux/pagemap.h> | 17 | #include <linux/pagemap.h> |
| 19 | #include <linux/swap.h> | 18 | #include <linux/swap.h> |
| 19 | #include <asm/io.h> | ||
| 20 | 20 | ||
| 21 | #include "appldata.h" | 21 | #include "appldata.h" |
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | #define MY_PRINT_NAME "appldata_mem" /* for debug messages, etc. */ | ||
| 25 | #define P2K(x) ((x) << (PAGE_SHIFT - 10)) /* Converts #Pages to KB */ | 24 | #define P2K(x) ((x) << (PAGE_SHIFT - 10)) /* Converts #Pages to KB */ |
| 26 | 25 | ||
| 27 | /* | 26 | /* |
| @@ -70,30 +69,6 @@ static struct appldata_mem_data { | |||
| 70 | } __attribute__((packed)) appldata_mem_data; | 69 | } __attribute__((packed)) appldata_mem_data; |
| 71 | 70 | ||
| 72 | 71 | ||
| 73 | static inline void appldata_debug_print(struct appldata_mem_data *mem_data) | ||
| 74 | { | ||
| 75 | P_DEBUG("--- MEM - RECORD ---\n"); | ||
| 76 | P_DEBUG("pgpgin = %8lu KB\n", mem_data->pgpgin); | ||
| 77 | P_DEBUG("pgpgout = %8lu KB\n", mem_data->pgpgout); | ||
| 78 | P_DEBUG("pswpin = %8lu Pages\n", mem_data->pswpin); | ||
| 79 | P_DEBUG("pswpout = %8lu Pages\n", mem_data->pswpout); | ||
| 80 | P_DEBUG("pgalloc = %8lu \n", mem_data->pgalloc); | ||
| 81 | P_DEBUG("pgfault = %8lu \n", mem_data->pgfault); | ||
| 82 | P_DEBUG("pgmajfault = %8lu \n", mem_data->pgmajfault); | ||
| 83 | P_DEBUG("sharedram = %8lu KB\n", mem_data->sharedram); | ||
| 84 | P_DEBUG("totalram = %8lu KB\n", mem_data->totalram); | ||
| 85 | P_DEBUG("freeram = %8lu KB\n", mem_data->freeram); | ||
| 86 | P_DEBUG("totalhigh = %8lu KB\n", mem_data->totalhigh); | ||
| 87 | P_DEBUG("freehigh = %8lu KB\n", mem_data->freehigh); | ||
| 88 | P_DEBUG("bufferram = %8lu KB\n", mem_data->bufferram); | ||
| 89 | P_DEBUG("cached = %8lu KB\n", mem_data->cached); | ||
| 90 | P_DEBUG("totalswap = %8lu KB\n", mem_data->totalswap); | ||
| 91 | P_DEBUG("freeswap = %8lu KB\n", mem_data->freeswap); | ||
| 92 | P_DEBUG("sync_count_1 = %u\n", mem_data->sync_count_1); | ||
| 93 | P_DEBUG("sync_count_2 = %u\n", mem_data->sync_count_2); | ||
| 94 | P_DEBUG("timestamp = %lX\n", mem_data->timestamp); | ||
| 95 | } | ||
| 96 | |||
| 97 | /* | 72 | /* |
| 98 | * appldata_get_mem_data() | 73 | * appldata_get_mem_data() |
| 99 | * | 74 | * |
| @@ -140,9 +115,6 @@ static void appldata_get_mem_data(void *data) | |||
| 140 | 115 | ||
| 141 | mem_data->timestamp = get_clock(); | 116 | mem_data->timestamp = get_clock(); |
| 142 | mem_data->sync_count_2++; | 117 | mem_data->sync_count_2++; |
| 143 | #ifdef APPLDATA_DEBUG | ||
| 144 | appldata_debug_print(mem_data); | ||
| 145 | #endif | ||
| 146 | } | 118 | } |
| 147 | 119 | ||
| 148 | 120 | ||
| @@ -164,17 +136,7 @@ static struct appldata_ops ops = { | |||
| 164 | */ | 136 | */ |
| 165 | static int __init appldata_mem_init(void) | 137 | static int __init appldata_mem_init(void) |
| 166 | { | 138 | { |
| 167 | int rc; | 139 | return appldata_register_ops(&ops); |
| 168 | |||
| 169 | P_DEBUG("sizeof(mem) = %lu\n", sizeof(struct appldata_mem_data)); | ||
| 170 | |||
| 171 | rc = appldata_register_ops(&ops); | ||
| 172 | if (rc != 0) { | ||
| 173 | P_ERROR("Error registering ops, rc = %i\n", rc); | ||
| 174 | } else { | ||
| 175 | P_DEBUG("%s-ops registered!\n", ops.name); | ||
| 176 | } | ||
| 177 | return rc; | ||
| 178 | } | 140 | } |
| 179 | 141 | ||
| 180 | /* | 142 | /* |
| @@ -185,7 +147,6 @@ static int __init appldata_mem_init(void) | |||
| 185 | static void __exit appldata_mem_exit(void) | 147 | static void __exit appldata_mem_exit(void) |
| 186 | { | 148 | { |
| 187 | appldata_unregister_ops(&ops); | 149 | appldata_unregister_ops(&ops); |
| 188 | P_DEBUG("%s-ops unregistered!\n", ops.name); | ||
| 189 | } | 150 | } |
| 190 | 151 | ||
| 191 | 152 | ||
diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c index 4d8344336001..3b746556e1a3 100644 --- a/arch/s390/appldata/appldata_net_sum.c +++ b/arch/s390/appldata/appldata_net_sum.c | |||
| @@ -21,9 +21,6 @@ | |||
| 21 | #include "appldata.h" | 21 | #include "appldata.h" |
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | #define MY_PRINT_NAME "appldata_net_sum" /* for debug messages, etc. */ | ||
| 25 | |||
| 26 | |||
| 27 | /* | 24 | /* |
| 28 | * Network data | 25 | * Network data |
| 29 | * | 26 | * |
| @@ -60,26 +57,6 @@ static struct appldata_net_sum_data { | |||
| 60 | } __attribute__((packed)) appldata_net_sum_data; | 57 | } __attribute__((packed)) appldata_net_sum_data; |
| 61 | 58 | ||
| 62 | 59 | ||
| 63 | static inline void appldata_print_debug(struct appldata_net_sum_data *net_data) | ||
| 64 | { | ||
| 65 | P_DEBUG("--- NET - RECORD ---\n"); | ||
| 66 | |||
| 67 | P_DEBUG("nr_interfaces = %u\n", net_data->nr_interfaces); | ||
| 68 | P_DEBUG("rx_packets = %8lu\n", net_data->rx_packets); | ||
| 69 | P_DEBUG("tx_packets = %8lu\n", net_data->tx_packets); | ||
| 70 | P_DEBUG("rx_bytes = %8lu\n", net_data->rx_bytes); | ||
| 71 | P_DEBUG("tx_bytes = %8lu\n", net_data->tx_bytes); | ||
| 72 | P_DEBUG("rx_errors = %8lu\n", net_data->rx_errors); | ||
| 73 | P_DEBUG("tx_errors = %8lu\n", net_data->tx_errors); | ||
| 74 | P_DEBUG("rx_dropped = %8lu\n", net_data->rx_dropped); | ||
| 75 | P_DEBUG("tx_dropped = %8lu\n", net_data->tx_dropped); | ||
| 76 | P_DEBUG("collisions = %8lu\n", net_data->collisions); | ||
| 77 | |||
| 78 | P_DEBUG("sync_count_1 = %u\n", net_data->sync_count_1); | ||
| 79 | P_DEBUG("sync_count_2 = %u\n", net_data->sync_count_2); | ||
| 80 | P_DEBUG("timestamp = %lX\n", net_data->timestamp); | ||
| 81 | } | ||
| 82 | |||
| 83 | /* | 60 | /* |
| 84 | * appldata_get_net_sum_data() | 61 | * appldata_get_net_sum_data() |
| 85 | * | 62 | * |
| @@ -135,9 +112,6 @@ static void appldata_get_net_sum_data(void *data) | |||
| 135 | 112 | ||
| 136 | net_data->timestamp = get_clock(); | 113 | net_data->timestamp = get_clock(); |
| 137 | net_data->sync_count_2++; | 114 | net_data->sync_count_2++; |
| 138 | #ifdef APPLDATA_DEBUG | ||
| 139 | appldata_print_debug(net_data); | ||
| 140 | #endif | ||
| 141 | } | 115 | } |
| 142 | 116 | ||
| 143 | 117 | ||
| @@ -159,17 +133,7 @@ static struct appldata_ops ops = { | |||
| 159 | */ | 133 | */ |
| 160 | static int __init appldata_net_init(void) | 134 | static int __init appldata_net_init(void) |
| 161 | { | 135 | { |
| 162 | int rc; | 136 | return appldata_register_ops(&ops); |
| 163 | |||
| 164 | P_DEBUG("sizeof(net) = %lu\n", sizeof(struct appldata_net_sum_data)); | ||
| 165 | |||
| 166 | rc = appldata_register_ops(&ops); | ||
| 167 | if (rc != 0) { | ||
| 168 | P_ERROR("Error registering ops, rc = %i\n", rc); | ||
| 169 | } else { | ||
| 170 | P_DEBUG("%s-ops registered!\n", ops.name); | ||
| 171 | } | ||
| 172 | return rc; | ||
| 173 | } | 137 | } |
| 174 | 138 | ||
| 175 | /* | 139 | /* |
| @@ -180,7 +144,6 @@ static int __init appldata_net_init(void) | |||
| 180 | static void __exit appldata_net_exit(void) | 144 | static void __exit appldata_net_exit(void) |
| 181 | { | 145 | { |
| 182 | appldata_unregister_ops(&ops); | 146 | appldata_unregister_ops(&ops); |
| 183 | P_DEBUG("%s-ops unregistered!\n", ops.name); | ||
| 184 | } | 147 | } |
| 185 | 148 | ||
| 186 | 149 | ||
diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c index 6b3eafe10453..eb44f9f8ab91 100644 --- a/arch/s390/appldata/appldata_os.c +++ b/arch/s390/appldata/appldata_os.c | |||
| @@ -89,44 +89,6 @@ static struct appldata_ops ops = { | |||
| 89 | }; | 89 | }; |
| 90 | 90 | ||
| 91 | 91 | ||
| 92 | static inline void appldata_print_debug(struct appldata_os_data *os_data) | ||
| 93 | { | ||
| 94 | int a0, a1, a2, i; | ||
| 95 | |||
| 96 | P_DEBUG("--- OS - RECORD ---\n"); | ||
| 97 | P_DEBUG("nr_threads = %u\n", os_data->nr_threads); | ||
| 98 | P_DEBUG("nr_running = %u\n", os_data->nr_running); | ||
| 99 | P_DEBUG("nr_iowait = %u\n", os_data->nr_iowait); | ||
| 100 | P_DEBUG("avenrun(int) = %8x / %8x / %8x\n", os_data->avenrun[0], | ||
| 101 | os_data->avenrun[1], os_data->avenrun[2]); | ||
| 102 | a0 = os_data->avenrun[0]; | ||
| 103 | a1 = os_data->avenrun[1]; | ||
| 104 | a2 = os_data->avenrun[2]; | ||
| 105 | P_DEBUG("avenrun(float) = %d.%02d / %d.%02d / %d.%02d\n", | ||
| 106 | LOAD_INT(a0), LOAD_FRAC(a0), LOAD_INT(a1), LOAD_FRAC(a1), | ||
| 107 | LOAD_INT(a2), LOAD_FRAC(a2)); | ||
| 108 | |||
| 109 | P_DEBUG("nr_cpus = %u\n", os_data->nr_cpus); | ||
| 110 | for (i = 0; i < os_data->nr_cpus; i++) { | ||
| 111 | P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, " | ||
| 112 | "idle = %u, irq = %u, softirq = %u, iowait = %u, " | ||
| 113 | "steal = %u\n", | ||
| 114 | os_data->os_cpu[i].cpu_id, | ||
| 115 | os_data->os_cpu[i].per_cpu_user, | ||
| 116 | os_data->os_cpu[i].per_cpu_nice, | ||
| 117 | os_data->os_cpu[i].per_cpu_system, | ||
| 118 | os_data->os_cpu[i].per_cpu_idle, | ||
| 119 | os_data->os_cpu[i].per_cpu_irq, | ||
| 120 | os_data->os_cpu[i].per_cpu_softirq, | ||
| 121 | os_data->os_cpu[i].per_cpu_iowait, | ||
| 122 | os_data->os_cpu[i].per_cpu_steal); | ||
| 123 | } | ||
| 124 | |||
| 125 | P_DEBUG("sync_count_1 = %u\n", os_data->sync_count_1); | ||
| 126 | P_DEBUG("sync_count_2 = %u\n", os_data->sync_count_2); | ||
| 127 | P_DEBUG("timestamp = %lX\n", os_data->timestamp); | ||
| 128 | } | ||
| 129 | |||
| 130 | /* | 92 | /* |
| 131 | * appldata_get_os_data() | 93 | * appldata_get_os_data() |
| 132 | * | 94 | * |
| @@ -180,13 +142,10 @@ static void appldata_get_os_data(void *data) | |||
| 180 | APPLDATA_START_INTERVAL_REC, | 142 | APPLDATA_START_INTERVAL_REC, |
| 181 | (unsigned long) ops.data, new_size, | 143 | (unsigned long) ops.data, new_size, |
| 182 | ops.mod_lvl); | 144 | ops.mod_lvl); |
| 183 | if (rc != 0) { | 145 | if (rc != 0) |
| 184 | P_ERROR("os: START NEW DIAG 0xDC failed, " | 146 | P_ERROR("os: START NEW DIAG 0xDC failed, " |
| 185 | "return code: %d, new size = %i\n", rc, | 147 | "return code: %d, new size = %i\n", rc, |
| 186 | new_size); | 148 | new_size); |
| 187 | P_INFO("os: stopping old record now\n"); | ||
| 188 | } else | ||
| 189 | P_INFO("os: new record size = %i\n", new_size); | ||
| 190 | 149 | ||
| 191 | rc = appldata_diag(APPLDATA_RECORD_OS_ID, | 150 | rc = appldata_diag(APPLDATA_RECORD_OS_ID, |
| 192 | APPLDATA_STOP_REC, | 151 | APPLDATA_STOP_REC, |
| @@ -204,9 +163,6 @@ static void appldata_get_os_data(void *data) | |||
| 204 | } | 163 | } |
| 205 | os_data->timestamp = get_clock(); | 164 | os_data->timestamp = get_clock(); |
| 206 | os_data->sync_count_2++; | 165 | os_data->sync_count_2++; |
| 207 | #ifdef APPLDATA_DEBUG | ||
| 208 | appldata_print_debug(os_data); | ||
| 209 | #endif | ||
| 210 | } | 166 | } |
| 211 | 167 | ||
| 212 | 168 | ||
| @@ -227,12 +183,9 @@ static int __init appldata_os_init(void) | |||
| 227 | rc = -ENOMEM; | 183 | rc = -ENOMEM; |
| 228 | goto out; | 184 | goto out; |
| 229 | } | 185 | } |
| 230 | P_DEBUG("max. sizeof(os) = %i, sizeof(os_cpu) = %lu\n", max_size, | ||
| 231 | sizeof(struct appldata_os_per_cpu)); | ||
| 232 | 186 | ||
| 233 | appldata_os_data = kzalloc(max_size, GFP_DMA); | 187 | appldata_os_data = kzalloc(max_size, GFP_DMA); |
| 234 | if (appldata_os_data == NULL) { | 188 | if (appldata_os_data == NULL) { |
| 235 | P_ERROR("No memory for %s!\n", ops.name); | ||
| 236 | rc = -ENOMEM; | 189 | rc = -ENOMEM; |
| 237 | goto out; | 190 | goto out; |
| 238 | } | 191 | } |
| @@ -240,17 +193,12 @@ static int __init appldata_os_init(void) | |||
| 240 | appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu); | 193 | appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu); |
| 241 | appldata_os_data->cpu_offset = offsetof(struct appldata_os_data, | 194 | appldata_os_data->cpu_offset = offsetof(struct appldata_os_data, |
| 242 | os_cpu); | 195 | os_cpu); |
| 243 | P_DEBUG("cpu offset = %u\n", appldata_os_data->cpu_offset); | ||
| 244 | 196 | ||
| 245 | ops.data = appldata_os_data; | 197 | ops.data = appldata_os_data; |
| 246 | ops.callback = &appldata_get_os_data; | 198 | ops.callback = &appldata_get_os_data; |
| 247 | rc = appldata_register_ops(&ops); | 199 | rc = appldata_register_ops(&ops); |
| 248 | if (rc != 0) { | 200 | if (rc != 0) |
| 249 | P_ERROR("Error registering ops, rc = %i\n", rc); | ||
| 250 | kfree(appldata_os_data); | 201 | kfree(appldata_os_data); |
| 251 | } else { | ||
| 252 | P_DEBUG("%s-ops registered!\n", ops.name); | ||
| 253 | } | ||
| 254 | out: | 202 | out: |
| 255 | return rc; | 203 | return rc; |
| 256 | } | 204 | } |
| @@ -264,7 +212,6 @@ static void __exit appldata_os_exit(void) | |||
| 264 | { | 212 | { |
| 265 | appldata_unregister_ops(&ops); | 213 | appldata_unregister_ops(&ops); |
| 266 | kfree(appldata_os_data); | 214 | kfree(appldata_os_data); |
| 267 | P_DEBUG("%s-ops unregistered!\n", ops.name); | ||
| 268 | } | 215 | } |
| 269 | 216 | ||
| 270 | 217 | ||
diff --git a/arch/s390/crypto/crypt_s390.h b/arch/s390/crypto/crypt_s390.h index 9992f95ef992..0ef9829f2ad6 100644 --- a/arch/s390/crypto/crypt_s390.h +++ b/arch/s390/crypto/crypt_s390.h | |||
| @@ -296,6 +296,10 @@ static inline int crypt_s390_func_available(int func) | |||
| 296 | unsigned char status[16]; | 296 | unsigned char status[16]; |
| 297 | int ret; | 297 | int ret; |
| 298 | 298 | ||
| 299 | /* check if CPACF facility (bit 17) is available */ | ||
| 300 | if (!(stfl() & 1ULL << (31 - 17))) | ||
| 301 | return 0; | ||
| 302 | |||
| 299 | switch (func & CRYPT_S390_OP_MASK) { | 303 | switch (func & CRYPT_S390_OP_MASK) { |
| 300 | case CRYPT_S390_KM: | 304 | case CRYPT_S390_KM: |
| 301 | ret = crypt_s390_km(KM_QUERY, &status, NULL, NULL, 0); | 305 | ret = crypt_s390_km(KM_QUERY, &status, NULL, NULL, 0); |
diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c index 0cfefddd8375..eca724d229ec 100644 --- a/arch/s390/crypto/prng.c +++ b/arch/s390/crypto/prng.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <linux/fs.h> | 6 | #include <linux/fs.h> |
| 7 | #include <linux/init.h> | 7 | #include <linux/init.h> |
| 8 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
| 9 | #include <linux/smp_lock.h> | ||
| 9 | #include <linux/miscdevice.h> | 10 | #include <linux/miscdevice.h> |
| 10 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 11 | #include <linux/moduleparam.h> | 12 | #include <linux/moduleparam.h> |
| @@ -48,6 +49,7 @@ static unsigned char parm_block[32] = { | |||
| 48 | 49 | ||
| 49 | static int prng_open(struct inode *inode, struct file *file) | 50 | static int prng_open(struct inode *inode, struct file *file) |
| 50 | { | 51 | { |
| 52 | cycle_kernel_lock(); | ||
| 51 | return nonseekable_open(inode, file); | 53 | return nonseekable_open(inode, file); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| @@ -185,11 +187,8 @@ static int __init prng_init(void) | |||
| 185 | prng_seed(16); | 187 | prng_seed(16); |
| 186 | 188 | ||
| 187 | ret = misc_register(&prng_dev); | 189 | ret = misc_register(&prng_dev); |
| 188 | if (ret) { | 190 | if (ret) |
| 189 | printk(KERN_WARNING | ||
| 190 | "Could not register misc device for PRNG.\n"); | ||
| 191 | goto out_buf; | 191 | goto out_buf; |
| 192 | } | ||
| 193 | return 0; | 192 | return 0; |
| 194 | 193 | ||
| 195 | out_buf: | 194 | out_buf: |
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index 4b010ff814c9..7383781f3e6a 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c | |||
| @@ -150,33 +150,24 @@ static ssize_t hypfs_aio_read(struct kiocb *iocb, const struct iovec *iov, | |||
| 150 | unsigned long nr_segs, loff_t offset) | 150 | unsigned long nr_segs, loff_t offset) |
| 151 | { | 151 | { |
| 152 | char *data; | 152 | char *data; |
| 153 | size_t len; | 153 | ssize_t ret; |
| 154 | struct file *filp = iocb->ki_filp; | 154 | struct file *filp = iocb->ki_filp; |
| 155 | /* XXX: temporary */ | 155 | /* XXX: temporary */ |
| 156 | char __user *buf = iov[0].iov_base; | 156 | char __user *buf = iov[0].iov_base; |
| 157 | size_t count = iov[0].iov_len; | 157 | size_t count = iov[0].iov_len; |
| 158 | 158 | ||
| 159 | if (nr_segs != 1) { | 159 | if (nr_segs != 1) |
| 160 | count = -EINVAL; | 160 | return -EINVAL; |
| 161 | goto out; | ||
| 162 | } | ||
| 163 | 161 | ||
| 164 | data = filp->private_data; | 162 | data = filp->private_data; |
| 165 | len = strlen(data); | 163 | ret = simple_read_from_buffer(buf, count, &offset, data, strlen(data)); |
| 166 | if (offset > len) { | 164 | if (ret <= 0) |
| 167 | count = 0; | 165 | return ret; |
| 168 | goto out; | 166 | |
| 169 | } | 167 | iocb->ki_pos += ret; |
| 170 | if (count > len - offset) | ||
| 171 | count = len - offset; | ||
| 172 | if (copy_to_user(buf, data + offset, count)) { | ||
| 173 | count = -EFAULT; | ||
| 174 | goto out; | ||
| 175 | } | ||
| 176 | iocb->ki_pos += count; | ||
| 177 | file_accessed(filp); | 168 | file_accessed(filp); |
| 178 | out: | 169 | |
| 179 | return count; | 170 | return ret; |
| 180 | } | 171 | } |
| 181 | static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov, | 172 | static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov, |
| 182 | unsigned long nr_segs, loff_t offset) | 173 | unsigned long nr_segs, loff_t offset) |
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 6302f5082588..50f657e77344 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
| @@ -7,9 +7,14 @@ | |||
| 7 | # | 7 | # |
| 8 | CFLAGS_smp.o := -Wno-nonnull | 8 | CFLAGS_smp.o := -Wno-nonnull |
| 9 | 9 | ||
| 10 | # | ||
| 11 | # Pass UTS_MACHINE for user_regset definition | ||
| 12 | # | ||
| 13 | CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' | ||
| 14 | |||
| 10 | obj-y := bitmap.o traps.o time.o process.o base.o early.o \ | 15 | obj-y := bitmap.o traps.o time.o process.o base.o early.o \ |
| 11 | setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ | 16 | setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ |
| 12 | s390_ext.o debug.o irq.o ipl.o dis.o diag.o | 17 | s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o |
| 13 | 18 | ||
| 14 | obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) | 19 | obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) |
| 15 | obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) | 20 | obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) |
| @@ -23,7 +28,7 @@ obj-$(CONFIG_AUDIT) += audit.o | |||
| 23 | compat-obj-$(CONFIG_AUDIT) += compat_audit.o | 28 | compat-obj-$(CONFIG_AUDIT) += compat_audit.o |
| 24 | obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \ | 29 | obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \ |
| 25 | compat_wrapper.o compat_exec_domain.o \ | 30 | compat_wrapper.o compat_exec_domain.o \ |
| 26 | binfmt_elf32.o $(compat-obj-y) | 31 | $(compat-obj-y) |
| 27 | 32 | ||
| 28 | obj-$(CONFIG_VIRT_TIMER) += vtime.o | 33 | obj-$(CONFIG_VIRT_TIMER) += vtime.o |
| 29 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 34 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
diff --git a/arch/s390/kernel/binfmt_elf32.c b/arch/s390/kernel/binfmt_elf32.c deleted file mode 100644 index 3e1c315b736d..000000000000 --- a/arch/s390/kernel/binfmt_elf32.c +++ /dev/null | |||
| @@ -1,214 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Support for 32-bit Linux for S390 ELF binaries. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
| 5 | * Author(s): Gerhard Tonn (ton@de.ibm.com) | ||
| 6 | * | ||
| 7 | * Heavily inspired by the 32-bit Sparc compat code which is | ||
| 8 | * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) | ||
| 9 | * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) | ||
| 10 | */ | ||
| 11 | |||
| 12 | #define __ASMS390_ELF_H | ||
| 13 | |||
| 14 | #include <linux/time.h> | ||
| 15 | |||
| 16 | /* | ||
| 17 | * These are used to set parameters in the core dumps. | ||
| 18 | */ | ||
| 19 | #define ELF_CLASS ELFCLASS32 | ||
| 20 | #define ELF_DATA ELFDATA2MSB | ||
| 21 | #define ELF_ARCH EM_S390 | ||
| 22 | |||
| 23 | /* | ||
| 24 | * This is used to ensure we don't load something for the wrong architecture. | ||
| 25 | */ | ||
| 26 | #define elf_check_arch(x) \ | ||
| 27 | (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \ | ||
| 28 | && (x)->e_ident[EI_CLASS] == ELF_CLASS) | ||
| 29 | |||
| 30 | /* ELF register definitions */ | ||
| 31 | #define NUM_GPRS 16 | ||
| 32 | #define NUM_FPRS 16 | ||
| 33 | #define NUM_ACRS 16 | ||
| 34 | |||
| 35 | /* For SVR4/S390 the function pointer to be registered with `atexit` is | ||
| 36 | passed in R14. */ | ||
| 37 | #define ELF_PLAT_INIT(_r, load_addr) \ | ||
| 38 | do { \ | ||
| 39 | _r->gprs[14] = 0; \ | ||
| 40 | } while(0) | ||
| 41 | |||
| 42 | #define USE_ELF_CORE_DUMP | ||
| 43 | #define ELF_EXEC_PAGESIZE 4096 | ||
| 44 | |||
| 45 | /* This is the location that an ET_DYN program is loaded if exec'ed. Typical | ||
| 46 | use of this is to invoke "./ld.so someprog" to test out a new version of | ||
| 47 | the loader. We need to make sure that it is out of the way of the program | ||
| 48 | that it will "exec", and that there is sufficient room for the brk. */ | ||
| 49 | |||
| 50 | #define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) | ||
| 51 | |||
| 52 | /* Wow, the "main" arch needs arch dependent functions too.. :) */ | ||
| 53 | |||
| 54 | /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is | ||
| 55 | now struct_user_regs, they are different) */ | ||
| 56 | |||
| 57 | #define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs32(regs, &pr_reg); | ||
| 58 | |||
| 59 | #define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs32(tsk, regs) | ||
| 60 | |||
| 61 | #define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs) | ||
| 62 | |||
| 63 | /* This yields a mask that user programs can use to figure out what | ||
| 64 | instruction set this CPU supports. */ | ||
| 65 | |||
| 66 | #define ELF_HWCAP (0) | ||
| 67 | |||
| 68 | /* This yields a string that ld.so will use to load implementation | ||
| 69 | specific libraries for optimization. This is more specific in | ||
| 70 | intent than poking at uname or /proc/cpuinfo. | ||
| 71 | |||
| 72 | For the moment, we have only optimizations for the Intel generations, | ||
| 73 | but that could change... */ | ||
| 74 | |||
| 75 | #define ELF_PLATFORM (NULL) | ||
| 76 | |||
| 77 | #define SET_PERSONALITY(ex, ibcs2) \ | ||
| 78 | do { \ | ||
| 79 | if (ibcs2) \ | ||
| 80 | set_personality(PER_SVR4); \ | ||
| 81 | else if (current->personality != PER_LINUX32) \ | ||
| 82 | set_personality(PER_LINUX); \ | ||
| 83 | set_thread_flag(TIF_31BIT); \ | ||
| 84 | } while (0) | ||
| 85 | |||
| 86 | #include "compat_linux.h" | ||
| 87 | |||
| 88 | typedef _s390_fp_regs32 elf_fpregset_t; | ||
| 89 | |||
| 90 | typedef struct | ||
| 91 | { | ||
| 92 | |||
| 93 | _psw_t32 psw; | ||
| 94 | __u32 gprs[__NUM_GPRS]; | ||
| 95 | __u32 acrs[__NUM_ACRS]; | ||
| 96 | __u32 orig_gpr2; | ||
| 97 | } s390_regs32; | ||
| 98 | typedef s390_regs32 elf_gregset_t; | ||
| 99 | |||
| 100 | static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs) | ||
| 101 | { | ||
| 102 | int i; | ||
| 103 | |||
| 104 | memcpy(®s->psw.mask, &ptregs->psw.mask, 4); | ||
| 105 | memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); | ||
| 106 | for (i = 0; i < NUM_GPRS; i++) | ||
| 107 | regs->gprs[i] = ptregs->gprs[i]; | ||
| 108 | save_access_regs(regs->acrs); | ||
| 109 | regs->orig_gpr2 = ptregs->orig_gpr2; | ||
| 110 | return 1; | ||
| 111 | } | ||
| 112 | |||
| 113 | static inline int dump_task_regs32(struct task_struct *tsk, elf_gregset_t *regs) | ||
| 114 | { | ||
| 115 | struct pt_regs *ptregs = task_pt_regs(tsk); | ||
| 116 | int i; | ||
| 117 | |||
| 118 | memcpy(®s->psw.mask, &ptregs->psw.mask, 4); | ||
| 119 | memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); | ||
| 120 | for (i = 0; i < NUM_GPRS; i++) | ||
| 121 | regs->gprs[i] = ptregs->gprs[i]; | ||
| 122 | memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs)); | ||
| 123 | regs->orig_gpr2 = ptregs->orig_gpr2; | ||
| 124 | return 1; | ||
| 125 | } | ||
| 126 | |||
| 127 | static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) | ||
| 128 | { | ||
| 129 | if (tsk == current) | ||
| 130 | save_fp_regs((s390_fp_regs *) fpregs); | ||
| 131 | else | ||
| 132 | memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t)); | ||
| 133 | return 1; | ||
| 134 | } | ||
| 135 | |||
| 136 | #include <asm/processor.h> | ||
| 137 | #include <asm/pgalloc.h> | ||
| 138 | #include <linux/module.h> | ||
| 139 | #include <linux/elfcore.h> | ||
| 140 | #include <linux/binfmts.h> | ||
| 141 | #include <linux/compat.h> | ||
| 142 | |||
| 143 | #define elf_prstatus elf_prstatus32 | ||
| 144 | struct elf_prstatus32 | ||
| 145 | { | ||
| 146 | struct elf_siginfo pr_info; /* Info associated with signal */ | ||
| 147 | short pr_cursig; /* Current signal */ | ||
| 148 | u32 pr_sigpend; /* Set of pending signals */ | ||
| 149 | u32 pr_sighold; /* Set of held signals */ | ||
| 150 | pid_t pr_pid; | ||
| 151 | pid_t pr_ppid; | ||
| 152 | pid_t pr_pgrp; | ||
| 153 | pid_t pr_sid; | ||
| 154 | struct compat_timeval pr_utime; /* User time */ | ||
| 155 | struct compat_timeval pr_stime; /* System time */ | ||
| 156 | struct compat_timeval pr_cutime; /* Cumulative user time */ | ||
| 157 | struct compat_timeval pr_cstime; /* Cumulative system time */ | ||
| 158 | elf_gregset_t pr_reg; /* GP registers */ | ||
| 159 | int pr_fpvalid; /* True if math co-processor being used. */ | ||
| 160 | }; | ||
| 161 | |||
| 162 | #define elf_prpsinfo elf_prpsinfo32 | ||
| 163 | struct elf_prpsinfo32 | ||
| 164 | { | ||
| 165 | char pr_state; /* numeric process state */ | ||
| 166 | char pr_sname; /* char for pr_state */ | ||
| 167 | char pr_zomb; /* zombie */ | ||
| 168 | char pr_nice; /* nice val */ | ||
| 169 | u32 pr_flag; /* flags */ | ||
| 170 | u16 pr_uid; | ||
| 171 | u16 pr_gid; | ||
| 172 | pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; | ||
| 173 | /* Lots missing */ | ||
| 174 | char pr_fname[16]; /* filename of executable */ | ||
| 175 | char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ | ||
| 176 | }; | ||
| 177 | |||
| 178 | #include <linux/highuid.h> | ||
| 179 | |||
| 180 | /* | ||
| 181 | #define init_elf_binfmt init_elf32_binfmt | ||
| 182 | */ | ||
| 183 | |||
| 184 | #undef start_thread | ||
| 185 | #define start_thread start_thread31 | ||
| 186 | |||
| 187 | static inline void start_thread31(struct pt_regs *regs, unsigned long new_psw, | ||
| 188 | unsigned long new_stackp) | ||
| 189 | { | ||
| 190 | set_fs(USER_DS); | ||
| 191 | regs->psw.mask = psw_user32_bits; | ||
| 192 | regs->psw.addr = new_psw; | ||
| 193 | regs->gprs[15] = new_stackp; | ||
| 194 | crst_table_downgrade(current->mm, 1UL << 31); | ||
| 195 | } | ||
| 196 | |||
| 197 | MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries," | ||
| 198 | " Copyright 2000 IBM Corporation"); | ||
| 199 | MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>"); | ||
| 200 | |||
| 201 | #undef MODULE_DESCRIPTION | ||
| 202 | #undef MODULE_AUTHOR | ||
| 203 | |||
| 204 | #undef cputime_to_timeval | ||
| 205 | #define cputime_to_timeval cputime_to_compat_timeval | ||
| 206 | static inline void | ||
| 207 | cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) | ||
| 208 | { | ||
| 209 | value->tv_usec = cputime % 1000000; | ||
| 210 | value->tv_sec = cputime / 1000000; | ||
| 211 | } | ||
| 212 | |||
| 213 | #include "../../../fs/binfmt_elf.c" | ||
| 214 | |||
diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h index 419aef913ee1..cde81fa64f89 100644 --- a/arch/s390/kernel/compat_ptrace.h +++ b/arch/s390/kernel/compat_ptrace.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | #ifndef _PTRACE32_H | 1 | #ifndef _PTRACE32_H |
| 2 | #define _PTRACE32_H | 2 | #define _PTRACE32_H |
| 3 | 3 | ||
| 4 | #include "compat_linux.h" /* needed for _psw_t32 */ | 4 | #include "compat_linux.h" /* needed for psw_compat_t */ |
| 5 | 5 | ||
| 6 | typedef struct { | 6 | typedef struct { |
| 7 | __u32 cr[3]; | 7 | __u32 cr[3]; |
| @@ -38,7 +38,7 @@ typedef struct { | |||
| 38 | 38 | ||
| 39 | struct user_regs_struct32 | 39 | struct user_regs_struct32 |
| 40 | { | 40 | { |
| 41 | _psw_t32 psw; | 41 | psw_compat_t psw; |
| 42 | u32 gprs[NUM_GPRS]; | 42 | u32 gprs[NUM_GPRS]; |
| 43 | u32 acrs[NUM_ACRS]; | 43 | u32 acrs[NUM_ACRS]; |
| 44 | u32 orig_gpr2; | 44 | u32 orig_gpr2; |
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index c93d1296cc0a..d80fcd4a7fe1 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c | |||
| @@ -1079,7 +1079,6 @@ __init debug_init(void) | |||
| 1079 | s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table); | 1079 | s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table); |
| 1080 | mutex_lock(&debug_mutex); | 1080 | mutex_lock(&debug_mutex); |
| 1081 | debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT,NULL); | 1081 | debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT,NULL); |
| 1082 | printk(KERN_INFO "debug: Initialization complete\n"); | ||
| 1083 | initialized = 1; | 1082 | initialized = 1; |
| 1084 | mutex_unlock(&debug_mutex); | 1083 | mutex_unlock(&debug_mutex); |
| 1085 | 1084 | ||
| @@ -1193,7 +1192,6 @@ debug_get_uint(char *buf) | |||
| 1193 | for(; isspace(*buf); buf++); | 1192 | for(; isspace(*buf); buf++); |
| 1194 | rc = simple_strtoul(buf, &buf, 10); | 1193 | rc = simple_strtoul(buf, &buf, 10); |
| 1195 | if(*buf){ | 1194 | if(*buf){ |
| 1196 | printk("debug: no integer specified!\n"); | ||
| 1197 | rc = -EINVAL; | 1195 | rc = -EINVAL; |
| 1198 | } | 1196 | } |
| 1199 | return rc; | 1197 | return rc; |
| @@ -1340,19 +1338,12 @@ static void debug_flush(debug_info_t* id, int area) | |||
| 1340 | memset(id->areas[i][j], 0, PAGE_SIZE); | 1338 | memset(id->areas[i][j], 0, PAGE_SIZE); |
| 1341 | } | 1339 | } |
| 1342 | } | 1340 | } |
| 1343 | printk(KERN_INFO "debug: %s: all areas flushed\n",id->name); | ||
| 1344 | } else if(area >= 0 && area < id->nr_areas) { | 1341 | } else if(area >= 0 && area < id->nr_areas) { |
| 1345 | id->active_entries[area] = 0; | 1342 | id->active_entries[area] = 0; |
| 1346 | id->active_pages[area] = 0; | 1343 | id->active_pages[area] = 0; |
| 1347 | for(i = 0; i < id->pages_per_area; i++) { | 1344 | for(i = 0; i < id->pages_per_area; i++) { |
| 1348 | memset(id->areas[area][i],0,PAGE_SIZE); | 1345 | memset(id->areas[area][i],0,PAGE_SIZE); |
| 1349 | } | 1346 | } |
| 1350 | printk(KERN_INFO "debug: %s: area %i has been flushed\n", | ||
| 1351 | id->name, area); | ||
| 1352 | } else { | ||
| 1353 | printk(KERN_INFO | ||
| 1354 | "debug: %s: area %i cannot be flushed (range: %i - %i)\n", | ||
| 1355 | id->name, area, 0, id->nr_areas-1); | ||
| 1356 | } | 1347 | } |
| 1357 | spin_unlock_irqrestore(&id->lock,flags); | 1348 | spin_unlock_irqrestore(&id->lock,flags); |
| 1358 | } | 1349 | } |
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index d0e09684b9ce..2a2ca268b1dd 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
| 15 | #include <linux/pfn.h> | 15 | #include <linux/pfn.h> |
| 16 | #include <linux/uaccess.h> | 16 | #include <linux/uaccess.h> |
| 17 | #include <asm/ebcdic.h> | ||
| 17 | #include <asm/ipl.h> | 18 | #include <asm/ipl.h> |
| 18 | #include <asm/lowcore.h> | 19 | #include <asm/lowcore.h> |
| 19 | #include <asm/processor.h> | 20 | #include <asm/processor.h> |
| @@ -26,12 +27,40 @@ | |||
| 26 | /* | 27 | /* |
| 27 | * Create a Kernel NSS if the SAVESYS= parameter is defined | 28 | * Create a Kernel NSS if the SAVESYS= parameter is defined |
| 28 | */ | 29 | */ |
| 29 | #define DEFSYS_CMD_SIZE 96 | 30 | #define DEFSYS_CMD_SIZE 128 |
| 30 | #define SAVESYS_CMD_SIZE 32 | 31 | #define SAVESYS_CMD_SIZE 32 |
| 31 | 32 | ||
| 32 | char kernel_nss_name[NSS_NAME_SIZE + 1]; | 33 | char kernel_nss_name[NSS_NAME_SIZE + 1]; |
| 33 | 34 | ||
| 35 | static void __init setup_boot_command_line(void); | ||
| 36 | |||
| 37 | |||
| 34 | #ifdef CONFIG_SHARED_KERNEL | 38 | #ifdef CONFIG_SHARED_KERNEL |
| 39 | int __init savesys_ipl_nss(char *cmd, const int cmdlen); | ||
| 40 | |||
| 41 | asm( | ||
| 42 | " .section .init.text,\"ax\",@progbits\n" | ||
| 43 | " .align 4\n" | ||
| 44 | " .type savesys_ipl_nss, @function\n" | ||
| 45 | "savesys_ipl_nss:\n" | ||
| 46 | #ifdef CONFIG_64BIT | ||
| 47 | " stmg 6,15,48(15)\n" | ||
| 48 | " lgr 14,3\n" | ||
| 49 | " sam31\n" | ||
| 50 | " diag 2,14,0x8\n" | ||
| 51 | " sam64\n" | ||
| 52 | " lgr 2,14\n" | ||
| 53 | " lmg 6,15,48(15)\n" | ||
| 54 | #else | ||
| 55 | " stm 6,15,24(15)\n" | ||
| 56 | " lr 14,3\n" | ||
| 57 | " diag 2,14,0x8\n" | ||
| 58 | " lr 2,14\n" | ||
| 59 | " lm 6,15,24(15)\n" | ||
| 60 | #endif | ||
| 61 | " br 14\n" | ||
| 62 | " .size savesys_ipl_nss, .-savesys_ipl_nss\n"); | ||
| 63 | |||
| 35 | static noinline __init void create_kernel_nss(void) | 64 | static noinline __init void create_kernel_nss(void) |
| 36 | { | 65 | { |
| 37 | unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size; | 66 | unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size; |
| @@ -39,6 +68,7 @@ static noinline __init void create_kernel_nss(void) | |||
| 39 | unsigned int sinitrd_pfn, einitrd_pfn; | 68 | unsigned int sinitrd_pfn, einitrd_pfn; |
| 40 | #endif | 69 | #endif |
| 41 | int response; | 70 | int response; |
| 71 | size_t len; | ||
| 42 | char *savesys_ptr; | 72 | char *savesys_ptr; |
| 43 | char upper_command_line[COMMAND_LINE_SIZE]; | 73 | char upper_command_line[COMMAND_LINE_SIZE]; |
| 44 | char defsys_cmd[DEFSYS_CMD_SIZE]; | 74 | char defsys_cmd[DEFSYS_CMD_SIZE]; |
| @@ -49,8 +79,8 @@ static noinline __init void create_kernel_nss(void) | |||
| 49 | return; | 79 | return; |
| 50 | 80 | ||
| 51 | /* Convert COMMAND_LINE to upper case */ | 81 | /* Convert COMMAND_LINE to upper case */ |
| 52 | for (i = 0; i < strlen(COMMAND_LINE); i++) | 82 | for (i = 0; i < strlen(boot_command_line); i++) |
| 53 | upper_command_line[i] = toupper(COMMAND_LINE[i]); | 83 | upper_command_line[i] = toupper(boot_command_line[i]); |
| 54 | 84 | ||
| 55 | savesys_ptr = strstr(upper_command_line, "SAVESYS="); | 85 | savesys_ptr = strstr(upper_command_line, "SAVESYS="); |
| 56 | 86 | ||
| @@ -83,7 +113,8 @@ static noinline __init void create_kernel_nss(void) | |||
| 83 | } | 113 | } |
| 84 | #endif | 114 | #endif |
| 85 | 115 | ||
| 86 | sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK", defsys_cmd, min_size); | 116 | sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK PARMREGS=0-13", |
| 117 | defsys_cmd, min_size); | ||
| 87 | sprintf(savesys_cmd, "SAVESYS %s \n IPL %s", | 118 | sprintf(savesys_cmd, "SAVESYS %s \n IPL %s", |
| 88 | kernel_nss_name, kernel_nss_name); | 119 | kernel_nss_name, kernel_nss_name); |
| 89 | 120 | ||
| @@ -94,13 +125,24 @@ static noinline __init void create_kernel_nss(void) | |||
| 94 | return; | 125 | return; |
| 95 | } | 126 | } |
| 96 | 127 | ||
| 97 | __cpcmd(savesys_cmd, NULL, 0, &response); | 128 | len = strlen(savesys_cmd); |
| 129 | ASCEBC(savesys_cmd, len); | ||
| 130 | response = savesys_ipl_nss(savesys_cmd, len); | ||
| 98 | 131 | ||
| 99 | if (response != strlen(savesys_cmd)) { | 132 | /* On success: response is equal to the command size, |
| 133 | * max SAVESYS_CMD_SIZE | ||
| 134 | * On error: response contains the numeric portion of cp error message. | ||
| 135 | * for SAVESYS it will be >= 263 | ||
| 136 | */ | ||
| 137 | if (response > SAVESYS_CMD_SIZE) { | ||
| 100 | kernel_nss_name[0] = '\0'; | 138 | kernel_nss_name[0] = '\0'; |
| 101 | return; | 139 | return; |
| 102 | } | 140 | } |
| 103 | 141 | ||
| 142 | /* re-setup boot command line with new ipl vm parms */ | ||
| 143 | ipl_update_parameters(); | ||
| 144 | setup_boot_command_line(); | ||
| 145 | |||
| 104 | ipl_flags = IPL_NSS_VALID; | 146 | ipl_flags = IPL_NSS_VALID; |
| 105 | } | 147 | } |
| 106 | 148 | ||
| @@ -141,109 +183,11 @@ static noinline __init void detect_machine_type(void) | |||
| 141 | if (cpuinfo->cpu_id.version == 0xff) | 183 | if (cpuinfo->cpu_id.version == 0xff) |
| 142 | machine_flags |= MACHINE_FLAG_VM; | 184 | machine_flags |= MACHINE_FLAG_VM; |
| 143 | 185 | ||
| 144 | /* Running on a P/390 ? */ | ||
| 145 | if (cpuinfo->cpu_id.machine == 0x7490) | ||
| 146 | machine_flags |= MACHINE_FLAG_P390; | ||
| 147 | |||
| 148 | /* Running under KVM ? */ | 186 | /* Running under KVM ? */ |
| 149 | if (cpuinfo->cpu_id.version == 0xfe) | 187 | if (cpuinfo->cpu_id.version == 0xfe) |
| 150 | machine_flags |= MACHINE_FLAG_KVM; | 188 | machine_flags |= MACHINE_FLAG_KVM; |
| 151 | } | 189 | } |
| 152 | 190 | ||
| 153 | #ifdef CONFIG_64BIT | ||
| 154 | static noinline __init int memory_fast_detect(void) | ||
| 155 | { | ||
| 156 | unsigned long val0 = 0; | ||
| 157 | unsigned long val1 = 0xc; | ||
| 158 | int ret = -ENOSYS; | ||
| 159 | |||
| 160 | if (ipl_flags & IPL_NSS_VALID) | ||
| 161 | return -ENOSYS; | ||
| 162 | |||
| 163 | asm volatile( | ||
| 164 | " diag %1,%2,0x260\n" | ||
| 165 | "0: lhi %0,0\n" | ||
| 166 | "1:\n" | ||
| 167 | EX_TABLE(0b,1b) | ||
| 168 | : "+d" (ret), "+d" (val0), "+d" (val1) : : "cc"); | ||
| 169 | |||
| 170 | if (ret || val0 != val1) | ||
| 171 | return -ENOSYS; | ||
| 172 | |||
| 173 | memory_chunk[0].size = val0 + 1; | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | #else | ||
| 177 | static inline int memory_fast_detect(void) | ||
| 178 | { | ||
| 179 | return -ENOSYS; | ||
| 180 | } | ||
| 181 | #endif | ||
| 182 | |||
| 183 | static inline __init unsigned long __tprot(unsigned long addr) | ||
| 184 | { | ||
| 185 | int cc = -1; | ||
| 186 | |||
| 187 | asm volatile( | ||
| 188 | " tprot 0(%1),0\n" | ||
| 189 | "0: ipm %0\n" | ||
| 190 | " srl %0,28\n" | ||
| 191 | "1:\n" | ||
| 192 | EX_TABLE(0b,1b) | ||
| 193 | : "+d" (cc) : "a" (addr) : "cc"); | ||
| 194 | return (unsigned long)cc; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Checking memory in 128KB increments. */ | ||
| 198 | #define CHUNK_INCR (1UL << 17) | ||
| 199 | #define ADDR2G (1UL << 31) | ||
| 200 | |||
| 201 | static noinline __init void find_memory_chunks(unsigned long memsize) | ||
| 202 | { | ||
| 203 | unsigned long addr = 0, old_addr = 0; | ||
| 204 | unsigned long old_cc = CHUNK_READ_WRITE; | ||
| 205 | unsigned long cc; | ||
| 206 | int chunk = 0; | ||
| 207 | |||
| 208 | while (chunk < MEMORY_CHUNKS) { | ||
| 209 | cc = __tprot(addr); | ||
| 210 | while (cc == old_cc) { | ||
| 211 | addr += CHUNK_INCR; | ||
| 212 | if (memsize && addr >= memsize) | ||
| 213 | break; | ||
| 214 | #ifndef CONFIG_64BIT | ||
| 215 | if (addr == ADDR2G) | ||
| 216 | break; | ||
| 217 | #endif | ||
| 218 | cc = __tprot(addr); | ||
| 219 | } | ||
| 220 | |||
| 221 | if (old_addr != addr && | ||
| 222 | (old_cc == CHUNK_READ_WRITE || old_cc == CHUNK_READ_ONLY)) { | ||
| 223 | memory_chunk[chunk].addr = old_addr; | ||
| 224 | memory_chunk[chunk].size = addr - old_addr; | ||
| 225 | memory_chunk[chunk].type = old_cc; | ||
| 226 | chunk++; | ||
| 227 | } | ||
| 228 | |||
| 229 | old_addr = addr; | ||
| 230 | old_cc = cc; | ||
| 231 | |||
| 232 | #ifndef CONFIG_64BIT | ||
| 233 | if (addr == ADDR2G) | ||
| 234 | break; | ||
| 235 | #endif | ||
| 236 | /* | ||
| 237 | * Finish memory detection at the first hole | ||
| 238 | * if storage size is unknown. | ||
| 239 | */ | ||
| 240 | if (cc == -1UL && !memsize) | ||
| 241 | break; | ||
| 242 | if (memsize && addr >= memsize) | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | static __init void early_pgm_check_handler(void) | 191 | static __init void early_pgm_check_handler(void) |
| 248 | { | 192 | { |
| 249 | unsigned long addr; | 193 | unsigned long addr; |
| @@ -380,23 +324,61 @@ static __init void detect_machine_facilities(void) | |||
| 380 | #endif | 324 | #endif |
| 381 | } | 325 | } |
| 382 | 326 | ||
| 327 | static __init void rescue_initrd(void) | ||
| 328 | { | ||
| 329 | #ifdef CONFIG_BLK_DEV_INITRD | ||
| 330 | /* | ||
| 331 | * Move the initrd right behind the bss section in case it starts | ||
| 332 | * within the bss section. So we don't overwrite it when the bss | ||
| 333 | * section gets cleared. | ||
| 334 | */ | ||
| 335 | if (!INITRD_START || !INITRD_SIZE) | ||
| 336 | return; | ||
| 337 | if (INITRD_START >= (unsigned long) __bss_stop) | ||
| 338 | return; | ||
| 339 | memmove(__bss_stop, (void *) INITRD_START, INITRD_SIZE); | ||
| 340 | INITRD_START = (unsigned long) __bss_stop; | ||
| 341 | #endif | ||
| 342 | } | ||
| 343 | |||
| 344 | /* Set up boot command line */ | ||
| 345 | static void __init setup_boot_command_line(void) | ||
| 346 | { | ||
| 347 | char *parm = NULL; | ||
| 348 | |||
| 349 | /* copy arch command line */ | ||
| 350 | strlcpy(boot_command_line, COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); | ||
| 351 | boot_command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0; | ||
| 352 | |||
| 353 | /* append IPL PARM data to the boot command line */ | ||
| 354 | if (MACHINE_IS_VM) { | ||
| 355 | parm = boot_command_line + strlen(boot_command_line); | ||
| 356 | *parm++ = ' '; | ||
| 357 | get_ipl_vmparm(parm); | ||
| 358 | if (parm[0] == '=') | ||
| 359 | memmove(boot_command_line, parm + 1, strlen(parm)); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | |||
| 383 | /* | 364 | /* |
| 384 | * Save ipl parameters, clear bss memory, initialize storage keys | 365 | * Save ipl parameters, clear bss memory, initialize storage keys |
| 385 | * and create a kernel NSS at startup if the SAVESYS= parm is defined | 366 | * and create a kernel NSS at startup if the SAVESYS= parm is defined |
| 386 | */ | 367 | */ |
| 387 | void __init startup_init(void) | 368 | void __init startup_init(void) |
| 388 | { | 369 | { |
| 389 | unsigned long long memsize; | ||
| 390 | |||
| 391 | ipl_save_parameters(); | 370 | ipl_save_parameters(); |
| 371 | rescue_initrd(); | ||
| 392 | clear_bss_section(); | 372 | clear_bss_section(); |
| 393 | init_kernel_storage_key(); | 373 | init_kernel_storage_key(); |
| 394 | lockdep_init(); | 374 | lockdep_init(); |
| 395 | lockdep_off(); | 375 | lockdep_off(); |
| 396 | detect_machine_type(); | ||
| 397 | create_kernel_nss(); | ||
| 398 | sort_main_extable(); | 376 | sort_main_extable(); |
| 399 | setup_lowcore_early(); | 377 | setup_lowcore_early(); |
| 378 | detect_machine_type(); | ||
| 379 | ipl_update_parameters(); | ||
| 380 | setup_boot_command_line(); | ||
| 381 | create_kernel_nss(); | ||
| 400 | detect_mvpg(); | 382 | detect_mvpg(); |
| 401 | detect_ieee(); | 383 | detect_ieee(); |
| 402 | detect_csp(); | 384 | detect_csp(); |
| @@ -404,18 +386,7 @@ void __init startup_init(void) | |||
| 404 | detect_diag44(); | 386 | detect_diag44(); |
| 405 | detect_machine_facilities(); | 387 | detect_machine_facilities(); |
| 406 | setup_hpage(); | 388 | setup_hpage(); |
| 407 | sclp_read_info_early(); | ||
| 408 | sclp_facilities_detect(); | 389 | sclp_facilities_detect(); |
| 409 | memsize = sclp_memory_detect(); | 390 | detect_memory_layout(memory_chunk); |
| 410 | #ifndef CONFIG_64BIT | ||
| 411 | /* | ||
| 412 | * Can't deal with more than 2G in 31 bit addressing mode, so | ||
| 413 | * limit the value in order to avoid strange side effects. | ||
| 414 | */ | ||
| 415 | if (memsize > ADDR2G) | ||
| 416 | memsize = ADDR2G; | ||
| 417 | #endif | ||
| 418 | if (memory_fast_detect() < 0) | ||
| 419 | find_memory_chunks((unsigned long) memsize); | ||
| 420 | lockdep_on(); | 391 | lockdep_on(); |
| 421 | } | 392 | } |
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 532542447d66..54b2779b5e2f 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
| 15 | #include <linux/reboot.h> | 15 | #include <linux/reboot.h> |
| 16 | #include <linux/ctype.h> | 16 | #include <linux/ctype.h> |
| 17 | #include <linux/fs.h> | ||
| 17 | #include <asm/ipl.h> | 18 | #include <asm/ipl.h> |
| 18 | #include <asm/smp.h> | 19 | #include <asm/smp.h> |
| 19 | #include <asm/setup.h> | 20 | #include <asm/setup.h> |
| @@ -22,6 +23,7 @@ | |||
| 22 | #include <asm/ebcdic.h> | 23 | #include <asm/ebcdic.h> |
| 23 | #include <asm/reset.h> | 24 | #include <asm/reset.h> |
| 24 | #include <asm/sclp.h> | 25 | #include <asm/sclp.h> |
| 26 | #include <asm/setup.h> | ||
| 25 | 27 | ||
| 26 | #define IPL_PARM_BLOCK_VERSION 0 | 28 | #define IPL_PARM_BLOCK_VERSION 0 |
| 27 | 29 | ||
| @@ -121,6 +123,7 @@ enum ipl_method { | |||
| 121 | REIPL_METHOD_FCP_RO_VM, | 123 | REIPL_METHOD_FCP_RO_VM, |
| 122 | REIPL_METHOD_FCP_DUMP, | 124 | REIPL_METHOD_FCP_DUMP, |
| 123 | REIPL_METHOD_NSS, | 125 | REIPL_METHOD_NSS, |
| 126 | REIPL_METHOD_NSS_DIAG, | ||
| 124 | REIPL_METHOD_DEFAULT, | 127 | REIPL_METHOD_DEFAULT, |
| 125 | }; | 128 | }; |
| 126 | 129 | ||
| @@ -134,14 +137,15 @@ enum dump_method { | |||
| 134 | 137 | ||
| 135 | static int diag308_set_works = 0; | 138 | static int diag308_set_works = 0; |
| 136 | 139 | ||
| 140 | static struct ipl_parameter_block ipl_block; | ||
| 141 | |||
| 137 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; | 142 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; |
| 138 | 143 | ||
| 139 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; | 144 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; |
| 140 | static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; | 145 | static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; |
| 141 | static struct ipl_parameter_block *reipl_block_fcp; | 146 | static struct ipl_parameter_block *reipl_block_fcp; |
| 142 | static struct ipl_parameter_block *reipl_block_ccw; | 147 | static struct ipl_parameter_block *reipl_block_ccw; |
| 143 | 148 | static struct ipl_parameter_block *reipl_block_nss; | |
| 144 | static char reipl_nss_name[NSS_NAME_SIZE + 1]; | ||
| 145 | 149 | ||
| 146 | static int dump_capabilities = DUMP_TYPE_NONE; | 150 | static int dump_capabilities = DUMP_TYPE_NONE; |
| 147 | static enum dump_type dump_type = DUMP_TYPE_NONE; | 151 | static enum dump_type dump_type = DUMP_TYPE_NONE; |
| @@ -263,6 +267,56 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, | |||
| 263 | 267 | ||
| 264 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); | 268 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); |
| 265 | 269 | ||
| 270 | /* VM IPL PARM routines */ | ||
| 271 | static void reipl_get_ascii_vmparm(char *dest, | ||
| 272 | const struct ipl_parameter_block *ipb) | ||
| 273 | { | ||
| 274 | int i; | ||
| 275 | int len = 0; | ||
| 276 | char has_lowercase = 0; | ||
| 277 | |||
| 278 | if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) && | ||
| 279 | (ipb->ipl_info.ccw.vm_parm_len > 0)) { | ||
| 280 | |||
| 281 | len = ipb->ipl_info.ccw.vm_parm_len; | ||
| 282 | memcpy(dest, ipb->ipl_info.ccw.vm_parm, len); | ||
| 283 | /* If at least one character is lowercase, we assume mixed | ||
| 284 | * case; otherwise we convert everything to lowercase. | ||
| 285 | */ | ||
| 286 | for (i = 0; i < len; i++) | ||
| 287 | if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */ | ||
| 288 | (dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */ | ||
| 289 | (dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */ | ||
| 290 | has_lowercase = 1; | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | if (!has_lowercase) | ||
| 294 | EBC_TOLOWER(dest, len); | ||
| 295 | EBCASC(dest, len); | ||
| 296 | } | ||
| 297 | dest[len] = 0; | ||
| 298 | } | ||
| 299 | |||
| 300 | void get_ipl_vmparm(char *dest) | ||
| 301 | { | ||
| 302 | if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW)) | ||
| 303 | reipl_get_ascii_vmparm(dest, &ipl_block); | ||
| 304 | else | ||
| 305 | dest[0] = 0; | ||
| 306 | } | ||
| 307 | |||
| 308 | static ssize_t ipl_vm_parm_show(struct kobject *kobj, | ||
| 309 | struct kobj_attribute *attr, char *page) | ||
| 310 | { | ||
| 311 | char parm[DIAG308_VMPARM_SIZE + 1] = {}; | ||
| 312 | |||
| 313 | get_ipl_vmparm(parm); | ||
| 314 | return sprintf(page, "%s\n", parm); | ||
| 315 | } | ||
| 316 | |||
| 317 | static struct kobj_attribute sys_ipl_vm_parm_attr = | ||
| 318 | __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); | ||
| 319 | |||
| 266 | static ssize_t sys_ipl_device_show(struct kobject *kobj, | 320 | static ssize_t sys_ipl_device_show(struct kobject *kobj, |
| 267 | struct kobj_attribute *attr, char *page) | 321 | struct kobj_attribute *attr, char *page) |
| 268 | { | 322 | { |
| @@ -285,14 +339,8 @@ static struct kobj_attribute sys_ipl_device_attr = | |||
| 285 | static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, | 339 | static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, |
| 286 | char *buf, loff_t off, size_t count) | 340 | char *buf, loff_t off, size_t count) |
| 287 | { | 341 | { |
| 288 | unsigned int size = IPL_PARMBLOCK_SIZE; | 342 | return memory_read_from_buffer(buf, count, &off, IPL_PARMBLOCK_START, |
| 289 | 343 | IPL_PARMBLOCK_SIZE); | |
| 290 | if (off > size) | ||
| 291 | return 0; | ||
| 292 | if (off + count > size) | ||
| 293 | count = size - off; | ||
| 294 | memcpy(buf, (void *)IPL_PARMBLOCK_START + off, count); | ||
| 295 | return count; | ||
| 296 | } | 344 | } |
| 297 | 345 | ||
| 298 | static struct bin_attribute ipl_parameter_attr = { | 346 | static struct bin_attribute ipl_parameter_attr = { |
| @@ -310,12 +358,7 @@ static ssize_t ipl_scp_data_read(struct kobject *kobj, struct bin_attribute *att | |||
| 310 | unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len; | 358 | unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len; |
| 311 | void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data; | 359 | void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data; |
| 312 | 360 | ||
| 313 | if (off > size) | 361 | return memory_read_from_buffer(buf, count, &off, scp_data, size); |
| 314 | return 0; | ||
| 315 | if (off + count > size) | ||
| 316 | count = size - off; | ||
| 317 | memcpy(buf, scp_data + off, count); | ||
| 318 | return count; | ||
| 319 | } | 362 | } |
| 320 | 363 | ||
| 321 | static struct bin_attribute ipl_scp_data_attr = { | 364 | static struct bin_attribute ipl_scp_data_attr = { |
| @@ -370,15 +413,27 @@ static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj, | |||
| 370 | static struct kobj_attribute sys_ipl_ccw_loadparm_attr = | 413 | static struct kobj_attribute sys_ipl_ccw_loadparm_attr = |
| 371 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); | 414 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); |
| 372 | 415 | ||
| 373 | static struct attribute *ipl_ccw_attrs[] = { | 416 | static struct attribute *ipl_ccw_attrs_vm[] = { |
| 374 | &sys_ipl_type_attr.attr, | 417 | &sys_ipl_type_attr.attr, |
| 375 | &sys_ipl_device_attr.attr, | 418 | &sys_ipl_device_attr.attr, |
| 376 | &sys_ipl_ccw_loadparm_attr.attr, | 419 | &sys_ipl_ccw_loadparm_attr.attr, |
| 420 | &sys_ipl_vm_parm_attr.attr, | ||
| 377 | NULL, | 421 | NULL, |
| 378 | }; | 422 | }; |
| 379 | 423 | ||
| 380 | static struct attribute_group ipl_ccw_attr_group = { | 424 | static struct attribute *ipl_ccw_attrs_lpar[] = { |
| 381 | .attrs = ipl_ccw_attrs, | 425 | &sys_ipl_type_attr.attr, |
| 426 | &sys_ipl_device_attr.attr, | ||
| 427 | &sys_ipl_ccw_loadparm_attr.attr, | ||
| 428 | NULL, | ||
| 429 | }; | ||
| 430 | |||
| 431 | static struct attribute_group ipl_ccw_attr_group_vm = { | ||
| 432 | .attrs = ipl_ccw_attrs_vm, | ||
| 433 | }; | ||
| 434 | |||
| 435 | static struct attribute_group ipl_ccw_attr_group_lpar = { | ||
| 436 | .attrs = ipl_ccw_attrs_lpar | ||
| 382 | }; | 437 | }; |
| 383 | 438 | ||
| 384 | /* NSS ipl device attributes */ | 439 | /* NSS ipl device attributes */ |
| @@ -388,6 +443,8 @@ DEFINE_IPL_ATTR_RO(ipl_nss, name, "%s\n", kernel_nss_name); | |||
| 388 | static struct attribute *ipl_nss_attrs[] = { | 443 | static struct attribute *ipl_nss_attrs[] = { |
| 389 | &sys_ipl_type_attr.attr, | 444 | &sys_ipl_type_attr.attr, |
| 390 | &sys_ipl_nss_name_attr.attr, | 445 | &sys_ipl_nss_name_attr.attr, |
| 446 | &sys_ipl_ccw_loadparm_attr.attr, | ||
| 447 | &sys_ipl_vm_parm_attr.attr, | ||
| 391 | NULL, | 448 | NULL, |
| 392 | }; | 449 | }; |
| 393 | 450 | ||
| @@ -450,7 +507,12 @@ static int __init ipl_init(void) | |||
| 450 | } | 507 | } |
| 451 | switch (ipl_info.type) { | 508 | switch (ipl_info.type) { |
| 452 | case IPL_TYPE_CCW: | 509 | case IPL_TYPE_CCW: |
| 453 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group); | 510 | if (MACHINE_IS_VM) |
| 511 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
| 512 | &ipl_ccw_attr_group_vm); | ||
| 513 | else | ||
| 514 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
| 515 | &ipl_ccw_attr_group_lpar); | ||
| 454 | break; | 516 | break; |
| 455 | case IPL_TYPE_FCP: | 517 | case IPL_TYPE_FCP: |
| 456 | case IPL_TYPE_FCP_DUMP: | 518 | case IPL_TYPE_FCP_DUMP: |
| @@ -481,6 +543,83 @@ static struct shutdown_action __refdata ipl_action = { | |||
| 481 | * reipl shutdown action: Reboot Linux on shutdown. | 543 | * reipl shutdown action: Reboot Linux on shutdown. |
| 482 | */ | 544 | */ |
| 483 | 545 | ||
| 546 | /* VM IPL PARM attributes */ | ||
| 547 | static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb, | ||
| 548 | char *page) | ||
| 549 | { | ||
| 550 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; | ||
| 551 | |||
| 552 | reipl_get_ascii_vmparm(vmparm, ipb); | ||
| 553 | return sprintf(page, "%s\n", vmparm); | ||
| 554 | } | ||
| 555 | |||
| 556 | static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb, | ||
| 557 | size_t vmparm_max, | ||
| 558 | const char *buf, size_t len) | ||
| 559 | { | ||
| 560 | int i, ip_len; | ||
| 561 | |||
| 562 | /* ignore trailing newline */ | ||
| 563 | ip_len = len; | ||
| 564 | if ((len > 0) && (buf[len - 1] == '\n')) | ||
| 565 | ip_len--; | ||
| 566 | |||
| 567 | if (ip_len > vmparm_max) | ||
| 568 | return -EINVAL; | ||
| 569 | |||
| 570 | /* parm is used to store kernel options, check for common chars */ | ||
| 571 | for (i = 0; i < ip_len; i++) | ||
| 572 | if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i]))) | ||
| 573 | return -EINVAL; | ||
| 574 | |||
| 575 | memset(ipb->ipl_info.ccw.vm_parm, 0, DIAG308_VMPARM_SIZE); | ||
| 576 | ipb->ipl_info.ccw.vm_parm_len = ip_len; | ||
| 577 | if (ip_len > 0) { | ||
| 578 | ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID; | ||
| 579 | memcpy(ipb->ipl_info.ccw.vm_parm, buf, ip_len); | ||
| 580 | ASCEBC(ipb->ipl_info.ccw.vm_parm, ip_len); | ||
| 581 | } else { | ||
| 582 | ipb->ipl_info.ccw.vm_flags &= ~DIAG308_VM_FLAGS_VP_VALID; | ||
| 583 | } | ||
| 584 | |||
| 585 | return len; | ||
| 586 | } | ||
| 587 | |||
| 588 | /* NSS wrapper */ | ||
| 589 | static ssize_t reipl_nss_vmparm_show(struct kobject *kobj, | ||
| 590 | struct kobj_attribute *attr, char *page) | ||
| 591 | { | ||
| 592 | return reipl_generic_vmparm_show(reipl_block_nss, page); | ||
| 593 | } | ||
| 594 | |||
| 595 | static ssize_t reipl_nss_vmparm_store(struct kobject *kobj, | ||
| 596 | struct kobj_attribute *attr, | ||
| 597 | const char *buf, size_t len) | ||
| 598 | { | ||
| 599 | return reipl_generic_vmparm_store(reipl_block_nss, 56, buf, len); | ||
| 600 | } | ||
| 601 | |||
| 602 | /* CCW wrapper */ | ||
| 603 | static ssize_t reipl_ccw_vmparm_show(struct kobject *kobj, | ||
| 604 | struct kobj_attribute *attr, char *page) | ||
| 605 | { | ||
| 606 | return reipl_generic_vmparm_show(reipl_block_ccw, page); | ||
| 607 | } | ||
| 608 | |||
| 609 | static ssize_t reipl_ccw_vmparm_store(struct kobject *kobj, | ||
| 610 | struct kobj_attribute *attr, | ||
| 611 | const char *buf, size_t len) | ||
| 612 | { | ||
| 613 | return reipl_generic_vmparm_store(reipl_block_ccw, 64, buf, len); | ||
| 614 | } | ||
| 615 | |||
| 616 | static struct kobj_attribute sys_reipl_nss_vmparm_attr = | ||
| 617 | __ATTR(parm, S_IRUGO | S_IWUSR, reipl_nss_vmparm_show, | ||
| 618 | reipl_nss_vmparm_store); | ||
| 619 | static struct kobj_attribute sys_reipl_ccw_vmparm_attr = | ||
| 620 | __ATTR(parm, S_IRUGO | S_IWUSR, reipl_ccw_vmparm_show, | ||
| 621 | reipl_ccw_vmparm_store); | ||
| 622 | |||
| 484 | /* FCP reipl device attributes */ | 623 | /* FCP reipl device attributes */ |
| 485 | 624 | ||
| 486 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", | 625 | DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", |
| @@ -513,27 +652,26 @@ static struct attribute_group reipl_fcp_attr_group = { | |||
| 513 | DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | 652 | DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", |
| 514 | reipl_block_ccw->ipl_info.ccw.devno); | 653 | reipl_block_ccw->ipl_info.ccw.devno); |
| 515 | 654 | ||
| 516 | static void reipl_get_ascii_loadparm(char *loadparm) | 655 | static void reipl_get_ascii_loadparm(char *loadparm, |
| 656 | struct ipl_parameter_block *ibp) | ||
| 517 | { | 657 | { |
| 518 | memcpy(loadparm, &reipl_block_ccw->ipl_info.ccw.load_param, | 658 | memcpy(loadparm, ibp->ipl_info.ccw.load_parm, LOADPARM_LEN); |
| 519 | LOADPARM_LEN); | ||
| 520 | EBCASC(loadparm, LOADPARM_LEN); | 659 | EBCASC(loadparm, LOADPARM_LEN); |
| 521 | loadparm[LOADPARM_LEN] = 0; | 660 | loadparm[LOADPARM_LEN] = 0; |
| 522 | strstrip(loadparm); | 661 | strstrip(loadparm); |
| 523 | } | 662 | } |
| 524 | 663 | ||
| 525 | static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, | 664 | static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb, |
| 526 | struct kobj_attribute *attr, char *page) | 665 | char *page) |
| 527 | { | 666 | { |
| 528 | char buf[LOADPARM_LEN + 1]; | 667 | char buf[LOADPARM_LEN + 1]; |
| 529 | 668 | ||
| 530 | reipl_get_ascii_loadparm(buf); | 669 | reipl_get_ascii_loadparm(buf, ipb); |
| 531 | return sprintf(page, "%s\n", buf); | 670 | return sprintf(page, "%s\n", buf); |
| 532 | } | 671 | } |
| 533 | 672 | ||
| 534 | static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, | 673 | static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, |
| 535 | struct kobj_attribute *attr, | 674 | const char *buf, size_t len) |
| 536 | const char *buf, size_t len) | ||
| 537 | { | 675 | { |
| 538 | int i, lp_len; | 676 | int i, lp_len; |
| 539 | 677 | ||
| @@ -552,35 +690,128 @@ static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, | |||
| 552 | return -EINVAL; | 690 | return -EINVAL; |
| 553 | } | 691 | } |
| 554 | /* initialize loadparm with blanks */ | 692 | /* initialize loadparm with blanks */ |
| 555 | memset(&reipl_block_ccw->ipl_info.ccw.load_param, ' ', LOADPARM_LEN); | 693 | memset(ipb->ipl_info.ccw.load_parm, ' ', LOADPARM_LEN); |
| 556 | /* copy and convert to ebcdic */ | 694 | /* copy and convert to ebcdic */ |
| 557 | memcpy(&reipl_block_ccw->ipl_info.ccw.load_param, buf, lp_len); | 695 | memcpy(ipb->ipl_info.ccw.load_parm, buf, lp_len); |
| 558 | ASCEBC(reipl_block_ccw->ipl_info.ccw.load_param, LOADPARM_LEN); | 696 | ASCEBC(ipb->ipl_info.ccw.load_parm, LOADPARM_LEN); |
| 559 | return len; | 697 | return len; |
| 560 | } | 698 | } |
| 561 | 699 | ||
| 700 | /* NSS wrapper */ | ||
| 701 | static ssize_t reipl_nss_loadparm_show(struct kobject *kobj, | ||
| 702 | struct kobj_attribute *attr, char *page) | ||
| 703 | { | ||
| 704 | return reipl_generic_loadparm_show(reipl_block_nss, page); | ||
| 705 | } | ||
| 706 | |||
| 707 | static ssize_t reipl_nss_loadparm_store(struct kobject *kobj, | ||
| 708 | struct kobj_attribute *attr, | ||
| 709 | const char *buf, size_t len) | ||
| 710 | { | ||
| 711 | return reipl_generic_loadparm_store(reipl_block_nss, buf, len); | ||
| 712 | } | ||
| 713 | |||
| 714 | /* CCW wrapper */ | ||
| 715 | static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, | ||
| 716 | struct kobj_attribute *attr, char *page) | ||
| 717 | { | ||
| 718 | return reipl_generic_loadparm_show(reipl_block_ccw, page); | ||
| 719 | } | ||
| 720 | |||
| 721 | static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, | ||
| 722 | struct kobj_attribute *attr, | ||
| 723 | const char *buf, size_t len) | ||
| 724 | { | ||
| 725 | return reipl_generic_loadparm_store(reipl_block_ccw, buf, len); | ||
| 726 | } | ||
| 727 | |||
| 562 | static struct kobj_attribute sys_reipl_ccw_loadparm_attr = | 728 | static struct kobj_attribute sys_reipl_ccw_loadparm_attr = |
| 563 | __ATTR(loadparm, 0644, reipl_ccw_loadparm_show, | 729 | __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_ccw_loadparm_show, |
| 564 | reipl_ccw_loadparm_store); | 730 | reipl_ccw_loadparm_store); |
| 565 | 731 | ||
| 566 | static struct attribute *reipl_ccw_attrs[] = { | 732 | static struct attribute *reipl_ccw_attrs_vm[] = { |
| 567 | &sys_reipl_ccw_device_attr.attr, | 733 | &sys_reipl_ccw_device_attr.attr, |
| 568 | &sys_reipl_ccw_loadparm_attr.attr, | 734 | &sys_reipl_ccw_loadparm_attr.attr, |
| 735 | &sys_reipl_ccw_vmparm_attr.attr, | ||
| 569 | NULL, | 736 | NULL, |
| 570 | }; | 737 | }; |
| 571 | 738 | ||
| 572 | static struct attribute_group reipl_ccw_attr_group = { | 739 | static struct attribute *reipl_ccw_attrs_lpar[] = { |
| 740 | &sys_reipl_ccw_device_attr.attr, | ||
| 741 | &sys_reipl_ccw_loadparm_attr.attr, | ||
| 742 | NULL, | ||
| 743 | }; | ||
| 744 | |||
| 745 | static struct attribute_group reipl_ccw_attr_group_vm = { | ||
| 746 | .name = IPL_CCW_STR, | ||
| 747 | .attrs = reipl_ccw_attrs_vm, | ||
| 748 | }; | ||
| 749 | |||
| 750 | static struct attribute_group reipl_ccw_attr_group_lpar = { | ||
| 573 | .name = IPL_CCW_STR, | 751 | .name = IPL_CCW_STR, |
| 574 | .attrs = reipl_ccw_attrs, | 752 | .attrs = reipl_ccw_attrs_lpar, |
| 575 | }; | 753 | }; |
| 576 | 754 | ||
| 577 | 755 | ||
| 578 | /* NSS reipl device attributes */ | 756 | /* NSS reipl device attributes */ |
| 757 | static void reipl_get_ascii_nss_name(char *dst, | ||
| 758 | struct ipl_parameter_block *ipb) | ||
| 759 | { | ||
| 760 | memcpy(dst, ipb->ipl_info.ccw.nss_name, NSS_NAME_SIZE); | ||
| 761 | EBCASC(dst, NSS_NAME_SIZE); | ||
| 762 | dst[NSS_NAME_SIZE] = 0; | ||
| 763 | } | ||
| 764 | |||
| 765 | static ssize_t reipl_nss_name_show(struct kobject *kobj, | ||
| 766 | struct kobj_attribute *attr, char *page) | ||
| 767 | { | ||
| 768 | char nss_name[NSS_NAME_SIZE + 1] = {}; | ||
| 579 | 769 | ||
| 580 | DEFINE_IPL_ATTR_STR_RW(reipl_nss, name, "%s\n", "%s\n", reipl_nss_name); | 770 | reipl_get_ascii_nss_name(nss_name, reipl_block_nss); |
| 771 | return sprintf(page, "%s\n", nss_name); | ||
| 772 | } | ||
| 773 | |||
| 774 | static ssize_t reipl_nss_name_store(struct kobject *kobj, | ||
| 775 | struct kobj_attribute *attr, | ||
| 776 | const char *buf, size_t len) | ||
| 777 | { | ||
| 778 | int nss_len; | ||
| 779 | |||
| 780 | /* ignore trailing newline */ | ||
| 781 | nss_len = len; | ||
| 782 | if ((len > 0) && (buf[len - 1] == '\n')) | ||
| 783 | nss_len--; | ||
| 784 | |||
| 785 | if (nss_len > NSS_NAME_SIZE) | ||
| 786 | return -EINVAL; | ||
| 787 | |||
| 788 | memset(reipl_block_nss->ipl_info.ccw.nss_name, 0x40, NSS_NAME_SIZE); | ||
| 789 | if (nss_len > 0) { | ||
| 790 | reipl_block_nss->ipl_info.ccw.vm_flags |= | ||
| 791 | DIAG308_VM_FLAGS_NSS_VALID; | ||
| 792 | memcpy(reipl_block_nss->ipl_info.ccw.nss_name, buf, nss_len); | ||
| 793 | ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, nss_len); | ||
| 794 | EBC_TOUPPER(reipl_block_nss->ipl_info.ccw.nss_name, nss_len); | ||
| 795 | } else { | ||
| 796 | reipl_block_nss->ipl_info.ccw.vm_flags &= | ||
| 797 | ~DIAG308_VM_FLAGS_NSS_VALID; | ||
| 798 | } | ||
| 799 | |||
| 800 | return len; | ||
| 801 | } | ||
| 802 | |||
| 803 | static struct kobj_attribute sys_reipl_nss_name_attr = | ||
| 804 | __ATTR(name, S_IRUGO | S_IWUSR, reipl_nss_name_show, | ||
| 805 | reipl_nss_name_store); | ||
| 806 | |||
| 807 | static struct kobj_attribute sys_reipl_nss_loadparm_attr = | ||
| 808 | __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_nss_loadparm_show, | ||
| 809 | reipl_nss_loadparm_store); | ||
| 581 | 810 | ||
| 582 | static struct attribute *reipl_nss_attrs[] = { | 811 | static struct attribute *reipl_nss_attrs[] = { |
| 583 | &sys_reipl_nss_name_attr.attr, | 812 | &sys_reipl_nss_name_attr.attr, |
| 813 | &sys_reipl_nss_loadparm_attr.attr, | ||
| 814 | &sys_reipl_nss_vmparm_attr.attr, | ||
| 584 | NULL, | 815 | NULL, |
| 585 | }; | 816 | }; |
| 586 | 817 | ||
| @@ -617,7 +848,10 @@ static int reipl_set_type(enum ipl_type type) | |||
| 617 | reipl_method = REIPL_METHOD_FCP_DUMP; | 848 | reipl_method = REIPL_METHOD_FCP_DUMP; |
| 618 | break; | 849 | break; |
| 619 | case IPL_TYPE_NSS: | 850 | case IPL_TYPE_NSS: |
| 620 | reipl_method = REIPL_METHOD_NSS; | 851 | if (diag308_set_works) |
| 852 | reipl_method = REIPL_METHOD_NSS_DIAG; | ||
| 853 | else | ||
| 854 | reipl_method = REIPL_METHOD_NSS; | ||
| 621 | break; | 855 | break; |
| 622 | case IPL_TYPE_UNKNOWN: | 856 | case IPL_TYPE_UNKNOWN: |
| 623 | reipl_method = REIPL_METHOD_DEFAULT; | 857 | reipl_method = REIPL_METHOD_DEFAULT; |
| @@ -655,11 +889,38 @@ static struct kobj_attribute reipl_type_attr = | |||
| 655 | 889 | ||
| 656 | static struct kset *reipl_kset; | 890 | static struct kset *reipl_kset; |
| 657 | 891 | ||
| 892 | static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, | ||
| 893 | const enum ipl_method m) | ||
| 894 | { | ||
| 895 | char loadparm[LOADPARM_LEN + 1] = {}; | ||
| 896 | char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; | ||
| 897 | char nss_name[NSS_NAME_SIZE + 1] = {}; | ||
| 898 | size_t pos = 0; | ||
| 899 | |||
| 900 | reipl_get_ascii_loadparm(loadparm, ipb); | ||
| 901 | reipl_get_ascii_nss_name(nss_name, ipb); | ||
| 902 | reipl_get_ascii_vmparm(vmparm, ipb); | ||
| 903 | |||
| 904 | switch (m) { | ||
| 905 | case REIPL_METHOD_CCW_VM: | ||
| 906 | pos = sprintf(dst, "IPL %X CLEAR", ipb->ipl_info.ccw.devno); | ||
| 907 | break; | ||
| 908 | case REIPL_METHOD_NSS: | ||
| 909 | pos = sprintf(dst, "IPL %s", nss_name); | ||
| 910 | break; | ||
| 911 | default: | ||
| 912 | break; | ||
| 913 | } | ||
| 914 | if (strlen(loadparm) > 0) | ||
| 915 | pos += sprintf(dst + pos, " LOADPARM '%s'", loadparm); | ||
| 916 | if (strlen(vmparm) > 0) | ||
| 917 | sprintf(dst + pos, " PARM %s", vmparm); | ||
| 918 | } | ||
| 919 | |||
| 658 | static void reipl_run(struct shutdown_trigger *trigger) | 920 | static void reipl_run(struct shutdown_trigger *trigger) |
| 659 | { | 921 | { |
| 660 | struct ccw_dev_id devid; | 922 | struct ccw_dev_id devid; |
| 661 | static char buf[100]; | 923 | static char buf[128]; |
| 662 | char loadparm[LOADPARM_LEN + 1]; | ||
| 663 | 924 | ||
| 664 | switch (reipl_method) { | 925 | switch (reipl_method) { |
| 665 | case REIPL_METHOD_CCW_CIO: | 926 | case REIPL_METHOD_CCW_CIO: |
| @@ -668,13 +929,7 @@ static void reipl_run(struct shutdown_trigger *trigger) | |||
| 668 | reipl_ccw_dev(&devid); | 929 | reipl_ccw_dev(&devid); |
| 669 | break; | 930 | break; |
| 670 | case REIPL_METHOD_CCW_VM: | 931 | case REIPL_METHOD_CCW_VM: |
| 671 | reipl_get_ascii_loadparm(loadparm); | 932 | get_ipl_string(buf, reipl_block_ccw, REIPL_METHOD_CCW_VM); |
| 672 | if (strlen(loadparm) == 0) | ||
| 673 | sprintf(buf, "IPL %X CLEAR", | ||
| 674 | reipl_block_ccw->ipl_info.ccw.devno); | ||
| 675 | else | ||
| 676 | sprintf(buf, "IPL %X CLEAR LOADPARM '%s'", | ||
| 677 | reipl_block_ccw->ipl_info.ccw.devno, loadparm); | ||
| 678 | __cpcmd(buf, NULL, 0, NULL); | 933 | __cpcmd(buf, NULL, 0, NULL); |
| 679 | break; | 934 | break; |
| 680 | case REIPL_METHOD_CCW_DIAG: | 935 | case REIPL_METHOD_CCW_DIAG: |
| @@ -691,8 +946,12 @@ static void reipl_run(struct shutdown_trigger *trigger) | |||
| 691 | case REIPL_METHOD_FCP_RO_VM: | 946 | case REIPL_METHOD_FCP_RO_VM: |
| 692 | __cpcmd("IPL", NULL, 0, NULL); | 947 | __cpcmd("IPL", NULL, 0, NULL); |
| 693 | break; | 948 | break; |
| 949 | case REIPL_METHOD_NSS_DIAG: | ||
| 950 | diag308(DIAG308_SET, reipl_block_nss); | ||
| 951 | diag308(DIAG308_IPL, NULL); | ||
| 952 | break; | ||
| 694 | case REIPL_METHOD_NSS: | 953 | case REIPL_METHOD_NSS: |
| 695 | sprintf(buf, "IPL %s", reipl_nss_name); | 954 | get_ipl_string(buf, reipl_block_nss, REIPL_METHOD_NSS); |
| 696 | __cpcmd(buf, NULL, 0, NULL); | 955 | __cpcmd(buf, NULL, 0, NULL); |
| 697 | break; | 956 | break; |
| 698 | case REIPL_METHOD_DEFAULT: | 957 | case REIPL_METHOD_DEFAULT: |
| @@ -707,16 +966,36 @@ static void reipl_run(struct shutdown_trigger *trigger) | |||
| 707 | disabled_wait((unsigned long) __builtin_return_address(0)); | 966 | disabled_wait((unsigned long) __builtin_return_address(0)); |
| 708 | } | 967 | } |
| 709 | 968 | ||
| 710 | static void __init reipl_probe(void) | 969 | static void reipl_block_ccw_init(struct ipl_parameter_block *ipb) |
| 711 | { | 970 | { |
| 712 | void *buffer; | 971 | ipb->hdr.len = IPL_PARM_BLK_CCW_LEN; |
| 972 | ipb->hdr.version = IPL_PARM_BLOCK_VERSION; | ||
| 973 | ipb->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; | ||
| 974 | ipb->hdr.pbt = DIAG308_IPL_TYPE_CCW; | ||
| 975 | } | ||
| 713 | 976 | ||
| 714 | buffer = (void *) get_zeroed_page(GFP_KERNEL); | 977 | static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb) |
| 715 | if (!buffer) | 978 | { |
| 716 | return; | 979 | /* LOADPARM */ |
| 717 | if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) | 980 | /* check if read scp info worked and set loadparm */ |
| 718 | diag308_set_works = 1; | 981 | if (sclp_ipl_info.is_valid) |
| 719 | free_page((unsigned long)buffer); | 982 | memcpy(ipb->ipl_info.ccw.load_parm, |
| 983 | &sclp_ipl_info.loadparm, LOADPARM_LEN); | ||
| 984 | else | ||
| 985 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | ||
| 986 | memset(ipb->ipl_info.ccw.load_parm, 0x40, LOADPARM_LEN); | ||
| 987 | ipb->hdr.flags = DIAG308_FLAGS_LP_VALID; | ||
| 988 | |||
| 989 | /* VM PARM */ | ||
| 990 | if (MACHINE_IS_VM && diag308_set_works && | ||
| 991 | (ipl_block.ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID)) { | ||
| 992 | |||
| 993 | ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID; | ||
| 994 | ipb->ipl_info.ccw.vm_parm_len = | ||
| 995 | ipl_block.ipl_info.ccw.vm_parm_len; | ||
| 996 | memcpy(ipb->ipl_info.ccw.vm_parm, | ||
| 997 | ipl_block.ipl_info.ccw.vm_parm, DIAG308_VMPARM_SIZE); | ||
| 998 | } | ||
| 720 | } | 999 | } |
| 721 | 1000 | ||
| 722 | static int __init reipl_nss_init(void) | 1001 | static int __init reipl_nss_init(void) |
| @@ -725,10 +1004,31 @@ static int __init reipl_nss_init(void) | |||
| 725 | 1004 | ||
| 726 | if (!MACHINE_IS_VM) | 1005 | if (!MACHINE_IS_VM) |
| 727 | return 0; | 1006 | return 0; |
| 1007 | |||
| 1008 | reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL); | ||
| 1009 | if (!reipl_block_nss) | ||
| 1010 | return -ENOMEM; | ||
| 1011 | |||
| 1012 | if (!diag308_set_works) | ||
| 1013 | sys_reipl_nss_vmparm_attr.attr.mode = S_IRUGO; | ||
| 1014 | |||
| 728 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); | 1015 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); |
| 729 | if (rc) | 1016 | if (rc) |
| 730 | return rc; | 1017 | return rc; |
| 731 | strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1); | 1018 | |
| 1019 | reipl_block_ccw_init(reipl_block_nss); | ||
| 1020 | if (ipl_info.type == IPL_TYPE_NSS) { | ||
| 1021 | memset(reipl_block_nss->ipl_info.ccw.nss_name, | ||
| 1022 | ' ', NSS_NAME_SIZE); | ||
| 1023 | memcpy(reipl_block_nss->ipl_info.ccw.nss_name, | ||
| 1024 | kernel_nss_name, strlen(kernel_nss_name)); | ||
| 1025 | ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, NSS_NAME_SIZE); | ||
| 1026 | reipl_block_nss->ipl_info.ccw.vm_flags |= | ||
| 1027 | DIAG308_VM_FLAGS_NSS_VALID; | ||
| 1028 | |||
| 1029 | reipl_block_ccw_fill_parms(reipl_block_nss); | ||
| 1030 | } | ||
| 1031 | |||
| 732 | reipl_capabilities |= IPL_TYPE_NSS; | 1032 | reipl_capabilities |= IPL_TYPE_NSS; |
| 733 | return 0; | 1033 | return 0; |
| 734 | } | 1034 | } |
| @@ -740,28 +1040,27 @@ static int __init reipl_ccw_init(void) | |||
| 740 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); | 1040 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); |
| 741 | if (!reipl_block_ccw) | 1041 | if (!reipl_block_ccw) |
| 742 | return -ENOMEM; | 1042 | return -ENOMEM; |
| 743 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_ccw_attr_group); | 1043 | |
| 744 | if (rc) { | 1044 | if (MACHINE_IS_VM) { |
| 745 | free_page((unsigned long)reipl_block_ccw); | 1045 | if (!diag308_set_works) |
| 746 | return rc; | 1046 | sys_reipl_ccw_vmparm_attr.attr.mode = S_IRUGO; |
| 1047 | rc = sysfs_create_group(&reipl_kset->kobj, | ||
| 1048 | &reipl_ccw_attr_group_vm); | ||
| 1049 | } else { | ||
| 1050 | if(!diag308_set_works) | ||
| 1051 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | ||
| 1052 | rc = sysfs_create_group(&reipl_kset->kobj, | ||
| 1053 | &reipl_ccw_attr_group_lpar); | ||
| 747 | } | 1054 | } |
| 748 | reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN; | 1055 | if (rc) |
| 749 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | 1056 | return rc; |
| 750 | reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; | 1057 | |
| 751 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; | 1058 | reipl_block_ccw_init(reipl_block_ccw); |
| 752 | reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID; | 1059 | if (ipl_info.type == IPL_TYPE_CCW) { |
| 753 | /* check if read scp info worked and set loadparm */ | ||
| 754 | if (sclp_ipl_info.is_valid) | ||
| 755 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, | ||
| 756 | &sclp_ipl_info.loadparm, LOADPARM_LEN); | ||
| 757 | else | ||
| 758 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | ||
| 759 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, | ||
| 760 | LOADPARM_LEN); | ||
| 761 | if (!MACHINE_IS_VM && !diag308_set_works) | ||
| 762 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | ||
| 763 | if (ipl_info.type == IPL_TYPE_CCW) | ||
| 764 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; | 1060 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; |
| 1061 | reipl_block_ccw_fill_parms(reipl_block_ccw); | ||
| 1062 | } | ||
| 1063 | |||
| 765 | reipl_capabilities |= IPL_TYPE_CCW; | 1064 | reipl_capabilities |= IPL_TYPE_CCW; |
| 766 | return 0; | 1065 | return 0; |
| 767 | } | 1066 | } |
| @@ -1298,7 +1597,6 @@ static void __init shutdown_actions_init(void) | |||
| 1298 | 1597 | ||
| 1299 | static int __init s390_ipl_init(void) | 1598 | static int __init s390_ipl_init(void) |
| 1300 | { | 1599 | { |
| 1301 | reipl_probe(); | ||
| 1302 | sclp_get_ipl_info(&sclp_ipl_info); | 1600 | sclp_get_ipl_info(&sclp_ipl_info); |
| 1303 | shutdown_actions_init(); | 1601 | shutdown_actions_init(); |
| 1304 | shutdown_triggers_init(); | 1602 | shutdown_triggers_init(); |
| @@ -1405,6 +1703,12 @@ void __init setup_ipl(void) | |||
| 1405 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); | 1703 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); |
| 1406 | } | 1704 | } |
| 1407 | 1705 | ||
| 1706 | void __init ipl_update_parameters(void) | ||
| 1707 | { | ||
| 1708 | if (diag308(DIAG308_STORE, &ipl_block) == DIAG308_RC_OK) | ||
| 1709 | diag308_set_works = 1; | ||
| 1710 | } | ||
| 1711 | |||
| 1408 | void __init ipl_save_parameters(void) | 1712 | void __init ipl_save_parameters(void) |
| 1409 | { | 1713 | { |
| 1410 | struct cio_iplinfo iplinfo; | 1714 | struct cio_iplinfo iplinfo; |
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index ed04d1372d5d..288ad490a6dd 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
| @@ -41,10 +41,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
| 41 | if (is_prohibited_opcode((kprobe_opcode_t *) p->addr)) | 41 | if (is_prohibited_opcode((kprobe_opcode_t *) p->addr)) |
| 42 | return -EINVAL; | 42 | return -EINVAL; |
| 43 | 43 | ||
| 44 | if ((unsigned long)p->addr & 0x01) { | 44 | if ((unsigned long)p->addr & 0x01) |
| 45 | printk("Attempt to register kprobe at an unaligned address\n"); | ||
| 46 | return -EINVAL; | 45 | return -EINVAL; |
| 47 | } | ||
| 48 | 46 | ||
| 49 | /* Use the get_insn_slot() facility for correctness */ | 47 | /* Use the get_insn_slot() facility for correctness */ |
| 50 | if (!(p->ainsn.insn = get_insn_slot())) | 48 | if (!(p->ainsn.insn = get_insn_slot())) |
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 3c77dd36994c..131d7ee8b416 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c | |||
| @@ -52,7 +52,6 @@ void machine_kexec_cleanup(struct kimage *image) | |||
| 52 | 52 | ||
| 53 | void machine_shutdown(void) | 53 | void machine_shutdown(void) |
| 54 | { | 54 | { |
| 55 | printk(KERN_INFO "kexec: machine_shutdown called\n"); | ||
| 56 | } | 55 | } |
| 57 | 56 | ||
| 58 | void machine_kexec(struct kimage *image) | 57 | void machine_kexec(struct kimage *image) |
diff --git a/arch/s390/kernel/mem_detect.c b/arch/s390/kernel/mem_detect.c new file mode 100644 index 000000000000..18ed7abe16c5 --- /dev/null +++ b/arch/s390/kernel/mem_detect.c | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | /* | ||
| 2 | * Copyright IBM Corp. 2008 | ||
| 3 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <linux/kernel.h> | ||
| 7 | #include <linux/module.h> | ||
| 8 | #include <asm/ipl.h> | ||
| 9 | #include <asm/sclp.h> | ||
| 10 | #include <asm/setup.h> | ||
| 11 | |||
| 12 | static int memory_fast_detect(struct mem_chunk *chunk) | ||
| 13 | { | ||
| 14 | unsigned long val0 = 0; | ||
| 15 | unsigned long val1 = 0xc; | ||
| 16 | int rc = -EOPNOTSUPP; | ||
| 17 | |||
| 18 | if (ipl_flags & IPL_NSS_VALID) | ||
| 19 | return -EOPNOTSUPP; | ||
| 20 | asm volatile( | ||
| 21 | " diag %1,%2,0x260\n" | ||
| 22 | "0: lhi %0,0\n" | ||
| 23 | "1:\n" | ||
| 24 | EX_TABLE(0b,1b) | ||
| 25 | : "+d" (rc), "+d" (val0), "+d" (val1) : : "cc"); | ||
| 26 | |||
| 27 | if (rc || val0 != val1) | ||
| 28 | return -EOPNOTSUPP; | ||
| 29 | chunk->size = val0 + 1; | ||
| 30 | return 0; | ||
| 31 | } | ||
| 32 | |||
| 33 | static inline int tprot(unsigned long addr) | ||
| 34 | { | ||
| 35 | int rc = -EFAULT; | ||
| 36 | |||
| 37 | asm volatile( | ||
| 38 | " tprot 0(%1),0\n" | ||
| 39 | "0: ipm %0\n" | ||
| 40 | " srl %0,28\n" | ||
| 41 | "1:\n" | ||
| 42 | EX_TABLE(0b,1b) | ||
| 43 | : "+d" (rc) : "a" (addr) : "cc"); | ||
| 44 | return rc; | ||
| 45 | } | ||
| 46 | |||
| 47 | #define ADDR2G (1ULL << 31) | ||
| 48 | |||
| 49 | static void find_memory_chunks(struct mem_chunk chunk[]) | ||
| 50 | { | ||
| 51 | unsigned long long memsize, rnmax, rzm; | ||
| 52 | unsigned long addr = 0, size; | ||
| 53 | int i = 0, type; | ||
| 54 | |||
| 55 | rzm = sclp_get_rzm(); | ||
| 56 | rnmax = sclp_get_rnmax(); | ||
| 57 | memsize = rzm * rnmax; | ||
| 58 | if (!rzm) | ||
| 59 | rzm = 1ULL << 17; | ||
| 60 | if (sizeof(long) == 4) { | ||
| 61 | rzm = min(ADDR2G, rzm); | ||
| 62 | memsize = memsize ? min(ADDR2G, memsize) : ADDR2G; | ||
| 63 | } | ||
| 64 | do { | ||
| 65 | size = 0; | ||
| 66 | type = tprot(addr); | ||
| 67 | do { | ||
| 68 | size += rzm; | ||
| 69 | if (memsize && addr + size >= memsize) | ||
| 70 | break; | ||
| 71 | } while (type == tprot(addr + size)); | ||
| 72 | if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) { | ||
| 73 | chunk[i].addr = addr; | ||
| 74 | chunk[i].size = size; | ||
| 75 | chunk[i].type = type; | ||
| 76 | i++; | ||
| 77 | } | ||
| 78 | addr += size; | ||
| 79 | } while (addr < memsize && i < MEMORY_CHUNKS); | ||
| 80 | } | ||
| 81 | |||
| 82 | void detect_memory_layout(struct mem_chunk chunk[]) | ||
| 83 | { | ||
| 84 | unsigned long flags, cr0; | ||
| 85 | |||
| 86 | memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk)); | ||
| 87 | if (memory_fast_detect(&chunk[0]) == 0) | ||
| 88 | return; | ||
| 89 | /* Disable IRQs, DAT and low address protection so tprot does the | ||
| 90 | * right thing and we don't get scheduled away with low address | ||
| 91 | * protection disabled. | ||
| 92 | */ | ||
| 93 | flags = __raw_local_irq_stnsm(0xf8); | ||
| 94 | __ctl_store(cr0, 0, 0); | ||
| 95 | __ctl_clear_bit(0, 28); | ||
| 96 | find_memory_chunks(chunk); | ||
| 97 | __ctl_load(cr0, 0, 0); | ||
| 98 | __raw_local_irq_ssm(flags); | ||
| 99 | } | ||
| 100 | EXPORT_SYMBOL(detect_memory_layout); | ||
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 7920861109d2..85defd01d293 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c | |||
| @@ -75,46 +75,19 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
| 75 | return sf->gprs[8]; | 75 | return sf->gprs[8]; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | /* | ||
| 79 | * Need to know about CPUs going idle? | ||
| 80 | */ | ||
| 81 | static ATOMIC_NOTIFIER_HEAD(idle_chain); | ||
| 82 | DEFINE_PER_CPU(struct s390_idle_data, s390_idle); | 78 | DEFINE_PER_CPU(struct s390_idle_data, s390_idle); |
| 83 | 79 | ||
| 84 | int register_idle_notifier(struct notifier_block *nb) | ||
| 85 | { | ||
| 86 | return atomic_notifier_chain_register(&idle_chain, nb); | ||
| 87 | } | ||
| 88 | EXPORT_SYMBOL(register_idle_notifier); | ||
| 89 | |||
| 90 | int unregister_idle_notifier(struct notifier_block *nb) | ||
| 91 | { | ||
| 92 | return atomic_notifier_chain_unregister(&idle_chain, nb); | ||
| 93 | } | ||
| 94 | EXPORT_SYMBOL(unregister_idle_notifier); | ||
| 95 | |||
| 96 | static int s390_idle_enter(void) | 80 | static int s390_idle_enter(void) |
| 97 | { | 81 | { |
| 98 | struct s390_idle_data *idle; | 82 | struct s390_idle_data *idle; |
| 99 | int nr_calls = 0; | ||
| 100 | void *hcpu; | ||
| 101 | int rc; | ||
| 102 | 83 | ||
| 103 | hcpu = (void *)(long)smp_processor_id(); | ||
| 104 | rc = __atomic_notifier_call_chain(&idle_chain, S390_CPU_IDLE, hcpu, -1, | ||
| 105 | &nr_calls); | ||
| 106 | if (rc == NOTIFY_BAD) { | ||
| 107 | nr_calls--; | ||
| 108 | __atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE, | ||
| 109 | hcpu, nr_calls, NULL); | ||
| 110 | return rc; | ||
| 111 | } | ||
| 112 | idle = &__get_cpu_var(s390_idle); | 84 | idle = &__get_cpu_var(s390_idle); |
| 113 | spin_lock(&idle->lock); | 85 | spin_lock(&idle->lock); |
| 114 | idle->idle_count++; | 86 | idle->idle_count++; |
| 115 | idle->in_idle = 1; | 87 | idle->in_idle = 1; |
| 116 | idle->idle_enter = get_clock(); | 88 | idle->idle_enter = get_clock(); |
| 117 | spin_unlock(&idle->lock); | 89 | spin_unlock(&idle->lock); |
| 90 | vtime_stop_cpu_timer(); | ||
| 118 | return NOTIFY_OK; | 91 | return NOTIFY_OK; |
| 119 | } | 92 | } |
| 120 | 93 | ||
| @@ -122,13 +95,12 @@ void s390_idle_leave(void) | |||
| 122 | { | 95 | { |
| 123 | struct s390_idle_data *idle; | 96 | struct s390_idle_data *idle; |
| 124 | 97 | ||
| 98 | vtime_start_cpu_timer(); | ||
| 125 | idle = &__get_cpu_var(s390_idle); | 99 | idle = &__get_cpu_var(s390_idle); |
| 126 | spin_lock(&idle->lock); | 100 | spin_lock(&idle->lock); |
| 127 | idle->idle_time += get_clock() - idle->idle_enter; | 101 | idle->idle_time += get_clock() - idle->idle_enter; |
| 128 | idle->in_idle = 0; | 102 | idle->in_idle = 0; |
| 129 | spin_unlock(&idle->lock); | 103 | spin_unlock(&idle->lock); |
| 130 | atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE, | ||
| 131 | (void *)(long) smp_processor_id()); | ||
| 132 | } | 104 | } |
| 133 | 105 | ||
| 134 | extern void s390_handle_mcck(void); | 106 | extern void s390_handle_mcck(void); |
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 35827b9bd4d1..2815bfe348a6 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | #include <linux/security.h> | 33 | #include <linux/security.h> |
| 34 | #include <linux/audit.h> | 34 | #include <linux/audit.h> |
| 35 | #include <linux/signal.h> | 35 | #include <linux/signal.h> |
| 36 | #include <linux/elf.h> | ||
| 37 | #include <linux/regset.h> | ||
| 36 | 38 | ||
| 37 | #include <asm/segment.h> | 39 | #include <asm/segment.h> |
| 38 | #include <asm/page.h> | 40 | #include <asm/page.h> |
| @@ -47,6 +49,11 @@ | |||
| 47 | #include "compat_ptrace.h" | 49 | #include "compat_ptrace.h" |
| 48 | #endif | 50 | #endif |
| 49 | 51 | ||
| 52 | enum s390_regset { | ||
| 53 | REGSET_GENERAL, | ||
| 54 | REGSET_FP, | ||
| 55 | }; | ||
| 56 | |||
| 50 | static void | 57 | static void |
| 51 | FixPerRegisters(struct task_struct *task) | 58 | FixPerRegisters(struct task_struct *task) |
| 52 | { | 59 | { |
| @@ -126,24 +133,10 @@ ptrace_disable(struct task_struct *child) | |||
| 126 | * struct user contain pad bytes that should be read as zeroes. | 133 | * struct user contain pad bytes that should be read as zeroes. |
| 127 | * Lovely... | 134 | * Lovely... |
| 128 | */ | 135 | */ |
| 129 | static int | 136 | static unsigned long __peek_user(struct task_struct *child, addr_t addr) |
| 130 | peek_user(struct task_struct *child, addr_t addr, addr_t data) | ||
| 131 | { | 137 | { |
| 132 | struct user *dummy = NULL; | 138 | struct user *dummy = NULL; |
| 133 | addr_t offset, tmp, mask; | 139 | addr_t offset, tmp; |
| 134 | |||
| 135 | /* | ||
| 136 | * Stupid gdb peeks/pokes the access registers in 64 bit with | ||
| 137 | * an alignment of 4. Programmers from hell... | ||
| 138 | */ | ||
| 139 | mask = __ADDR_MASK; | ||
| 140 | #ifdef CONFIG_64BIT | ||
| 141 | if (addr >= (addr_t) &dummy->regs.acrs && | ||
| 142 | addr < (addr_t) &dummy->regs.orig_gpr2) | ||
| 143 | mask = 3; | ||
| 144 | #endif | ||
| 145 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | ||
| 146 | return -EIO; | ||
| 147 | 140 | ||
| 148 | if (addr < (addr_t) &dummy->regs.acrs) { | 141 | if (addr < (addr_t) &dummy->regs.acrs) { |
| 149 | /* | 142 | /* |
| @@ -197,24 +190,18 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data) | |||
| 197 | } else | 190 | } else |
| 198 | tmp = 0; | 191 | tmp = 0; |
| 199 | 192 | ||
| 200 | return put_user(tmp, (addr_t __user *) data); | 193 | return tmp; |
| 201 | } | 194 | } |
| 202 | 195 | ||
| 203 | /* | ||
| 204 | * Write a word to the user area of a process at location addr. This | ||
| 205 | * operation does have an additional problem compared to peek_user. | ||
| 206 | * Stores to the program status word and on the floating point | ||
| 207 | * control register needs to get checked for validity. | ||
| 208 | */ | ||
| 209 | static int | 196 | static int |
| 210 | poke_user(struct task_struct *child, addr_t addr, addr_t data) | 197 | peek_user(struct task_struct *child, addr_t addr, addr_t data) |
| 211 | { | 198 | { |
| 212 | struct user *dummy = NULL; | 199 | struct user *dummy = NULL; |
| 213 | addr_t offset, mask; | 200 | addr_t tmp, mask; |
| 214 | 201 | ||
| 215 | /* | 202 | /* |
| 216 | * Stupid gdb peeks/pokes the access registers in 64 bit with | 203 | * Stupid gdb peeks/pokes the access registers in 64 bit with |
| 217 | * an alignment of 4. Programmers from hell indeed... | 204 | * an alignment of 4. Programmers from hell... |
| 218 | */ | 205 | */ |
| 219 | mask = __ADDR_MASK; | 206 | mask = __ADDR_MASK; |
| 220 | #ifdef CONFIG_64BIT | 207 | #ifdef CONFIG_64BIT |
| @@ -225,6 +212,21 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) | |||
| 225 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | 212 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) |
| 226 | return -EIO; | 213 | return -EIO; |
| 227 | 214 | ||
| 215 | tmp = __peek_user(child, addr); | ||
| 216 | return put_user(tmp, (addr_t __user *) data); | ||
| 217 | } | ||
| 218 | |||
| 219 | /* | ||
| 220 | * Write a word to the user area of a process at location addr. This | ||
| 221 | * operation does have an additional problem compared to peek_user. | ||
| 222 | * Stores to the program status word and on the floating point | ||
| 223 | * control register needs to get checked for validity. | ||
| 224 | */ | ||
| 225 | static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) | ||
| 226 | { | ||
| 227 | struct user *dummy = NULL; | ||
| 228 | addr_t offset; | ||
| 229 | |||
| 228 | if (addr < (addr_t) &dummy->regs.acrs) { | 230 | if (addr < (addr_t) &dummy->regs.acrs) { |
| 229 | /* | 231 | /* |
| 230 | * psw and gprs are stored on the stack | 232 | * psw and gprs are stored on the stack |
| @@ -292,6 +294,28 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) | |||
| 292 | return 0; | 294 | return 0; |
| 293 | } | 295 | } |
| 294 | 296 | ||
| 297 | static int | ||
| 298 | poke_user(struct task_struct *child, addr_t addr, addr_t data) | ||
| 299 | { | ||
| 300 | struct user *dummy = NULL; | ||
| 301 | addr_t mask; | ||
| 302 | |||
| 303 | /* | ||
| 304 | * Stupid gdb peeks/pokes the access registers in 64 bit with | ||
| 305 | * an alignment of 4. Programmers from hell indeed... | ||
| 306 | */ | ||
| 307 | mask = __ADDR_MASK; | ||
| 308 | #ifdef CONFIG_64BIT | ||
| 309 | if (addr >= (addr_t) &dummy->regs.acrs && | ||
| 310 | addr < (addr_t) &dummy->regs.orig_gpr2) | ||
| 311 | mask = 3; | ||
| 312 | #endif | ||
| 313 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | ||
| 314 | return -EIO; | ||
| 315 | |||
| 316 | return __poke_user(child, addr, data); | ||
| 317 | } | ||
| 318 | |||
| 295 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 319 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
| 296 | { | 320 | { |
| 297 | ptrace_area parea; | 321 | ptrace_area parea; |
| @@ -367,18 +391,13 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
| 367 | /* | 391 | /* |
| 368 | * Same as peek_user but for a 31 bit program. | 392 | * Same as peek_user but for a 31 bit program. |
| 369 | */ | 393 | */ |
| 370 | static int | 394 | static u32 __peek_user_compat(struct task_struct *child, addr_t addr) |
| 371 | peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | ||
| 372 | { | 395 | { |
| 373 | struct user32 *dummy32 = NULL; | 396 | struct user32 *dummy32 = NULL; |
| 374 | per_struct32 *dummy_per32 = NULL; | 397 | per_struct32 *dummy_per32 = NULL; |
| 375 | addr_t offset; | 398 | addr_t offset; |
| 376 | __u32 tmp; | 399 | __u32 tmp; |
| 377 | 400 | ||
| 378 | if (!test_thread_flag(TIF_31BIT) || | ||
| 379 | (addr & 3) || addr > sizeof(struct user) - 3) | ||
| 380 | return -EIO; | ||
| 381 | |||
| 382 | if (addr < (addr_t) &dummy32->regs.acrs) { | 401 | if (addr < (addr_t) &dummy32->regs.acrs) { |
| 383 | /* | 402 | /* |
| 384 | * psw and gprs are stored on the stack | 403 | * psw and gprs are stored on the stack |
| @@ -435,25 +454,32 @@ peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | |||
| 435 | } else | 454 | } else |
| 436 | tmp = 0; | 455 | tmp = 0; |
| 437 | 456 | ||
| 457 | return tmp; | ||
| 458 | } | ||
| 459 | |||
| 460 | static int peek_user_compat(struct task_struct *child, | ||
| 461 | addr_t addr, addr_t data) | ||
| 462 | { | ||
| 463 | __u32 tmp; | ||
| 464 | |||
| 465 | if (!test_thread_flag(TIF_31BIT) || | ||
| 466 | (addr & 3) || addr > sizeof(struct user) - 3) | ||
| 467 | return -EIO; | ||
| 468 | |||
| 469 | tmp = __peek_user_compat(child, addr); | ||
| 438 | return put_user(tmp, (__u32 __user *) data); | 470 | return put_user(tmp, (__u32 __user *) data); |
| 439 | } | 471 | } |
| 440 | 472 | ||
| 441 | /* | 473 | /* |
| 442 | * Same as poke_user but for a 31 bit program. | 474 | * Same as poke_user but for a 31 bit program. |
| 443 | */ | 475 | */ |
| 444 | static int | 476 | static int __poke_user_compat(struct task_struct *child, |
| 445 | poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | 477 | addr_t addr, addr_t data) |
| 446 | { | 478 | { |
| 447 | struct user32 *dummy32 = NULL; | 479 | struct user32 *dummy32 = NULL; |
| 448 | per_struct32 *dummy_per32 = NULL; | 480 | per_struct32 *dummy_per32 = NULL; |
| 481 | __u32 tmp = (__u32) data; | ||
| 449 | addr_t offset; | 482 | addr_t offset; |
| 450 | __u32 tmp; | ||
| 451 | |||
| 452 | if (!test_thread_flag(TIF_31BIT) || | ||
| 453 | (addr & 3) || addr > sizeof(struct user32) - 3) | ||
| 454 | return -EIO; | ||
| 455 | |||
| 456 | tmp = (__u32) data; | ||
| 457 | 483 | ||
| 458 | if (addr < (addr_t) &dummy32->regs.acrs) { | 484 | if (addr < (addr_t) &dummy32->regs.acrs) { |
| 459 | /* | 485 | /* |
| @@ -528,6 +554,16 @@ poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | |||
| 528 | return 0; | 554 | return 0; |
| 529 | } | 555 | } |
| 530 | 556 | ||
| 557 | static int poke_user_compat(struct task_struct *child, | ||
| 558 | addr_t addr, addr_t data) | ||
| 559 | { | ||
| 560 | if (!test_thread_flag(TIF_31BIT) || | ||
| 561 | (addr & 3) || addr > sizeof(struct user32) - 3) | ||
| 562 | return -EIO; | ||
| 563 | |||
| 564 | return __poke_user_compat(child, addr, data); | ||
| 565 | } | ||
| 566 | |||
| 531 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | 567 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, |
| 532 | compat_ulong_t caddr, compat_ulong_t cdata) | 568 | compat_ulong_t caddr, compat_ulong_t cdata) |
| 533 | { | 569 | { |
| @@ -539,11 +575,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 539 | switch (request) { | 575 | switch (request) { |
| 540 | case PTRACE_PEEKUSR: | 576 | case PTRACE_PEEKUSR: |
| 541 | /* read the word at location addr in the USER area. */ | 577 | /* read the word at location addr in the USER area. */ |
| 542 | return peek_user_emu31(child, addr, data); | 578 | return peek_user_compat(child, addr, data); |
| 543 | 579 | ||
| 544 | case PTRACE_POKEUSR: | 580 | case PTRACE_POKEUSR: |
| 545 | /* write the word at location addr in the USER area */ | 581 | /* write the word at location addr in the USER area */ |
| 546 | return poke_user_emu31(child, addr, data); | 582 | return poke_user_compat(child, addr, data); |
| 547 | 583 | ||
| 548 | case PTRACE_PEEKUSR_AREA: | 584 | case PTRACE_PEEKUSR_AREA: |
| 549 | case PTRACE_POKEUSR_AREA: | 585 | case PTRACE_POKEUSR_AREA: |
| @@ -555,13 +591,13 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 555 | copied = 0; | 591 | copied = 0; |
| 556 | while (copied < parea.len) { | 592 | while (copied < parea.len) { |
| 557 | if (request == PTRACE_PEEKUSR_AREA) | 593 | if (request == PTRACE_PEEKUSR_AREA) |
| 558 | ret = peek_user_emu31(child, addr, data); | 594 | ret = peek_user_compat(child, addr, data); |
| 559 | else { | 595 | else { |
| 560 | __u32 utmp; | 596 | __u32 utmp; |
| 561 | if (get_user(utmp, | 597 | if (get_user(utmp, |
| 562 | (__u32 __force __user *) data)) | 598 | (__u32 __force __user *) data)) |
| 563 | return -EFAULT; | 599 | return -EFAULT; |
| 564 | ret = poke_user_emu31(child, addr, utmp); | 600 | ret = poke_user_compat(child, addr, utmp); |
| 565 | } | 601 | } |
| 566 | if (ret) | 602 | if (ret) |
| 567 | return ret; | 603 | return ret; |
| @@ -610,3 +646,240 @@ syscall_trace(struct pt_regs *regs, int entryexit) | |||
| 610 | regs->gprs[2], regs->orig_gpr2, regs->gprs[3], | 646 | regs->gprs[2], regs->orig_gpr2, regs->gprs[3], |
| 611 | regs->gprs[4], regs->gprs[5]); | 647 | regs->gprs[4], regs->gprs[5]); |
| 612 | } | 648 | } |
| 649 | |||
| 650 | /* | ||
| 651 | * user_regset definitions. | ||
| 652 | */ | ||
| 653 | |||
| 654 | static int s390_regs_get(struct task_struct *target, | ||
| 655 | const struct user_regset *regset, | ||
| 656 | unsigned int pos, unsigned int count, | ||
| 657 | void *kbuf, void __user *ubuf) | ||
| 658 | { | ||
| 659 | if (target == current) | ||
| 660 | save_access_regs(target->thread.acrs); | ||
| 661 | |||
| 662 | if (kbuf) { | ||
| 663 | unsigned long *k = kbuf; | ||
| 664 | while (count > 0) { | ||
| 665 | *k++ = __peek_user(target, pos); | ||
| 666 | count -= sizeof(*k); | ||
| 667 | pos += sizeof(*k); | ||
| 668 | } | ||
| 669 | } else { | ||
| 670 | unsigned long __user *u = ubuf; | ||
| 671 | while (count > 0) { | ||
| 672 | if (__put_user(__peek_user(target, pos), u++)) | ||
| 673 | return -EFAULT; | ||
| 674 | count -= sizeof(*u); | ||
| 675 | pos += sizeof(*u); | ||
| 676 | } | ||
| 677 | } | ||
| 678 | return 0; | ||
| 679 | } | ||
| 680 | |||
| 681 | static int s390_regs_set(struct task_struct *target, | ||
| 682 | const struct user_regset *regset, | ||
| 683 | unsigned int pos, unsigned int count, | ||
| 684 | const void *kbuf, const void __user *ubuf) | ||
| 685 | { | ||
| 686 | int rc = 0; | ||
| 687 | |||
| 688 | if (target == current) | ||
| 689 | save_access_regs(target->thread.acrs); | ||
| 690 | |||
| 691 | if (kbuf) { | ||
| 692 | const unsigned long *k = kbuf; | ||
| 693 | while (count > 0 && !rc) { | ||
| 694 | rc = __poke_user(target, pos, *k++); | ||
| 695 | count -= sizeof(*k); | ||
| 696 | pos += sizeof(*k); | ||
| 697 | } | ||
| 698 | } else { | ||
| 699 | const unsigned long __user *u = ubuf; | ||
| 700 | while (count > 0 && !rc) { | ||
| 701 | unsigned long word; | ||
| 702 | rc = __get_user(word, u++); | ||
| 703 | if (rc) | ||
| 704 | break; | ||
| 705 | rc = __poke_user(target, pos, word); | ||
| 706 | count -= sizeof(*u); | ||
| 707 | pos += sizeof(*u); | ||
| 708 | } | ||
| 709 | } | ||
| 710 | |||
| 711 | if (rc == 0 && target == current) | ||
| 712 | restore_access_regs(target->thread.acrs); | ||
| 713 | |||
| 714 | return rc; | ||
| 715 | } | ||
| 716 | |||
| 717 | static int s390_fpregs_get(struct task_struct *target, | ||
| 718 | const struct user_regset *regset, unsigned int pos, | ||
| 719 | unsigned int count, void *kbuf, void __user *ubuf) | ||
| 720 | { | ||
| 721 | if (target == current) | ||
| 722 | save_fp_regs(&target->thread.fp_regs); | ||
| 723 | |||
| 724 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
| 725 | &target->thread.fp_regs, 0, -1); | ||
| 726 | } | ||
| 727 | |||
| 728 | static int s390_fpregs_set(struct task_struct *target, | ||
| 729 | const struct user_regset *regset, unsigned int pos, | ||
| 730 | unsigned int count, const void *kbuf, | ||
| 731 | const void __user *ubuf) | ||
| 732 | { | ||
| 733 | int rc = 0; | ||
| 734 | |||
| 735 | if (target == current) | ||
| 736 | save_fp_regs(&target->thread.fp_regs); | ||
| 737 | |||
| 738 | /* If setting FPC, must validate it first. */ | ||
| 739 | if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) { | ||
| 740 | u32 fpc[2] = { target->thread.fp_regs.fpc, 0 }; | ||
| 741 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc, | ||
| 742 | 0, offsetof(s390_fp_regs, fprs)); | ||
| 743 | if (rc) | ||
| 744 | return rc; | ||
| 745 | if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0) | ||
| 746 | return -EINVAL; | ||
| 747 | target->thread.fp_regs.fpc = fpc[0]; | ||
| 748 | } | ||
| 749 | |||
| 750 | if (rc == 0 && count > 0) | ||
| 751 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
| 752 | target->thread.fp_regs.fprs, | ||
| 753 | offsetof(s390_fp_regs, fprs), -1); | ||
| 754 | |||
| 755 | if (rc == 0 && target == current) | ||
| 756 | restore_fp_regs(&target->thread.fp_regs); | ||
| 757 | |||
| 758 | return rc; | ||
| 759 | } | ||
| 760 | |||
| 761 | static const struct user_regset s390_regsets[] = { | ||
| 762 | [REGSET_GENERAL] = { | ||
| 763 | .core_note_type = NT_PRSTATUS, | ||
| 764 | .n = sizeof(s390_regs) / sizeof(long), | ||
| 765 | .size = sizeof(long), | ||
| 766 | .align = sizeof(long), | ||
| 767 | .get = s390_regs_get, | ||
| 768 | .set = s390_regs_set, | ||
| 769 | }, | ||
| 770 | [REGSET_FP] = { | ||
| 771 | .core_note_type = NT_PRFPREG, | ||
| 772 | .n = sizeof(s390_fp_regs) / sizeof(long), | ||
| 773 | .size = sizeof(long), | ||
| 774 | .align = sizeof(long), | ||
| 775 | .get = s390_fpregs_get, | ||
| 776 | .set = s390_fpregs_set, | ||
| 777 | }, | ||
| 778 | }; | ||
| 779 | |||
| 780 | static const struct user_regset_view user_s390_view = { | ||
| 781 | .name = UTS_MACHINE, | ||
| 782 | .e_machine = EM_S390, | ||
| 783 | .regsets = s390_regsets, | ||
| 784 | .n = ARRAY_SIZE(s390_regsets) | ||
| 785 | }; | ||
| 786 | |||
| 787 | #ifdef CONFIG_COMPAT | ||
| 788 | static int s390_compat_regs_get(struct task_struct *target, | ||
| 789 | const struct user_regset *regset, | ||
| 790 | unsigned int pos, unsigned int count, | ||
| 791 | void *kbuf, void __user *ubuf) | ||
| 792 | { | ||
| 793 | if (target == current) | ||
| 794 | save_access_regs(target->thread.acrs); | ||
| 795 | |||
| 796 | if (kbuf) { | ||
| 797 | compat_ulong_t *k = kbuf; | ||
| 798 | while (count > 0) { | ||
| 799 | *k++ = __peek_user_compat(target, pos); | ||
| 800 | count -= sizeof(*k); | ||
| 801 | pos += sizeof(*k); | ||
| 802 | } | ||
| 803 | } else { | ||
| 804 | compat_ulong_t __user *u = ubuf; | ||
| 805 | while (count > 0) { | ||
| 806 | if (__put_user(__peek_user_compat(target, pos), u++)) | ||
| 807 | return -EFAULT; | ||
| 808 | count -= sizeof(*u); | ||
| 809 | pos += sizeof(*u); | ||
| 810 | } | ||
| 811 | } | ||
| 812 | return 0; | ||
| 813 | } | ||
| 814 | |||
| 815 | static int s390_compat_regs_set(struct task_struct *target, | ||
| 816 | const struct user_regset *regset, | ||
| 817 | unsigned int pos, unsigned int count, | ||
| 818 | const void *kbuf, const void __user *ubuf) | ||
| 819 | { | ||
| 820 | int rc = 0; | ||
| 821 | |||
| 822 | if (target == current) | ||
| 823 | save_access_regs(target->thread.acrs); | ||
| 824 | |||
| 825 | if (kbuf) { | ||
| 826 | const compat_ulong_t *k = kbuf; | ||
| 827 | while (count > 0 && !rc) { | ||
| 828 | rc = __poke_user_compat(target, pos, *k++); | ||
| 829 | count -= sizeof(*k); | ||
| 830 | pos += sizeof(*k); | ||
| 831 | } | ||
| 832 | } else { | ||
| 833 | const compat_ulong_t __user *u = ubuf; | ||
| 834 | while (count > 0 && !rc) { | ||
| 835 | compat_ulong_t word; | ||
| 836 | rc = __get_user(word, u++); | ||
| 837 | if (rc) | ||
| 838 | break; | ||
| 839 | rc = __poke_user_compat(target, pos, word); | ||
| 840 | count -= sizeof(*u); | ||
| 841 | pos += sizeof(*u); | ||
| 842 | } | ||
| 843 | } | ||
| 844 | |||
| 845 | if (rc == 0 && target == current) | ||
| 846 | restore_access_regs(target->thread.acrs); | ||
| 847 | |||
| 848 | return rc; | ||
| 849 | } | ||
| 850 | |||
| 851 | static const struct user_regset s390_compat_regsets[] = { | ||
| 852 | [REGSET_GENERAL] = { | ||
| 853 | .core_note_type = NT_PRSTATUS, | ||
| 854 | .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), | ||
| 855 | .size = sizeof(compat_long_t), | ||
| 856 | .align = sizeof(compat_long_t), | ||
| 857 | .get = s390_compat_regs_get, | ||
| 858 | .set = s390_compat_regs_set, | ||
| 859 | }, | ||
| 860 | [REGSET_FP] = { | ||
| 861 | .core_note_type = NT_PRFPREG, | ||
| 862 | .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), | ||
| 863 | .size = sizeof(compat_long_t), | ||
| 864 | .align = sizeof(compat_long_t), | ||
| 865 | .get = s390_fpregs_get, | ||
| 866 | .set = s390_fpregs_set, | ||
| 867 | }, | ||
| 868 | }; | ||
| 869 | |||
| 870 | static const struct user_regset_view user_s390_compat_view = { | ||
| 871 | .name = "s390", | ||
| 872 | .e_machine = EM_S390, | ||
| 873 | .regsets = s390_compat_regsets, | ||
| 874 | .n = ARRAY_SIZE(s390_compat_regsets) | ||
| 875 | }; | ||
| 876 | #endif | ||
| 877 | |||
| 878 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
| 879 | { | ||
| 880 | #ifdef CONFIG_COMPAT | ||
| 881 | if (test_tsk_thread_flag(task, TIF_31BIT)) | ||
| 882 | return &user_s390_compat_view; | ||
| 883 | #endif | ||
| 884 | return &user_s390_view; | ||
| 885 | } | ||
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 2bc70b6e876a..b358e18273b0 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
| @@ -77,7 +77,7 @@ unsigned long machine_flags; | |||
| 77 | unsigned long elf_hwcap = 0; | 77 | unsigned long elf_hwcap = 0; |
| 78 | char elf_platform[ELF_PLATFORM_SIZE]; | 78 | char elf_platform[ELF_PLATFORM_SIZE]; |
| 79 | 79 | ||
| 80 | struct mem_chunk __meminitdata memory_chunk[MEMORY_CHUNKS]; | 80 | struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS]; |
| 81 | volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ | 81 | volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ |
| 82 | static unsigned long __initdata memory_end; | 82 | static unsigned long __initdata memory_end; |
| 83 | 83 | ||
| @@ -205,12 +205,6 @@ static void __init conmode_default(void) | |||
| 205 | SET_CONSOLE_SCLP; | 205 | SET_CONSOLE_SCLP; |
| 206 | #endif | 206 | #endif |
| 207 | } | 207 | } |
| 208 | } else if (MACHINE_IS_P390) { | ||
| 209 | #if defined(CONFIG_TN3215_CONSOLE) | ||
| 210 | SET_CONSOLE_3215; | ||
| 211 | #elif defined(CONFIG_TN3270_CONSOLE) | ||
| 212 | SET_CONSOLE_3270; | ||
| 213 | #endif | ||
| 214 | } else { | 208 | } else { |
| 215 | #if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE) | 209 | #if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE) |
| 216 | SET_CONSOLE_SCLP; | 210 | SET_CONSOLE_SCLP; |
| @@ -221,18 +215,17 @@ static void __init conmode_default(void) | |||
| 221 | #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) | 215 | #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) |
| 222 | static void __init setup_zfcpdump(unsigned int console_devno) | 216 | static void __init setup_zfcpdump(unsigned int console_devno) |
| 223 | { | 217 | { |
| 224 | static char str[64]; | 218 | static char str[41]; |
| 225 | 219 | ||
| 226 | if (ipl_info.type != IPL_TYPE_FCP_DUMP) | 220 | if (ipl_info.type != IPL_TYPE_FCP_DUMP) |
| 227 | return; | 221 | return; |
| 228 | if (console_devno != -1) | 222 | if (console_devno != -1) |
| 229 | sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x", | 223 | sprintf(str, " cio_ignore=all,!0.0.%04x,!0.0.%04x", |
| 230 | ipl_info.data.fcp.dev_id.devno, console_devno); | 224 | ipl_info.data.fcp.dev_id.devno, console_devno); |
| 231 | else | 225 | else |
| 232 | sprintf(str, "cio_ignore=all,!0.0.%04x", | 226 | sprintf(str, " cio_ignore=all,!0.0.%04x", |
| 233 | ipl_info.data.fcp.dev_id.devno); | 227 | ipl_info.data.fcp.dev_id.devno); |
| 234 | strcat(COMMAND_LINE, " "); | 228 | strcat(boot_command_line, str); |
| 235 | strcat(COMMAND_LINE, str); | ||
| 236 | console_loglevel = 2; | 229 | console_loglevel = 2; |
| 237 | } | 230 | } |
| 238 | #else | 231 | #else |
| @@ -289,32 +282,6 @@ static int __init early_parse_mem(char *p) | |||
| 289 | } | 282 | } |
| 290 | early_param("mem", early_parse_mem); | 283 | early_param("mem", early_parse_mem); |
| 291 | 284 | ||
| 292 | /* | ||
| 293 | * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes | ||
| 294 | */ | ||
| 295 | static int __init early_parse_ipldelay(char *p) | ||
| 296 | { | ||
| 297 | unsigned long delay = 0; | ||
| 298 | |||
| 299 | delay = simple_strtoul(p, &p, 0); | ||
| 300 | |||
| 301 | switch (*p) { | ||
| 302 | case 's': | ||
| 303 | case 'S': | ||
| 304 | delay *= 1000000; | ||
| 305 | break; | ||
| 306 | case 'm': | ||
| 307 | case 'M': | ||
| 308 | delay *= 60 * 1000000; | ||
| 309 | } | ||
| 310 | |||
| 311 | /* now wait for the requested amount of time */ | ||
| 312 | udelay(delay); | ||
| 313 | |||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | early_param("ipldelay", early_parse_ipldelay); | ||
| 317 | |||
| 318 | #ifdef CONFIG_S390_SWITCH_AMODE | 285 | #ifdef CONFIG_S390_SWITCH_AMODE |
| 319 | #ifdef CONFIG_PGSTE | 286 | #ifdef CONFIG_PGSTE |
| 320 | unsigned int switch_amode = 1; | 287 | unsigned int switch_amode = 1; |
| @@ -804,11 +771,9 @@ setup_arch(char **cmdline_p) | |||
| 804 | printk("We are running native (64 bit mode)\n"); | 771 | printk("We are running native (64 bit mode)\n"); |
| 805 | #endif /* CONFIG_64BIT */ | 772 | #endif /* CONFIG_64BIT */ |
| 806 | 773 | ||
| 807 | /* Save unparsed command line copy for /proc/cmdline */ | 774 | /* Have one command line that is parsed and saved in /proc/cmdline */ |
| 808 | strlcpy(boot_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); | 775 | /* boot_command_line has been already set up in early.c */ |
| 809 | 776 | *cmdline_p = boot_command_line; | |
| 810 | *cmdline_p = COMMAND_LINE; | ||
| 811 | *(*cmdline_p + COMMAND_LINE_SIZE - 1) = '\0'; | ||
| 812 | 777 | ||
| 813 | ROOT_DEV = Root_RAM0; | 778 | ROOT_DEV = Root_RAM0; |
| 814 | 779 | ||
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 7aec676fefd5..7418bebb547f 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * Time of day based timer functions. | 3 | * Time of day based timer functions. |
| 4 | * | 4 | * |
| 5 | * S390 version | 5 | * S390 version |
| 6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | 6 | * Copyright IBM Corp. 1999, 2008 |
| 7 | * Author(s): Hartmut Penner (hp@de.ibm.com), | 7 | * Author(s): Hartmut Penner (hp@de.ibm.com), |
| 8 | * Martin Schwidefsky (schwidefsky@de.ibm.com), | 8 | * Martin Schwidefsky (schwidefsky@de.ibm.com), |
| 9 | * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) | 9 | * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) |
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/notifier.h> | 31 | #include <linux/notifier.h> |
| 32 | #include <linux/clocksource.h> | 32 | #include <linux/clocksource.h> |
| 33 | #include <linux/clockchips.h> | 33 | #include <linux/clockchips.h> |
| 34 | #include <linux/bootmem.h> | ||
| 34 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
| 35 | #include <asm/delay.h> | 36 | #include <asm/delay.h> |
| 36 | #include <asm/s390_ext.h> | 37 | #include <asm/s390_ext.h> |
| @@ -162,7 +163,7 @@ void init_cpu_timer(void) | |||
| 162 | /* Enable clock comparator timer interrupt. */ | 163 | /* Enable clock comparator timer interrupt. */ |
| 163 | __ctl_set_bit(0,11); | 164 | __ctl_set_bit(0,11); |
| 164 | 165 | ||
| 165 | /* Always allow ETR external interrupts, even without an ETR. */ | 166 | /* Always allow the timing alert external interrupt. */ |
| 166 | __ctl_set_bit(0, 4); | 167 | __ctl_set_bit(0, 4); |
| 167 | } | 168 | } |
| 168 | 169 | ||
| @@ -170,8 +171,21 @@ static void clock_comparator_interrupt(__u16 code) | |||
| 170 | { | 171 | { |
| 171 | } | 172 | } |
| 172 | 173 | ||
| 174 | static void etr_timing_alert(struct etr_irq_parm *); | ||
| 175 | static void stp_timing_alert(struct stp_irq_parm *); | ||
| 176 | |||
| 177 | static void timing_alert_interrupt(__u16 code) | ||
| 178 | { | ||
| 179 | if (S390_lowcore.ext_params & 0x00c40000) | ||
| 180 | etr_timing_alert((struct etr_irq_parm *) | ||
| 181 | &S390_lowcore.ext_params); | ||
| 182 | if (S390_lowcore.ext_params & 0x00038000) | ||
| 183 | stp_timing_alert((struct stp_irq_parm *) | ||
| 184 | &S390_lowcore.ext_params); | ||
| 185 | } | ||
| 186 | |||
| 173 | static void etr_reset(void); | 187 | static void etr_reset(void); |
| 174 | static void etr_ext_handler(__u16); | 188 | static void stp_reset(void); |
| 175 | 189 | ||
| 176 | /* | 190 | /* |
| 177 | * Get the TOD clock running. | 191 | * Get the TOD clock running. |
| @@ -181,6 +195,7 @@ static u64 __init reset_tod_clock(void) | |||
| 181 | u64 time; | 195 | u64 time; |
| 182 | 196 | ||
| 183 | etr_reset(); | 197 | etr_reset(); |
| 198 | stp_reset(); | ||
| 184 | if (store_clock(&time) == 0) | 199 | if (store_clock(&time) == 0) |
| 185 | return time; | 200 | return time; |
| 186 | /* TOD clock not running. Set the clock to Unix Epoch. */ | 201 | /* TOD clock not running. Set the clock to Unix Epoch. */ |
| @@ -231,8 +246,9 @@ void __init time_init(void) | |||
| 231 | if (clocksource_register(&clocksource_tod) != 0) | 246 | if (clocksource_register(&clocksource_tod) != 0) |
| 232 | panic("Could not register TOD clock source"); | 247 | panic("Could not register TOD clock source"); |
| 233 | 248 | ||
| 234 | /* request the etr external interrupt */ | 249 | /* request the timing alert external interrupt */ |
| 235 | if (register_early_external_interrupt(0x1406, etr_ext_handler, | 250 | if (register_early_external_interrupt(0x1406, |
| 251 | timing_alert_interrupt, | ||
| 236 | &ext_int_etr_cc) != 0) | 252 | &ext_int_etr_cc) != 0) |
| 237 | panic("Couldn't request external interrupt 0x1406"); | 253 | panic("Couldn't request external interrupt 0x1406"); |
| 238 | 254 | ||
| @@ -245,10 +261,112 @@ void __init time_init(void) | |||
| 245 | } | 261 | } |
| 246 | 262 | ||
| 247 | /* | 263 | /* |
| 264 | * The time is "clock". old is what we think the time is. | ||
| 265 | * Adjust the value by a multiple of jiffies and add the delta to ntp. | ||
| 266 | * "delay" is an approximation how long the synchronization took. If | ||
| 267 | * the time correction is positive, then "delay" is subtracted from | ||
| 268 | * the time difference and only the remaining part is passed to ntp. | ||
| 269 | */ | ||
| 270 | static unsigned long long adjust_time(unsigned long long old, | ||
| 271 | unsigned long long clock, | ||
| 272 | unsigned long long delay) | ||
| 273 | { | ||
| 274 | unsigned long long delta, ticks; | ||
| 275 | struct timex adjust; | ||
| 276 | |||
| 277 | if (clock > old) { | ||
| 278 | /* It is later than we thought. */ | ||
| 279 | delta = ticks = clock - old; | ||
| 280 | delta = ticks = (delta < delay) ? 0 : delta - delay; | ||
| 281 | delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); | ||
| 282 | adjust.offset = ticks * (1000000 / HZ); | ||
| 283 | } else { | ||
| 284 | /* It is earlier than we thought. */ | ||
| 285 | delta = ticks = old - clock; | ||
| 286 | delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); | ||
| 287 | delta = -delta; | ||
| 288 | adjust.offset = -ticks * (1000000 / HZ); | ||
| 289 | } | ||
| 290 | jiffies_timer_cc += delta; | ||
| 291 | if (adjust.offset != 0) { | ||
| 292 | printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n", | ||
| 293 | adjust.offset); | ||
| 294 | adjust.modes = ADJ_OFFSET_SINGLESHOT; | ||
| 295 | do_adjtimex(&adjust); | ||
| 296 | } | ||
| 297 | return delta; | ||
| 298 | } | ||
| 299 | |||
| 300 | static DEFINE_PER_CPU(atomic_t, clock_sync_word); | ||
| 301 | static unsigned long clock_sync_flags; | ||
| 302 | |||
| 303 | #define CLOCK_SYNC_HAS_ETR 0 | ||
| 304 | #define CLOCK_SYNC_HAS_STP 1 | ||
| 305 | #define CLOCK_SYNC_ETR 2 | ||
| 306 | #define CLOCK_SYNC_STP 3 | ||
| 307 | |||
| 308 | /* | ||
| 309 | * The synchronous get_clock function. It will write the current clock | ||
| 310 | * value to the clock pointer and return 0 if the clock is in sync with | ||
| 311 | * the external time source. If the clock mode is local it will return | ||
| 312 | * -ENOSYS and -EAGAIN if the clock is not in sync with the external | ||
| 313 | * reference. | ||
| 314 | */ | ||
| 315 | int get_sync_clock(unsigned long long *clock) | ||
| 316 | { | ||
| 317 | atomic_t *sw_ptr; | ||
| 318 | unsigned int sw0, sw1; | ||
| 319 | |||
| 320 | sw_ptr = &get_cpu_var(clock_sync_word); | ||
| 321 | sw0 = atomic_read(sw_ptr); | ||
| 322 | *clock = get_clock(); | ||
| 323 | sw1 = atomic_read(sw_ptr); | ||
| 324 | put_cpu_var(clock_sync_sync); | ||
| 325 | if (sw0 == sw1 && (sw0 & 0x80000000U)) | ||
| 326 | /* Success: time is in sync. */ | ||
| 327 | return 0; | ||
| 328 | if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) && | ||
| 329 | !test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) | ||
| 330 | return -ENOSYS; | ||
| 331 | if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) && | ||
| 332 | !test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) | ||
| 333 | return -EACCES; | ||
| 334 | return -EAGAIN; | ||
| 335 | } | ||
| 336 | EXPORT_SYMBOL(get_sync_clock); | ||
| 337 | |||
| 338 | /* | ||
| 339 | * Make get_sync_clock return -EAGAIN. | ||
| 340 | */ | ||
| 341 | static void disable_sync_clock(void *dummy) | ||
| 342 | { | ||
| 343 | atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word); | ||
| 344 | /* | ||
| 345 | * Clear the in-sync bit 2^31. All get_sync_clock calls will | ||
| 346 | * fail until the sync bit is turned back on. In addition | ||
| 347 | * increase the "sequence" counter to avoid the race of an | ||
| 348 | * etr event and the complete recovery against get_sync_clock. | ||
| 349 | */ | ||
| 350 | atomic_clear_mask(0x80000000, sw_ptr); | ||
| 351 | atomic_inc(sw_ptr); | ||
| 352 | } | ||
| 353 | |||
| 354 | /* | ||
| 355 | * Make get_sync_clock return 0 again. | ||
| 356 | * Needs to be called from a context disabled for preemption. | ||
| 357 | */ | ||
| 358 | static void enable_sync_clock(void) | ||
| 359 | { | ||
| 360 | atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word); | ||
| 361 | atomic_set_mask(0x80000000, sw_ptr); | ||
| 362 | } | ||
| 363 | |||
| 364 | /* | ||
| 248 | * External Time Reference (ETR) code. | 365 | * External Time Reference (ETR) code. |
| 249 | */ | 366 | */ |
| 250 | static int etr_port0_online; | 367 | static int etr_port0_online; |
| 251 | static int etr_port1_online; | 368 | static int etr_port1_online; |
| 369 | static int etr_steai_available; | ||
| 252 | 370 | ||
| 253 | static int __init early_parse_etr(char *p) | 371 | static int __init early_parse_etr(char *p) |
| 254 | { | 372 | { |
| @@ -273,12 +391,6 @@ enum etr_event { | |||
| 273 | ETR_EVENT_UPDATE, | 391 | ETR_EVENT_UPDATE, |
| 274 | }; | 392 | }; |
| 275 | 393 | ||
| 276 | enum etr_flags { | ||
| 277 | ETR_FLAG_ENOSYS, | ||
| 278 | ETR_FLAG_EACCES, | ||
| 279 | ETR_FLAG_STEAI, | ||
| 280 | }; | ||
| 281 | |||
| 282 | /* | 394 | /* |
| 283 | * Valid bit combinations of the eacr register are (x = don't care): | 395 | * Valid bit combinations of the eacr register are (x = don't care): |
| 284 | * e0 e1 dp p0 p1 ea es sl | 396 | * e0 e1 dp p0 p1 ea es sl |
| @@ -305,74 +417,18 @@ enum etr_flags { | |||
| 305 | */ | 417 | */ |
| 306 | static struct etr_eacr etr_eacr; | 418 | static struct etr_eacr etr_eacr; |
| 307 | static u64 etr_tolec; /* time of last eacr update */ | 419 | static u64 etr_tolec; /* time of last eacr update */ |
| 308 | static unsigned long etr_flags; | ||
| 309 | static struct etr_aib etr_port0; | 420 | static struct etr_aib etr_port0; |
| 310 | static int etr_port0_uptodate; | 421 | static int etr_port0_uptodate; |
| 311 | static struct etr_aib etr_port1; | 422 | static struct etr_aib etr_port1; |
| 312 | static int etr_port1_uptodate; | 423 | static int etr_port1_uptodate; |
| 313 | static unsigned long etr_events; | 424 | static unsigned long etr_events; |
| 314 | static struct timer_list etr_timer; | 425 | static struct timer_list etr_timer; |
| 315 | static DEFINE_PER_CPU(atomic_t, etr_sync_word); | ||
| 316 | 426 | ||
| 317 | static void etr_timeout(unsigned long dummy); | 427 | static void etr_timeout(unsigned long dummy); |
| 318 | static void etr_work_fn(struct work_struct *work); | 428 | static void etr_work_fn(struct work_struct *work); |
| 319 | static DECLARE_WORK(etr_work, etr_work_fn); | 429 | static DECLARE_WORK(etr_work, etr_work_fn); |
| 320 | 430 | ||
| 321 | /* | 431 | /* |
| 322 | * The etr get_clock function. It will write the current clock value | ||
| 323 | * to the clock pointer and return 0 if the clock is in sync with the | ||
| 324 | * external time source. If the clock mode is local it will return | ||
| 325 | * -ENOSYS and -EAGAIN if the clock is not in sync with the external | ||
| 326 | * reference. This function is what ETR is all about.. | ||
| 327 | */ | ||
| 328 | int get_sync_clock(unsigned long long *clock) | ||
| 329 | { | ||
| 330 | atomic_t *sw_ptr; | ||
| 331 | unsigned int sw0, sw1; | ||
| 332 | |||
| 333 | sw_ptr = &get_cpu_var(etr_sync_word); | ||
| 334 | sw0 = atomic_read(sw_ptr); | ||
| 335 | *clock = get_clock(); | ||
| 336 | sw1 = atomic_read(sw_ptr); | ||
| 337 | put_cpu_var(etr_sync_sync); | ||
| 338 | if (sw0 == sw1 && (sw0 & 0x80000000U)) | ||
| 339 | /* Success: time is in sync. */ | ||
| 340 | return 0; | ||
| 341 | if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) | ||
| 342 | return -ENOSYS; | ||
| 343 | if (test_bit(ETR_FLAG_EACCES, &etr_flags)) | ||
| 344 | return -EACCES; | ||
| 345 | return -EAGAIN; | ||
| 346 | } | ||
| 347 | EXPORT_SYMBOL(get_sync_clock); | ||
| 348 | |||
| 349 | /* | ||
| 350 | * Make get_sync_clock return -EAGAIN. | ||
| 351 | */ | ||
| 352 | static void etr_disable_sync_clock(void *dummy) | ||
| 353 | { | ||
| 354 | atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word); | ||
| 355 | /* | ||
| 356 | * Clear the in-sync bit 2^31. All get_sync_clock calls will | ||
| 357 | * fail until the sync bit is turned back on. In addition | ||
| 358 | * increase the "sequence" counter to avoid the race of an | ||
| 359 | * etr event and the complete recovery against get_sync_clock. | ||
| 360 | */ | ||
| 361 | atomic_clear_mask(0x80000000, sw_ptr); | ||
| 362 | atomic_inc(sw_ptr); | ||
| 363 | } | ||
| 364 | |||
| 365 | /* | ||
| 366 | * Make get_sync_clock return 0 again. | ||
| 367 | * Needs to be called from a context disabled for preemption. | ||
| 368 | */ | ||
| 369 | static void etr_enable_sync_clock(void) | ||
| 370 | { | ||
| 371 | atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word); | ||
| 372 | atomic_set_mask(0x80000000, sw_ptr); | ||
| 373 | } | ||
| 374 | |||
| 375 | /* | ||
| 376 | * Reset ETR attachment. | 432 | * Reset ETR attachment. |
| 377 | */ | 433 | */ |
| 378 | static void etr_reset(void) | 434 | static void etr_reset(void) |
| @@ -381,15 +437,13 @@ static void etr_reset(void) | |||
| 381 | .e0 = 0, .e1 = 0, ._pad0 = 4, .dp = 0, | 437 | .e0 = 0, .e1 = 0, ._pad0 = 4, .dp = 0, |
| 382 | .p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0, | 438 | .p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0, |
| 383 | .es = 0, .sl = 0 }; | 439 | .es = 0, .sl = 0 }; |
| 384 | if (etr_setr(&etr_eacr) == 0) | 440 | if (etr_setr(&etr_eacr) == 0) { |
| 385 | etr_tolec = get_clock(); | 441 | etr_tolec = get_clock(); |
| 386 | else { | 442 | set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags); |
| 387 | set_bit(ETR_FLAG_ENOSYS, &etr_flags); | 443 | } else if (etr_port0_online || etr_port1_online) { |
| 388 | if (etr_port0_online || etr_port1_online) { | 444 | printk(KERN_WARNING "Running on non ETR capable " |
| 389 | printk(KERN_WARNING "Running on non ETR capable " | 445 | "machine, only local mode available.\n"); |
| 390 | "machine, only local mode available.\n"); | 446 | etr_port0_online = etr_port1_online = 0; |
| 391 | etr_port0_online = etr_port1_online = 0; | ||
| 392 | } | ||
| 393 | } | 447 | } |
| 394 | } | 448 | } |
| 395 | 449 | ||
| @@ -397,14 +451,12 @@ static int __init etr_init(void) | |||
| 397 | { | 451 | { |
| 398 | struct etr_aib aib; | 452 | struct etr_aib aib; |
| 399 | 453 | ||
| 400 | if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) | 454 | if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags)) |
| 401 | return 0; | 455 | return 0; |
| 402 | /* Check if this machine has the steai instruction. */ | 456 | /* Check if this machine has the steai instruction. */ |
| 403 | if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0) | 457 | if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0) |
| 404 | set_bit(ETR_FLAG_STEAI, &etr_flags); | 458 | etr_steai_available = 1; |
| 405 | setup_timer(&etr_timer, etr_timeout, 0UL); | 459 | setup_timer(&etr_timer, etr_timeout, 0UL); |
| 406 | if (!etr_port0_online && !etr_port1_online) | ||
| 407 | set_bit(ETR_FLAG_EACCES, &etr_flags); | ||
| 408 | if (etr_port0_online) { | 460 | if (etr_port0_online) { |
| 409 | set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); | 461 | set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); |
| 410 | schedule_work(&etr_work); | 462 | schedule_work(&etr_work); |
| @@ -435,7 +487,8 @@ void etr_switch_to_local(void) | |||
| 435 | { | 487 | { |
| 436 | if (!etr_eacr.sl) | 488 | if (!etr_eacr.sl) |
| 437 | return; | 489 | return; |
| 438 | etr_disable_sync_clock(NULL); | 490 | if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) |
| 491 | disable_sync_clock(NULL); | ||
| 439 | set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); | 492 | set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); |
| 440 | schedule_work(&etr_work); | 493 | schedule_work(&etr_work); |
| 441 | } | 494 | } |
| @@ -450,23 +503,21 @@ void etr_sync_check(void) | |||
| 450 | { | 503 | { |
| 451 | if (!etr_eacr.es) | 504 | if (!etr_eacr.es) |
| 452 | return; | 505 | return; |
| 453 | etr_disable_sync_clock(NULL); | 506 | if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) |
| 507 | disable_sync_clock(NULL); | ||
| 454 | set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); | 508 | set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); |
| 455 | schedule_work(&etr_work); | 509 | schedule_work(&etr_work); |
| 456 | } | 510 | } |
| 457 | 511 | ||
| 458 | /* | 512 | /* |
| 459 | * ETR external interrupt. There are two causes: | 513 | * ETR timing alert. There are two causes: |
| 460 | * 1) port state change, check the usability of the port | 514 | * 1) port state change, check the usability of the port |
| 461 | * 2) port alert, one of the ETR-data-validity bits (v1-v2 bits of the | 515 | * 2) port alert, one of the ETR-data-validity bits (v1-v2 bits of the |
| 462 | * sldr-status word) or ETR-data word 1 (edf1) or ETR-data word 3 (edf3) | 516 | * sldr-status word) or ETR-data word 1 (edf1) or ETR-data word 3 (edf3) |
| 463 | * or ETR-data word 4 (edf4) has changed. | 517 | * or ETR-data word 4 (edf4) has changed. |
| 464 | */ | 518 | */ |
| 465 | static void etr_ext_handler(__u16 code) | 519 | static void etr_timing_alert(struct etr_irq_parm *intparm) |
| 466 | { | 520 | { |
| 467 | struct etr_interruption_parameter *intparm = | ||
| 468 | (struct etr_interruption_parameter *) &S390_lowcore.ext_params; | ||
| 469 | |||
| 470 | if (intparm->pc0) | 521 | if (intparm->pc0) |
| 471 | /* ETR port 0 state change. */ | 522 | /* ETR port 0 state change. */ |
| 472 | set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); | 523 | set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); |
| @@ -591,58 +642,23 @@ static int etr_aib_follows(struct etr_aib *a1, struct etr_aib *a2, int p) | |||
| 591 | return 1; | 642 | return 1; |
| 592 | } | 643 | } |
| 593 | 644 | ||
| 594 | /* | 645 | struct clock_sync_data { |
| 595 | * The time is "clock". old is what we think the time is. | ||
| 596 | * Adjust the value by a multiple of jiffies and add the delta to ntp. | ||
| 597 | * "delay" is an approximation how long the synchronization took. If | ||
| 598 | * the time correction is positive, then "delay" is subtracted from | ||
| 599 | * the time difference and only the remaining part is passed to ntp. | ||
| 600 | */ | ||
| 601 | static unsigned long long etr_adjust_time(unsigned long long old, | ||
| 602 | unsigned long long clock, | ||
| 603 | unsigned long long delay) | ||
| 604 | { | ||
| 605 | unsigned long long delta, ticks; | ||
| 606 | struct timex adjust; | ||
| 607 | |||
| 608 | if (clock > old) { | ||
| 609 | /* It is later than we thought. */ | ||
| 610 | delta = ticks = clock - old; | ||
| 611 | delta = ticks = (delta < delay) ? 0 : delta - delay; | ||
| 612 | delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); | ||
| 613 | adjust.offset = ticks * (1000000 / HZ); | ||
| 614 | } else { | ||
| 615 | /* It is earlier than we thought. */ | ||
| 616 | delta = ticks = old - clock; | ||
| 617 | delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); | ||
| 618 | delta = -delta; | ||
| 619 | adjust.offset = -ticks * (1000000 / HZ); | ||
| 620 | } | ||
| 621 | jiffies_timer_cc += delta; | ||
| 622 | if (adjust.offset != 0) { | ||
| 623 | printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n", | ||
| 624 | adjust.offset); | ||
| 625 | adjust.modes = ADJ_OFFSET_SINGLESHOT; | ||
| 626 | do_adjtimex(&adjust); | ||
| 627 | } | ||
| 628 | return delta; | ||
| 629 | } | ||
| 630 | |||
| 631 | static struct { | ||
| 632 | int in_sync; | 646 | int in_sync; |
| 633 | unsigned long long fixup_cc; | 647 | unsigned long long fixup_cc; |
| 634 | } etr_sync; | 648 | }; |
| 635 | 649 | ||
| 636 | static void etr_sync_cpu_start(void *dummy) | 650 | static void clock_sync_cpu_start(void *dummy) |
| 637 | { | 651 | { |
| 638 | etr_enable_sync_clock(); | 652 | struct clock_sync_data *sync = dummy; |
| 653 | |||
| 654 | enable_sync_clock(); | ||
| 639 | /* | 655 | /* |
| 640 | * This looks like a busy wait loop but it isn't. etr_sync_cpus | 656 | * This looks like a busy wait loop but it isn't. etr_sync_cpus |
| 641 | * is called on all other cpus while the TOD clocks is stopped. | 657 | * is called on all other cpus while the TOD clocks is stopped. |
| 642 | * __udelay will stop the cpu on an enabled wait psw until the | 658 | * __udelay will stop the cpu on an enabled wait psw until the |
| 643 | * TOD is running again. | 659 | * TOD is running again. |
| 644 | */ | 660 | */ |
| 645 | while (etr_sync.in_sync == 0) { | 661 | while (sync->in_sync == 0) { |
| 646 | __udelay(1); | 662 | __udelay(1); |
| 647 | /* | 663 | /* |
| 648 | * A different cpu changes *in_sync. Therefore use | 664 | * A different cpu changes *in_sync. Therefore use |
| @@ -650,17 +666,17 @@ static void etr_sync_cpu_start(void *dummy) | |||
| 650 | */ | 666 | */ |
| 651 | barrier(); | 667 | barrier(); |
| 652 | } | 668 | } |
| 653 | if (etr_sync.in_sync != 1) | 669 | if (sync->in_sync != 1) |
| 654 | /* Didn't work. Clear per-cpu in sync bit again. */ | 670 | /* Didn't work. Clear per-cpu in sync bit again. */ |
| 655 | etr_disable_sync_clock(NULL); | 671 | disable_sync_clock(NULL); |
| 656 | /* | 672 | /* |
| 657 | * This round of TOD syncing is done. Set the clock comparator | 673 | * This round of TOD syncing is done. Set the clock comparator |
| 658 | * to the next tick and let the processor continue. | 674 | * to the next tick and let the processor continue. |
| 659 | */ | 675 | */ |
| 660 | fixup_clock_comparator(etr_sync.fixup_cc); | 676 | fixup_clock_comparator(sync->fixup_cc); |
| 661 | } | 677 | } |
| 662 | 678 | ||
| 663 | static void etr_sync_cpu_end(void *dummy) | 679 | static void clock_sync_cpu_end(void *dummy) |
| 664 | { | 680 | { |
| 665 | } | 681 | } |
| 666 | 682 | ||
| @@ -672,6 +688,7 @@ static void etr_sync_cpu_end(void *dummy) | |||
| 672 | static int etr_sync_clock(struct etr_aib *aib, int port) | 688 | static int etr_sync_clock(struct etr_aib *aib, int port) |
| 673 | { | 689 | { |
| 674 | struct etr_aib *sync_port; | 690 | struct etr_aib *sync_port; |
| 691 | struct clock_sync_data etr_sync; | ||
| 675 | unsigned long long clock, old_clock, delay, delta; | 692 | unsigned long long clock, old_clock, delay, delta; |
| 676 | int follows; | 693 | int follows; |
| 677 | int rc; | 694 | int rc; |
| @@ -690,9 +707,9 @@ static int etr_sync_clock(struct etr_aib *aib, int port) | |||
| 690 | */ | 707 | */ |
| 691 | memset(&etr_sync, 0, sizeof(etr_sync)); | 708 | memset(&etr_sync, 0, sizeof(etr_sync)); |
| 692 | preempt_disable(); | 709 | preempt_disable(); |
| 693 | smp_call_function(etr_sync_cpu_start, NULL, 0, 0); | 710 | smp_call_function(clock_sync_cpu_start, &etr_sync, 0, 0); |
| 694 | local_irq_disable(); | 711 | local_irq_disable(); |
| 695 | etr_enable_sync_clock(); | 712 | enable_sync_clock(); |
| 696 | 713 | ||
| 697 | /* Set clock to next OTE. */ | 714 | /* Set clock to next OTE. */ |
| 698 | __ctl_set_bit(14, 21); | 715 | __ctl_set_bit(14, 21); |
| @@ -707,13 +724,13 @@ static int etr_sync_clock(struct etr_aib *aib, int port) | |||
| 707 | /* Adjust Linux timing variables. */ | 724 | /* Adjust Linux timing variables. */ |
| 708 | delay = (unsigned long long) | 725 | delay = (unsigned long long) |
| 709 | (aib->edf2.etv - sync_port->edf2.etv) << 32; | 726 | (aib->edf2.etv - sync_port->edf2.etv) << 32; |
| 710 | delta = etr_adjust_time(old_clock, clock, delay); | 727 | delta = adjust_time(old_clock, clock, delay); |
| 711 | etr_sync.fixup_cc = delta; | 728 | etr_sync.fixup_cc = delta; |
| 712 | fixup_clock_comparator(delta); | 729 | fixup_clock_comparator(delta); |
| 713 | /* Verify that the clock is properly set. */ | 730 | /* Verify that the clock is properly set. */ |
| 714 | if (!etr_aib_follows(sync_port, aib, port)) { | 731 | if (!etr_aib_follows(sync_port, aib, port)) { |
| 715 | /* Didn't work. */ | 732 | /* Didn't work. */ |
| 716 | etr_disable_sync_clock(NULL); | 733 | disable_sync_clock(NULL); |
| 717 | etr_sync.in_sync = -EAGAIN; | 734 | etr_sync.in_sync = -EAGAIN; |
| 718 | rc = -EAGAIN; | 735 | rc = -EAGAIN; |
| 719 | } else { | 736 | } else { |
| @@ -724,12 +741,12 @@ static int etr_sync_clock(struct etr_aib *aib, int port) | |||
| 724 | /* Could not set the clock ?!? */ | 741 | /* Could not set the clock ?!? */ |
| 725 | __ctl_clear_bit(0, 29); | 742 | __ctl_clear_bit(0, 29); |
| 726 | __ctl_clear_bit(14, 21); | 743 | __ctl_clear_bit(14, 21); |
| 727 | etr_disable_sync_clock(NULL); | 744 | disable_sync_clock(NULL); |
| 728 | etr_sync.in_sync = -EAGAIN; | 745 | etr_sync.in_sync = -EAGAIN; |
| 729 | rc = -EAGAIN; | 746 | rc = -EAGAIN; |
| 730 | } | 747 | } |
| 731 | local_irq_enable(); | 748 | local_irq_enable(); |
| 732 | smp_call_function(etr_sync_cpu_end,NULL,0,0); | 749 | smp_call_function(clock_sync_cpu_end, NULL, 0, 0); |
| 733 | preempt_enable(); | 750 | preempt_enable(); |
| 734 | return rc; | 751 | return rc; |
| 735 | } | 752 | } |
| @@ -832,7 +849,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib, | |||
| 832 | * Do not try to get the alternate port aib if the clock | 849 | * Do not try to get the alternate port aib if the clock |
| 833 | * is not in sync yet. | 850 | * is not in sync yet. |
| 834 | */ | 851 | */ |
| 835 | if (!eacr.es) | 852 | if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags) && !eacr.es) |
| 836 | return eacr; | 853 | return eacr; |
| 837 | 854 | ||
| 838 | /* | 855 | /* |
| @@ -840,7 +857,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib, | |||
| 840 | * the other port immediately. If only stetr is available the | 857 | * the other port immediately. If only stetr is available the |
| 841 | * data-port bit toggle has to be used. | 858 | * data-port bit toggle has to be used. |
| 842 | */ | 859 | */ |
| 843 | if (test_bit(ETR_FLAG_STEAI, &etr_flags)) { | 860 | if (etr_steai_available) { |
| 844 | if (eacr.p0 && !etr_port0_uptodate) { | 861 | if (eacr.p0 && !etr_port0_uptodate) { |
| 845 | etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0); | 862 | etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0); |
| 846 | etr_port0_uptodate = 1; | 863 | etr_port0_uptodate = 1; |
| @@ -909,10 +926,10 @@ static void etr_work_fn(struct work_struct *work) | |||
| 909 | if (!eacr.ea) { | 926 | if (!eacr.ea) { |
| 910 | /* Both ports offline. Reset everything. */ | 927 | /* Both ports offline. Reset everything. */ |
| 911 | eacr.dp = eacr.es = eacr.sl = 0; | 928 | eacr.dp = eacr.es = eacr.sl = 0; |
| 912 | on_each_cpu(etr_disable_sync_clock, NULL, 0, 1); | 929 | on_each_cpu(disable_sync_clock, NULL, 0, 1); |
| 913 | del_timer_sync(&etr_timer); | 930 | del_timer_sync(&etr_timer); |
| 914 | etr_update_eacr(eacr); | 931 | etr_update_eacr(eacr); |
| 915 | set_bit(ETR_FLAG_EACCES, &etr_flags); | 932 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); |
| 916 | return; | 933 | return; |
| 917 | } | 934 | } |
| 918 | 935 | ||
| @@ -953,7 +970,6 @@ static void etr_work_fn(struct work_struct *work) | |||
| 953 | eacr.e1 = 1; | 970 | eacr.e1 = 1; |
| 954 | sync_port = (etr_port0_uptodate && | 971 | sync_port = (etr_port0_uptodate && |
| 955 | etr_port_valid(&etr_port0, 0)) ? 0 : -1; | 972 | etr_port_valid(&etr_port0, 0)) ? 0 : -1; |
| 956 | clear_bit(ETR_FLAG_EACCES, &etr_flags); | ||
| 957 | } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) { | 973 | } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) { |
| 958 | eacr.sl = 0; | 974 | eacr.sl = 0; |
| 959 | eacr.e0 = 0; | 975 | eacr.e0 = 0; |
| @@ -962,7 +978,6 @@ static void etr_work_fn(struct work_struct *work) | |||
| 962 | eacr.es = 0; | 978 | eacr.es = 0; |
| 963 | sync_port = (etr_port1_uptodate && | 979 | sync_port = (etr_port1_uptodate && |
| 964 | etr_port_valid(&etr_port1, 1)) ? 1 : -1; | 980 | etr_port_valid(&etr_port1, 1)) ? 1 : -1; |
| 965 | clear_bit(ETR_FLAG_EACCES, &etr_flags); | ||
| 966 | } else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) { | 981 | } else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) { |
| 967 | eacr.sl = 1; | 982 | eacr.sl = 1; |
| 968 | eacr.e0 = 1; | 983 | eacr.e0 = 1; |
| @@ -976,7 +991,6 @@ static void etr_work_fn(struct work_struct *work) | |||
| 976 | eacr.e1 = 1; | 991 | eacr.e1 = 1; |
| 977 | sync_port = (etr_port0_uptodate && | 992 | sync_port = (etr_port0_uptodate && |
| 978 | etr_port_valid(&etr_port0, 0)) ? 0 : -1; | 993 | etr_port_valid(&etr_port0, 0)) ? 0 : -1; |
| 979 | clear_bit(ETR_FLAG_EACCES, &etr_flags); | ||
| 980 | } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) { | 994 | } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) { |
| 981 | eacr.sl = 1; | 995 | eacr.sl = 1; |
| 982 | eacr.e0 = 0; | 996 | eacr.e0 = 0; |
| @@ -985,19 +999,22 @@ static void etr_work_fn(struct work_struct *work) | |||
| 985 | eacr.es = 0; | 999 | eacr.es = 0; |
| 986 | sync_port = (etr_port1_uptodate && | 1000 | sync_port = (etr_port1_uptodate && |
| 987 | etr_port_valid(&etr_port1, 1)) ? 1 : -1; | 1001 | etr_port_valid(&etr_port1, 1)) ? 1 : -1; |
| 988 | clear_bit(ETR_FLAG_EACCES, &etr_flags); | ||
| 989 | } else { | 1002 | } else { |
| 990 | /* Both ports not usable. */ | 1003 | /* Both ports not usable. */ |
| 991 | eacr.es = eacr.sl = 0; | 1004 | eacr.es = eacr.sl = 0; |
| 992 | sync_port = -1; | 1005 | sync_port = -1; |
| 993 | set_bit(ETR_FLAG_EACCES, &etr_flags); | 1006 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); |
| 994 | } | 1007 | } |
| 995 | 1008 | ||
| 1009 | if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) | ||
| 1010 | eacr.es = 0; | ||
| 1011 | |||
| 996 | /* | 1012 | /* |
| 997 | * If the clock is in sync just update the eacr and return. | 1013 | * If the clock is in sync just update the eacr and return. |
| 998 | * If there is no valid sync port wait for a port update. | 1014 | * If there is no valid sync port wait for a port update. |
| 999 | */ | 1015 | */ |
| 1000 | if (eacr.es || sync_port < 0) { | 1016 | if (test_bit(CLOCK_SYNC_STP, &clock_sync_flags) || |
| 1017 | eacr.es || sync_port < 0) { | ||
| 1001 | etr_update_eacr(eacr); | 1018 | etr_update_eacr(eacr); |
| 1002 | etr_set_tolec_timeout(now); | 1019 | etr_set_tolec_timeout(now); |
| 1003 | return; | 1020 | return; |
| @@ -1018,11 +1035,13 @@ static void etr_work_fn(struct work_struct *work) | |||
| 1018 | * and set up a timer to try again after 0.5 seconds | 1035 | * and set up a timer to try again after 0.5 seconds |
| 1019 | */ | 1036 | */ |
| 1020 | etr_update_eacr(eacr); | 1037 | etr_update_eacr(eacr); |
| 1038 | set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
| 1021 | if (now < etr_tolec + (1600000 << 12) || | 1039 | if (now < etr_tolec + (1600000 << 12) || |
| 1022 | etr_sync_clock(&aib, sync_port) != 0) { | 1040 | etr_sync_clock(&aib, sync_port) != 0) { |
| 1023 | /* Sync failed. Try again in 1/2 second. */ | 1041 | /* Sync failed. Try again in 1/2 second. */ |
| 1024 | eacr.es = 0; | 1042 | eacr.es = 0; |
| 1025 | etr_update_eacr(eacr); | 1043 | etr_update_eacr(eacr); |
| 1044 | clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); | ||
| 1026 | etr_set_sync_timeout(); | 1045 | etr_set_sync_timeout(); |
| 1027 | } else | 1046 | } else |
| 1028 | etr_set_tolec_timeout(now); | 1047 | etr_set_tolec_timeout(now); |
| @@ -1097,8 +1116,8 @@ static ssize_t etr_online_store(struct sys_device *dev, | |||
| 1097 | value = simple_strtoul(buf, NULL, 0); | 1116 | value = simple_strtoul(buf, NULL, 0); |
| 1098 | if (value != 0 && value != 1) | 1117 | if (value != 0 && value != 1) |
| 1099 | return -EINVAL; | 1118 | return -EINVAL; |
| 1100 | if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) | 1119 | if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags)) |
| 1101 | return -ENOSYS; | 1120 | return -EOPNOTSUPP; |
| 1102 | if (dev == &etr_port0_dev) { | 1121 | if (dev == &etr_port0_dev) { |
| 1103 | if (etr_port0_online == value) | 1122 | if (etr_port0_online == value) |
| 1104 | return count; /* Nothing to do. */ | 1123 | return count; /* Nothing to do. */ |
| @@ -1292,3 +1311,318 @@ out: | |||
| 1292 | } | 1311 | } |
| 1293 | 1312 | ||
| 1294 | device_initcall(etr_init_sysfs); | 1313 | device_initcall(etr_init_sysfs); |
| 1314 | |||
| 1315 | /* | ||
| 1316 | * Server Time Protocol (STP) code. | ||
| 1317 | */ | ||
| 1318 | static int stp_online; | ||
| 1319 | static struct stp_sstpi stp_info; | ||
| 1320 | static void *stp_page; | ||
| 1321 | |||
| 1322 | static void stp_work_fn(struct work_struct *work); | ||
| 1323 | static DECLARE_WORK(stp_work, stp_work_fn); | ||
| 1324 | |||
| 1325 | static int __init early_parse_stp(char *p) | ||
| 1326 | { | ||
| 1327 | if (strncmp(p, "off", 3) == 0) | ||
| 1328 | stp_online = 0; | ||
| 1329 | else if (strncmp(p, "on", 2) == 0) | ||
| 1330 | stp_online = 1; | ||
| 1331 | return 0; | ||
| 1332 | } | ||
| 1333 | early_param("stp", early_parse_stp); | ||
| 1334 | |||
| 1335 | /* | ||
| 1336 | * Reset STP attachment. | ||
| 1337 | */ | ||
| 1338 | static void stp_reset(void) | ||
| 1339 | { | ||
| 1340 | int rc; | ||
| 1341 | |||
| 1342 | stp_page = alloc_bootmem_pages(PAGE_SIZE); | ||
| 1343 | rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000); | ||
| 1344 | if (rc == 1) | ||
| 1345 | set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags); | ||
| 1346 | else if (stp_online) { | ||
| 1347 | printk(KERN_WARNING "Running on non STP capable machine.\n"); | ||
| 1348 | free_bootmem((unsigned long) stp_page, PAGE_SIZE); | ||
| 1349 | stp_page = NULL; | ||
| 1350 | stp_online = 0; | ||
| 1351 | } | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | static int __init stp_init(void) | ||
| 1355 | { | ||
| 1356 | if (test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags) && stp_online) | ||
| 1357 | schedule_work(&stp_work); | ||
| 1358 | return 0; | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | arch_initcall(stp_init); | ||
| 1362 | |||
| 1363 | /* | ||
| 1364 | * STP timing alert. There are three causes: | ||
| 1365 | * 1) timing status change | ||
| 1366 | * 2) link availability change | ||
| 1367 | * 3) time control parameter change | ||
| 1368 | * In all three cases we are only interested in the clock source state. | ||
| 1369 | * If a STP clock source is now available use it. | ||
| 1370 | */ | ||
| 1371 | static void stp_timing_alert(struct stp_irq_parm *intparm) | ||
| 1372 | { | ||
| 1373 | if (intparm->tsc || intparm->lac || intparm->tcpc) | ||
| 1374 | schedule_work(&stp_work); | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | /* | ||
| 1378 | * STP sync check machine check. This is called when the timing state | ||
| 1379 | * changes from the synchronized state to the unsynchronized state. | ||
| 1380 | * After a STP sync check the clock is not in sync. The machine check | ||
| 1381 | * is broadcasted to all cpus at the same time. | ||
| 1382 | */ | ||
| 1383 | void stp_sync_check(void) | ||
| 1384 | { | ||
| 1385 | if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) | ||
| 1386 | return; | ||
| 1387 | disable_sync_clock(NULL); | ||
| 1388 | schedule_work(&stp_work); | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | /* | ||
| 1392 | * STP island condition machine check. This is called when an attached | ||
| 1393 | * server attempts to communicate over an STP link and the servers | ||
| 1394 | * have matching CTN ids and have a valid stratum-1 configuration | ||
| 1395 | * but the configurations do not match. | ||
| 1396 | */ | ||
| 1397 | void stp_island_check(void) | ||
| 1398 | { | ||
| 1399 | if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) | ||
| 1400 | return; | ||
| 1401 | disable_sync_clock(NULL); | ||
| 1402 | schedule_work(&stp_work); | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | /* | ||
| 1406 | * STP tasklet. Check for the STP state and take over the clock | ||
| 1407 | * synchronization if the STP clock source is usable. | ||
| 1408 | */ | ||
| 1409 | static void stp_work_fn(struct work_struct *work) | ||
| 1410 | { | ||
| 1411 | struct clock_sync_data stp_sync; | ||
| 1412 | unsigned long long old_clock, delta; | ||
| 1413 | int rc; | ||
| 1414 | |||
| 1415 | if (!stp_online) { | ||
| 1416 | chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000); | ||
| 1417 | return; | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0); | ||
| 1421 | if (rc) | ||
| 1422 | return; | ||
| 1423 | |||
| 1424 | rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi)); | ||
| 1425 | if (rc || stp_info.c == 0) | ||
| 1426 | return; | ||
| 1427 | |||
| 1428 | /* | ||
| 1429 | * Catch all other cpus and make them wait until we have | ||
| 1430 | * successfully synced the clock. smp_call_function will | ||
| 1431 | * return after all other cpus are in clock_sync_cpu_start. | ||
| 1432 | */ | ||
| 1433 | memset(&stp_sync, 0, sizeof(stp_sync)); | ||
| 1434 | preempt_disable(); | ||
| 1435 | smp_call_function(clock_sync_cpu_start, &stp_sync, 0, 0); | ||
| 1436 | local_irq_disable(); | ||
| 1437 | enable_sync_clock(); | ||
| 1438 | |||
| 1439 | set_bit(CLOCK_SYNC_STP, &clock_sync_flags); | ||
| 1440 | if (test_and_clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) | ||
| 1441 | schedule_work(&etr_work); | ||
| 1442 | |||
| 1443 | rc = 0; | ||
| 1444 | if (stp_info.todoff[0] || stp_info.todoff[1] || | ||
| 1445 | stp_info.todoff[2] || stp_info.todoff[3] || | ||
| 1446 | stp_info.tmd != 2) { | ||
| 1447 | old_clock = get_clock(); | ||
| 1448 | rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0); | ||
| 1449 | if (rc == 0) { | ||
| 1450 | delta = adjust_time(old_clock, get_clock(), 0); | ||
| 1451 | fixup_clock_comparator(delta); | ||
| 1452 | rc = chsc_sstpi(stp_page, &stp_info, | ||
| 1453 | sizeof(struct stp_sstpi)); | ||
| 1454 | if (rc == 0 && stp_info.tmd != 2) | ||
| 1455 | rc = -EAGAIN; | ||
| 1456 | } | ||
| 1457 | } | ||
| 1458 | if (rc) { | ||
| 1459 | disable_sync_clock(NULL); | ||
| 1460 | stp_sync.in_sync = -EAGAIN; | ||
| 1461 | clear_bit(CLOCK_SYNC_STP, &clock_sync_flags); | ||
| 1462 | if (etr_port0_online || etr_port1_online) | ||
| 1463 | schedule_work(&etr_work); | ||
| 1464 | } else | ||
| 1465 | stp_sync.in_sync = 1; | ||
| 1466 | |||
| 1467 | local_irq_enable(); | ||
| 1468 | smp_call_function(clock_sync_cpu_end, NULL, 0, 0); | ||
| 1469 | preempt_enable(); | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | /* | ||
| 1473 | * STP class sysfs interface functions | ||
| 1474 | */ | ||
| 1475 | static struct sysdev_class stp_sysclass = { | ||
| 1476 | .name = "stp", | ||
| 1477 | }; | ||
| 1478 | |||
| 1479 | static ssize_t stp_ctn_id_show(struct sysdev_class *class, char *buf) | ||
| 1480 | { | ||
| 1481 | if (!stp_online) | ||
| 1482 | return -ENODATA; | ||
| 1483 | return sprintf(buf, "%016llx\n", | ||
| 1484 | *(unsigned long long *) stp_info.ctnid); | ||
| 1485 | } | ||
| 1486 | |||
| 1487 | static SYSDEV_CLASS_ATTR(ctn_id, 0400, stp_ctn_id_show, NULL); | ||
| 1488 | |||
| 1489 | static ssize_t stp_ctn_type_show(struct sysdev_class *class, char *buf) | ||
| 1490 | { | ||
| 1491 | if (!stp_online) | ||
| 1492 | return -ENODATA; | ||
| 1493 | return sprintf(buf, "%i\n", stp_info.ctn); | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | static SYSDEV_CLASS_ATTR(ctn_type, 0400, stp_ctn_type_show, NULL); | ||
| 1497 | |||
| 1498 | static ssize_t stp_dst_offset_show(struct sysdev_class *class, char *buf) | ||
| 1499 | { | ||
| 1500 | if (!stp_online || !(stp_info.vbits & 0x2000)) | ||
| 1501 | return -ENODATA; | ||
| 1502 | return sprintf(buf, "%i\n", (int)(s16) stp_info.dsto); | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | static SYSDEV_CLASS_ATTR(dst_offset, 0400, stp_dst_offset_show, NULL); | ||
| 1506 | |||
| 1507 | static ssize_t stp_leap_seconds_show(struct sysdev_class *class, char *buf) | ||
| 1508 | { | ||
| 1509 | if (!stp_online || !(stp_info.vbits & 0x8000)) | ||
| 1510 | return -ENODATA; | ||
| 1511 | return sprintf(buf, "%i\n", (int)(s16) stp_info.leaps); | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | static SYSDEV_CLASS_ATTR(leap_seconds, 0400, stp_leap_seconds_show, NULL); | ||
| 1515 | |||
| 1516 | static ssize_t stp_stratum_show(struct sysdev_class *class, char *buf) | ||
| 1517 | { | ||
| 1518 | if (!stp_online) | ||
| 1519 | return -ENODATA; | ||
| 1520 | return sprintf(buf, "%i\n", (int)(s16) stp_info.stratum); | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | static SYSDEV_CLASS_ATTR(stratum, 0400, stp_stratum_show, NULL); | ||
| 1524 | |||
| 1525 | static ssize_t stp_time_offset_show(struct sysdev_class *class, char *buf) | ||
| 1526 | { | ||
| 1527 | if (!stp_online || !(stp_info.vbits & 0x0800)) | ||
| 1528 | return -ENODATA; | ||
| 1529 | return sprintf(buf, "%i\n", (int) stp_info.tto); | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | static SYSDEV_CLASS_ATTR(time_offset, 0400, stp_time_offset_show, NULL); | ||
| 1533 | |||
| 1534 | static ssize_t stp_time_zone_offset_show(struct sysdev_class *class, char *buf) | ||
| 1535 | { | ||
| 1536 | if (!stp_online || !(stp_info.vbits & 0x4000)) | ||
| 1537 | return -ENODATA; | ||
| 1538 | return sprintf(buf, "%i\n", (int)(s16) stp_info.tzo); | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | static SYSDEV_CLASS_ATTR(time_zone_offset, 0400, | ||
| 1542 | stp_time_zone_offset_show, NULL); | ||
| 1543 | |||
| 1544 | static ssize_t stp_timing_mode_show(struct sysdev_class *class, char *buf) | ||
| 1545 | { | ||
| 1546 | if (!stp_online) | ||
| 1547 | return -ENODATA; | ||
| 1548 | return sprintf(buf, "%i\n", stp_info.tmd); | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | static SYSDEV_CLASS_ATTR(timing_mode, 0400, stp_timing_mode_show, NULL); | ||
| 1552 | |||
| 1553 | static ssize_t stp_timing_state_show(struct sysdev_class *class, char *buf) | ||
| 1554 | { | ||
| 1555 | if (!stp_online) | ||
| 1556 | return -ENODATA; | ||
| 1557 | return sprintf(buf, "%i\n", stp_info.tst); | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | static SYSDEV_CLASS_ATTR(timing_state, 0400, stp_timing_state_show, NULL); | ||
| 1561 | |||
| 1562 | static ssize_t stp_online_show(struct sysdev_class *class, char *buf) | ||
| 1563 | { | ||
| 1564 | return sprintf(buf, "%i\n", stp_online); | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | static ssize_t stp_online_store(struct sysdev_class *class, | ||
| 1568 | const char *buf, size_t count) | ||
| 1569 | { | ||
| 1570 | unsigned int value; | ||
| 1571 | |||
| 1572 | value = simple_strtoul(buf, NULL, 0); | ||
| 1573 | if (value != 0 && value != 1) | ||
| 1574 | return -EINVAL; | ||
| 1575 | if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) | ||
| 1576 | return -EOPNOTSUPP; | ||
| 1577 | stp_online = value; | ||
| 1578 | schedule_work(&stp_work); | ||
| 1579 | return count; | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | /* | ||
| 1583 | * Can't use SYSDEV_CLASS_ATTR because the attribute should be named | ||
| 1584 | * stp/online but attr_online already exists in this file .. | ||
| 1585 | */ | ||
| 1586 | static struct sysdev_class_attribute attr_stp_online = { | ||
| 1587 | .attr = { .name = "online", .mode = 0600 }, | ||
| 1588 | .show = stp_online_show, | ||
| 1589 | .store = stp_online_store, | ||
| 1590 | }; | ||
| 1591 | |||
| 1592 | static struct sysdev_class_attribute *stp_attributes[] = { | ||
| 1593 | &attr_ctn_id, | ||
| 1594 | &attr_ctn_type, | ||
| 1595 | &attr_dst_offset, | ||
| 1596 | &attr_leap_seconds, | ||
| 1597 | &attr_stp_online, | ||
| 1598 | &attr_stratum, | ||
| 1599 | &attr_time_offset, | ||
| 1600 | &attr_time_zone_offset, | ||
| 1601 | &attr_timing_mode, | ||
| 1602 | &attr_timing_state, | ||
| 1603 | NULL | ||
| 1604 | }; | ||
| 1605 | |||
| 1606 | static int __init stp_init_sysfs(void) | ||
| 1607 | { | ||
| 1608 | struct sysdev_class_attribute **attr; | ||
| 1609 | int rc; | ||
| 1610 | |||
| 1611 | rc = sysdev_class_register(&stp_sysclass); | ||
| 1612 | if (rc) | ||
| 1613 | goto out; | ||
| 1614 | for (attr = stp_attributes; *attr; attr++) { | ||
| 1615 | rc = sysdev_class_create_file(&stp_sysclass, *attr); | ||
| 1616 | if (rc) | ||
| 1617 | goto out_unreg; | ||
| 1618 | } | ||
| 1619 | return 0; | ||
| 1620 | out_unreg: | ||
| 1621 | for (; attr >= stp_attributes; attr--) | ||
| 1622 | sysdev_class_remove_file(&stp_sysclass, *attr); | ||
| 1623 | sysdev_class_unregister(&stp_sysclass); | ||
| 1624 | out: | ||
| 1625 | return rc; | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | device_initcall(stp_init_sysfs); | ||
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 661a07217057..212d618b0095 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c | |||
| @@ -313,8 +313,6 @@ void __init s390_init_cpu_topology(void) | |||
| 313 | machine_has_topology_irq = 1; | 313 | machine_has_topology_irq = 1; |
| 314 | 314 | ||
| 315 | tl_info = alloc_bootmem_pages(PAGE_SIZE); | 315 | tl_info = alloc_bootmem_pages(PAGE_SIZE); |
| 316 | if (!tl_info) | ||
| 317 | goto error; | ||
| 318 | info = tl_info; | 316 | info = tl_info; |
| 319 | stsi(info, 15, 1, 2); | 317 | stsi(info, 15, 1, 2); |
| 320 | 318 | ||
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index b4607155e8d0..76c1e60c92f3 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S | |||
| @@ -40,7 +40,6 @@ SECTIONS | |||
| 40 | _etext = .; /* End of text section */ | 40 | _etext = .; /* End of text section */ |
| 41 | 41 | ||
| 42 | NOTES :text :note | 42 | NOTES :text :note |
| 43 | BUG_TABLE :text | ||
| 44 | 43 | ||
| 45 | RODATA | 44 | RODATA |
| 46 | 45 | ||
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index ca90ee3f930e..0fa5dc5d68e1 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c | |||
| @@ -136,7 +136,7 @@ static inline void set_vtimer(__u64 expires) | |||
| 136 | } | 136 | } |
| 137 | #endif | 137 | #endif |
| 138 | 138 | ||
| 139 | static void start_cpu_timer(void) | 139 | void vtime_start_cpu_timer(void) |
| 140 | { | 140 | { |
| 141 | struct vtimer_queue *vt_list; | 141 | struct vtimer_queue *vt_list; |
| 142 | 142 | ||
| @@ -150,7 +150,7 @@ static void start_cpu_timer(void) | |||
| 150 | set_vtimer(vt_list->idle); | 150 | set_vtimer(vt_list->idle); |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | static void stop_cpu_timer(void) | 153 | void vtime_stop_cpu_timer(void) |
| 154 | { | 154 | { |
| 155 | struct vtimer_queue *vt_list; | 155 | struct vtimer_queue *vt_list; |
| 156 | 156 | ||
| @@ -318,8 +318,7 @@ static void internal_add_vtimer(struct vtimer_list *timer) | |||
| 318 | vt_list = &per_cpu(virt_cpu_timer, timer->cpu); | 318 | vt_list = &per_cpu(virt_cpu_timer, timer->cpu); |
| 319 | spin_lock_irqsave(&vt_list->lock, flags); | 319 | spin_lock_irqsave(&vt_list->lock, flags); |
| 320 | 320 | ||
| 321 | if (timer->cpu != smp_processor_id()) | 321 | BUG_ON(timer->cpu != smp_processor_id()); |
| 322 | printk("internal_add_vtimer: BUG, running on wrong CPU"); | ||
| 323 | 322 | ||
| 324 | /* if list is empty we only have to set the timer */ | 323 | /* if list is empty we only have to set the timer */ |
| 325 | if (list_empty(&vt_list->list)) { | 324 | if (list_empty(&vt_list->list)) { |
| @@ -353,25 +352,12 @@ static void internal_add_vtimer(struct vtimer_list *timer) | |||
| 353 | put_cpu(); | 352 | put_cpu(); |
| 354 | } | 353 | } |
| 355 | 354 | ||
| 356 | static inline int prepare_vtimer(struct vtimer_list *timer) | 355 | static inline void prepare_vtimer(struct vtimer_list *timer) |
| 357 | { | 356 | { |
| 358 | if (!timer->function) { | 357 | BUG_ON(!timer->function); |
| 359 | printk("add_virt_timer: uninitialized timer\n"); | 358 | BUG_ON(!timer->expires || timer->expires > VTIMER_MAX_SLICE); |
| 360 | return -EINVAL; | 359 | BUG_ON(vtimer_pending(timer)); |
| 361 | } | ||
| 362 | |||
| 363 | if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) { | ||
| 364 | printk("add_virt_timer: invalid timer expire value!\n"); | ||
| 365 | return -EINVAL; | ||
| 366 | } | ||
| 367 | |||
| 368 | if (vtimer_pending(timer)) { | ||
| 369 | printk("add_virt_timer: timer pending\n"); | ||
| 370 | return -EBUSY; | ||
| 371 | } | ||
| 372 | |||
| 373 | timer->cpu = get_cpu(); | 360 | timer->cpu = get_cpu(); |
| 374 | return 0; | ||
| 375 | } | 361 | } |
| 376 | 362 | ||
| 377 | /* | 363 | /* |
| @@ -382,10 +368,7 @@ void add_virt_timer(void *new) | |||
| 382 | struct vtimer_list *timer; | 368 | struct vtimer_list *timer; |
| 383 | 369 | ||
| 384 | timer = (struct vtimer_list *)new; | 370 | timer = (struct vtimer_list *)new; |
| 385 | 371 | prepare_vtimer(timer); | |
| 386 | if (prepare_vtimer(timer) < 0) | ||
| 387 | return; | ||
| 388 | |||
| 389 | timer->interval = 0; | 372 | timer->interval = 0; |
| 390 | internal_add_vtimer(timer); | 373 | internal_add_vtimer(timer); |
| 391 | } | 374 | } |
| @@ -399,10 +382,7 @@ void add_virt_timer_periodic(void *new) | |||
| 399 | struct vtimer_list *timer; | 382 | struct vtimer_list *timer; |
| 400 | 383 | ||
| 401 | timer = (struct vtimer_list *)new; | 384 | timer = (struct vtimer_list *)new; |
| 402 | 385 | prepare_vtimer(timer); | |
| 403 | if (prepare_vtimer(timer) < 0) | ||
| 404 | return; | ||
| 405 | |||
| 406 | timer->interval = timer->expires; | 386 | timer->interval = timer->expires; |
| 407 | internal_add_vtimer(timer); | 387 | internal_add_vtimer(timer); |
| 408 | } | 388 | } |
| @@ -423,15 +403,8 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) | |||
| 423 | unsigned long flags; | 403 | unsigned long flags; |
| 424 | int cpu; | 404 | int cpu; |
| 425 | 405 | ||
| 426 | if (!timer->function) { | 406 | BUG_ON(!timer->function); |
| 427 | printk("mod_virt_timer: uninitialized timer\n"); | 407 | BUG_ON(!expires || expires > VTIMER_MAX_SLICE); |
| 428 | return -EINVAL; | ||
| 429 | } | ||
| 430 | |||
| 431 | if (!expires || expires > VTIMER_MAX_SLICE) { | ||
| 432 | printk("mod_virt_timer: invalid expire range\n"); | ||
| 433 | return -EINVAL; | ||
| 434 | } | ||
| 435 | 408 | ||
| 436 | /* | 409 | /* |
| 437 | * This is a common optimization triggered by the | 410 | * This is a common optimization triggered by the |
| @@ -444,6 +417,9 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) | |||
| 444 | cpu = get_cpu(); | 417 | cpu = get_cpu(); |
| 445 | vt_list = &per_cpu(virt_cpu_timer, cpu); | 418 | vt_list = &per_cpu(virt_cpu_timer, cpu); |
| 446 | 419 | ||
| 420 | /* check if we run on the right CPU */ | ||
| 421 | BUG_ON(timer->cpu != cpu); | ||
| 422 | |||
| 447 | /* disable interrupts before test if timer is pending */ | 423 | /* disable interrupts before test if timer is pending */ |
| 448 | spin_lock_irqsave(&vt_list->lock, flags); | 424 | spin_lock_irqsave(&vt_list->lock, flags); |
| 449 | 425 | ||
| @@ -458,14 +434,6 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) | |||
| 458 | return 0; | 434 | return 0; |
| 459 | } | 435 | } |
| 460 | 436 | ||
| 461 | /* check if we run on the right CPU */ | ||
| 462 | if (timer->cpu != cpu) { | ||
| 463 | printk("mod_virt_timer: running on wrong CPU, check your code\n"); | ||
| 464 | spin_unlock_irqrestore(&vt_list->lock, flags); | ||
| 465 | put_cpu(); | ||
| 466 | return -EINVAL; | ||
| 467 | } | ||
| 468 | |||
| 469 | list_del_init(&timer->entry); | 437 | list_del_init(&timer->entry); |
| 470 | timer->expires = expires; | 438 | timer->expires = expires; |
| 471 | 439 | ||
| @@ -536,24 +504,6 @@ void init_cpu_vtimer(void) | |||
| 536 | 504 | ||
| 537 | } | 505 | } |
| 538 | 506 | ||
| 539 | static int vtimer_idle_notify(struct notifier_block *self, | ||
| 540 | unsigned long action, void *hcpu) | ||
| 541 | { | ||
| 542 | switch (action) { | ||
| 543 | case S390_CPU_IDLE: | ||
| 544 | stop_cpu_timer(); | ||
| 545 | break; | ||
| 546 | case S390_CPU_NOT_IDLE: | ||
| 547 | start_cpu_timer(); | ||
| 548 | break; | ||
| 549 | } | ||
| 550 | return NOTIFY_OK; | ||
| 551 | } | ||
| 552 | |||
| 553 | static struct notifier_block vtimer_idle_nb = { | ||
| 554 | .notifier_call = vtimer_idle_notify, | ||
| 555 | }; | ||
| 556 | |||
| 557 | void __init vtime_init(void) | 507 | void __init vtime_init(void) |
| 558 | { | 508 | { |
| 559 | /* request the cpu timer external interrupt */ | 509 | /* request the cpu timer external interrupt */ |
| @@ -561,9 +511,6 @@ void __init vtime_init(void) | |||
| 561 | &ext_int_info_timer) != 0) | 511 | &ext_int_info_timer) != 0) |
| 562 | panic("Couldn't request external interrupt 0x1005"); | 512 | panic("Couldn't request external interrupt 0x1005"); |
| 563 | 513 | ||
| 564 | if (register_idle_notifier(&vtimer_idle_nb)) | ||
| 565 | panic("Couldn't register idle notifier"); | ||
| 566 | |||
| 567 | /* Enable cpu timer interrupts on the boot cpu. */ | 514 | /* Enable cpu timer interrupts on the boot cpu. */ |
| 568 | init_cpu_vtimer(); | 515 | init_cpu_vtimer(); |
| 569 | } | 516 | } |
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 05598649b326..388cc7420055 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c | |||
| @@ -202,3 +202,22 @@ void free_initrd_mem(unsigned long start, unsigned long end) | |||
| 202 | } | 202 | } |
| 203 | } | 203 | } |
| 204 | #endif | 204 | #endif |
| 205 | |||
| 206 | #ifdef CONFIG_MEMORY_HOTPLUG | ||
| 207 | int arch_add_memory(int nid, u64 start, u64 size) | ||
| 208 | { | ||
| 209 | struct pglist_data *pgdat; | ||
| 210 | struct zone *zone; | ||
| 211 | int rc; | ||
| 212 | |||
| 213 | pgdat = NODE_DATA(nid); | ||
| 214 | zone = pgdat->node_zones + ZONE_NORMAL; | ||
| 215 | rc = vmem_add_mapping(start, size); | ||
| 216 | if (rc) | ||
| 217 | return rc; | ||
| 218 | rc = __add_pages(zone, PFN_DOWN(start), PFN_DOWN(size)); | ||
| 219 | if (rc) | ||
| 220 | vmem_remove_mapping(start, size); | ||
| 221 | return rc; | ||
| 222 | } | ||
| 223 | #endif /* CONFIG_MEMORY_HOTPLUG */ | ||
