From: Hans-Joachim Picht <hpicht@redhat.com> Date: Thu, 12 Mar 2009 15:26:18 +0100 Subject: [s390] kernel: shutdown action 'dump_reipl' Message-id: 20090312142618.GO5103@redhat.com O-Subject: [RHEL5 U4 PATCH 14/20] FEAT: s390 - kernel : Shutdown action "dump_reipl" Bugzilla: 474688 Description ============ A new shutdown action "dump_reipl" is provided combining the already existing actions "dump" and "reipl": first a dump is taken, and after the dump process has completed an automatic re-ipl of the system is triggered. Bugzilla ========= BZ 474688 https://bugzilla.redhat.com/show_bug.cgi?id=474688 Upstream status of the patch: ============================= This patch is currently queued until the 2.6.30 merge window opens. It has been posted upstream for review: http://lkml.indiana.edu/hypermail/linux/kernel/0902.3/00677.html Test status: ============ The patch has been tested by the IBM test department. Please ACK. With best regards, --Hans diff --git a/arch/s390/boot/zfcpdump.c b/arch/s390/boot/zfcpdump.c index b8fc6ac..f2acfc2 100644 --- a/arch/s390/boot/zfcpdump.c +++ b/arch/s390/boot/zfcpdump.c @@ -11,8 +11,6 @@ * Author(s): Michael Holzheu */ -//#define GZIP_SUPPORT - #include <errno.h> #include <string.h> #include <dirent.h> @@ -42,6 +40,7 @@ static struct globals g; static int parse_parameter(char *parameter) { char *token; + char *end_ptr; token = strtok(parameter, "="); if (token == NULL) @@ -72,7 +71,12 @@ static int parse_parameter(char *parameter) "specified\n", PARM_MEM); return -1; } - g.parm_mem = strtoll(mem_str, NULL, 0); + g.parm_mem = strtoll(mem_str, &end_ptr, 0); + if (*end_ptr != 0) { + PRINT_ERR("Invalid value for '%s' parameter " + "specified\n", PARM_MEM); + return -1; + } } else if (strcmp(token, PARM_COMP) == 0) { /* Dump Compression */ g.parm_compress = strtok(NULL, "="); @@ -243,6 +247,7 @@ static int read_file(const char *file, char *buf, int size) static int enable_zfcp_device(void) { char command[1024], file[1024]; + struct stat s; /* device */ if (read_file(IPL_DEVNO, g.dump_devno, sizeof(g.dump_devno))) @@ -255,9 +260,12 @@ static int enable_zfcp_device(void) if (read_file(IPL_WWPN, g.dump_wwpn, sizeof(g.dump_wwpn))) return -1; sprintf(file, "/sys/bus/ccw/drivers/zfcp/%s/port_add", g.dump_devno); - sprintf(command, "%s\n", g.dump_wwpn); - if (write_to_file(file, command)) - return -1; + /* The port_add attribute has been removed in recent kernels */ + if (stat(file, &s) == 0) { + sprintf(command, "%s\n", g.dump_wwpn); + if (write_to_file(file, command)) + return -1; + } /* lun */ if (read_file(IPL_LUN, g.dump_lun, sizeof(g.dump_lun))) @@ -328,7 +336,15 @@ static int umount_dump_device(void) */ static void terminate(void) { + int fd; + sleep(WAIT_TIME_END); /* give the messages time to be displayed */ + fd = open(DEV_ZCORE_REIPL, O_WRONLY, 0); + if (fd == -1) + goto no_reipl; + write(fd, REIPL, 1); + close(fd); +no_reipl: reboot(LINUX_REBOOT_CMD_POWER_OFF); } @@ -661,9 +677,12 @@ static int create_dump(void) struct dump_page dp; char page_buf[DUMP_BUF_SIZE], buf[PAGE_SIZE], dpcpage[PAGE_SIZE]; char dump_name[1024]; - __u64 mem_loc; + __u64 mem_loc, mem_count; __u32 buf_loc = 0, dp_size, dp_flags; - int size, fin, fout; + int size, fin, fout, fmap, rc = 0; + char c_info[CHUNK_INFO_SIZE]; + struct mem_chunk *chunk, *chunk_first = NULL, *chunk_prev = NULL; + char *end_ptr; if (stat(g.dump_dir, &stat_buf) < 0) { PRINT_ERR("Specified dump dir '%s' not found!\n", g.dump_dir); @@ -686,11 +705,65 @@ static int create_dump(void) else return -1; + /* Open the memory map file - only available with kernel 2.6.25 or + * higher. If open fails, memory holes cannot be detected and only + * one single memory chunk is assumed */ + fmap = open(DEV_ZCORE_MAP, O_RDONLY, 0); + if (fmap == -1) { + chunk_first = calloc(1, sizeof(struct mem_chunk)); + if (chunk_first == NULL) { + PRINT_ERR("Could not allocate %d bytes of memory\n", + (int) sizeof(struct mem_chunk)); + return -1; + } + chunk_first->size = PARM_MEM_DFLT; + } else { + /* read information about memory chunks (start address and size) */ + do { + if (read(fmap, c_info, sizeof(c_info)) != sizeof(c_info)) { + PRINT_ERR("read() memory map file '%s' " + "failed!\n", DEV_ZCORE_MAP); + rc = -1; + goto failed_close_fmap; + } + chunk = calloc(1, sizeof(struct mem_chunk)); + if (chunk == NULL) { + PRINT_ERR("Could not allocate %d bytes of " + "memory\n", + (int) sizeof(struct mem_chunk)); + rc = -1; + goto failed_free_chunks; + } + chunk->size = strtoul(c_info + 17, &end_ptr, 16); + if (end_ptr != c_info + 33 || *end_ptr != ' ') { + PRINT_ERR("Invalid contents of memory map " + "file '%s'!\n", DEV_ZCORE_MAP); + rc = -1; + goto failed_free_chunks; + } + if (chunk->size == 0) + break; + chunk->addr = strtoul(c_info, &end_ptr, 16); + if (end_ptr != c_info + 16 || *end_ptr != ' ') { + PRINT_ERR("Invalid contents of memory map " + "file '%s'!\n", DEV_ZCORE_MAP); + rc = -1; + goto failed_free_chunks; + } + if (!chunk_first) + chunk_first = chunk; + else + chunk_prev->next = chunk; + chunk_prev = chunk; + } while (1); + } + /* try to open the source device */ fin = open(DEV_ZCORE, O_RDONLY, 0); if (fin == -1) { PRINT_ERR("open() source device '%s' failed!\n", DEV_ZCORE); - return -1; + rc = -1; + goto failed_free_chunks; } /* make the new filename */ @@ -698,6 +771,7 @@ static int create_dump(void) fout = open(dump_name, DUMP_FLAGS, DUMP_MODE); if (fout == -1) { PRINT_ERR("open() of dump file \"%s\" failed!\n", dump_name); + rc = -1; goto failed_close_fin; } @@ -708,10 +782,12 @@ static int create_dump(void) if (lseek(fin, 0, SEEK_SET) < 0) { PRINT_ERR("Cannot lseek() to get the dump header from the " "dump file!\n"); + rc = -1; goto failed_close_fout; } if (read(fin, &s390_dh, sizeof(s390_dh)) != sizeof(s390_dh)) { PRINT_ERR("Cannot read() dump header from dump file!\n"); + rc = -1; goto failed_close_fout; } @@ -742,21 +818,36 @@ static int create_dump(void) memcpy(page_buf, &dh, sizeof(dh)); if (lseek(fout, 0L, SEEK_SET) < 0) { PRINT_ERR("lseek() failed\n"); + rc = -1; goto failed_close_fout; } if (dump_write(fout, page_buf, DUMP_BUF_SIZE) != DUMP_BUF_SIZE) { PRINT_ERR("Error: Write dump header failed\n"); + rc = -1; goto failed_close_fout; } /* write dump */ + chunk = chunk_first; mem_loc = 0; + mem_count = 0; if (lseek(fin, DUMP_HEADER_SZ_S390SA, SEEK_SET) < 0) { PRINT_ERR("lseek() failed\n"); + rc = -1; goto failed_close_fout; } - while (mem_loc < dh.memory_size) { + while (mem_loc < dh.memory_end) { + if (mem_loc >= chunk->addr + chunk->size) { + chunk = chunk->next; + mem_loc = chunk->addr; + if (lseek(fin, DUMP_HEADER_SZ_S390SA + mem_loc, + SEEK_SET) < 0) { + PRINT_ERR("lseek() failed\n"); + rc = -1; + goto failed_close_fout; + } + } if (read(fin, buf, PAGE_SIZE) != PAGE_SIZE) { if (errno == EFAULT) { /* probably memory hole. Skip page */ @@ -764,6 +855,7 @@ static int create_dump(void) continue; } PRINT_PERR("read error\n"); + rc = -1; goto failed_close_fout; } memset(dpcpage, 0, PAGE_SIZE); @@ -796,11 +888,13 @@ static int create_dump(void) buf_loc += dp_size; if (dump_write(fout, page_buf, buf_loc) != buf_loc) { PRINT_ERR("write error\n"); + rc = -1; goto failed_close_fout; } buf_loc = 0; mem_loc += PAGE_SIZE; - show_progress(mem_loc, dh.memory_size); + mem_count += PAGE_SIZE; + show_progress(mem_count, dh.memory_size); } /* write end marker */ @@ -809,15 +903,21 @@ static int create_dump(void) dp.size = DUMP_DH_END; dp.flags = 0x0; dump_write(fout, &dp, sizeof(dp)); - close(fin); - close(fout); - return 0; failed_close_fout: close(fout); failed_close_fin: close(fin); - return -1; +failed_free_chunks: + chunk = chunk_first; + while (chunk) { + chunk_prev = chunk; + chunk = chunk->next; + free(chunk_prev); + } +failed_close_fmap: + close(fmap); + return rc; } /* diff --git a/arch/s390/boot/zfcpdump.h b/arch/s390/boot/zfcpdump.h index 7fb3914..d0e4b6e 100644 --- a/arch/s390/boot/zfcpdump.h +++ b/arch/s390/boot/zfcpdump.h @@ -12,7 +12,7 @@ #include <signal.h> #include <stdint.h> -#define ZFCPDUMP_VERSION "2.0" +#define ZFCPDUMP_VERSION "2.1" #define PRINT_TRACE(x...) \ do { \ @@ -76,6 +76,9 @@ struct globals { #define PROC_CMDLINE "/proc/cmdline" #define PROC_MISC "/proc/misc" #define DEV_ZCORE "/sys/kernel/debug/zcore/mem" +#define DEV_ZCORE_MAP "/sys/kernel/debug/zcore/memmap" +#define DEV_ZCORE_REIPL "/sys/kernel/debug/zcore/reipl" +#define REIPL "1" #define DEV_SCSI "/dev/sda" #define DUMP_DIR "/mnt" @@ -145,6 +148,7 @@ struct globals { #define DUMP_DH_END 0x4 /* end marker on a full dump */ #define PAGE_SIZE 4096 +#define CHUNK_INFO_SIZE 34 /* 2 16-byte char, each followed by blank */ /* * This is the header dumped at the top of every valid crash dump. @@ -209,6 +213,12 @@ struct dump_page { __u32 flags; /* flags (DUMP_COMPRESSED, DUMP_RAW or DUMP_END) */ } __attribute__((packed)); +struct mem_chunk { + __u64 addr; /* the start address of this memory chunk */ + __u64 size; /* the length of this memory chunk */ + struct mem_chunk *next; /* pointer to next memory chunk */ +}; + /* Compression function */ typedef int (*compress_fn_t)(const unsigned char *old, __u32 old_size, unsigned char *new, __u32 size); diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index c6dfba1..267340c 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -63,13 +63,14 @@ struct shutdown_trigger { }; /* - * Five shutdown action types are supported: + * The following shutdown action types are supported: */ #define SHUTDOWN_ACTION_IPL_STR "ipl" #define SHUTDOWN_ACTION_REIPL_STR "reipl" #define SHUTDOWN_ACTION_DUMP_STR "dump" #define SHUTDOWN_ACTION_VMCMD_STR "vmcmd" #define SHUTDOWN_ACTION_STOP_STR "stop" +#define SHUTDOWN_ACTION_DUMP_REIPL_STR "dump_reipl" struct shutdown_action { char *name; @@ -153,6 +154,7 @@ static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; static struct ipl_parameter_block *reipl_block_fcp; static struct ipl_parameter_block *reipl_block_ccw; static struct ipl_parameter_block *reipl_block_nss; +static struct ipl_parameter_block *reipl_block_actual; static int dump_capabilities = DUMP_TYPE_NONE; static enum dump_type dump_type = DUMP_TYPE_NONE; @@ -870,6 +872,7 @@ static int reipl_set_type(enum ipl_type type) reipl_method = REIPL_METHOD_CCW_VM; else reipl_method = REIPL_METHOD_CCW_CIO; + reipl_block_actual = reipl_block_ccw; break; case IPL_TYPE_FCP: if (diag308_set_works) @@ -878,6 +881,7 @@ static int reipl_set_type(enum ipl_type type) reipl_method = REIPL_METHOD_FCP_RO_VM; else reipl_method = REIPL_METHOD_FCP_RO_DIAG; + reipl_block_actual = reipl_block_fcp; break; case IPL_TYPE_FCP_DUMP: reipl_method = REIPL_METHOD_FCP_DUMP; @@ -887,6 +891,7 @@ static int reipl_set_type(enum ipl_type type) reipl_method = REIPL_METHOD_NSS_DIAG; else reipl_method = REIPL_METHOD_NSS; + reipl_block_actual = reipl_block_nss; break; case IPL_TYPE_UNKNOWN: reipl_method = REIPL_METHOD_DEFAULT; @@ -1362,6 +1367,48 @@ static struct shutdown_action dump_action = { .init = dump_init, }; +static void dump_reipl_run(struct shutdown_trigger *trigger) +{ + preempt_disable(); + /* + * Bypass dynamic address translation (DAT) when storing IPL parameter + * information block address and checksum into the prefix area + * (corresponding to absolute addresses 0-8191). + * When enhanced DAT applies and the STE format control in one, + * the absolute address is formed without prefixing. In this case a + * normal store (stg/st) into the prefix area would no more match to + * absolute addresses 0-8191. + */ +#ifdef CONFIG_64BIT + asm volatile("sturg %0,%1" + :: "a" ((unsigned long) reipl_block_actual), + "a" (&lowcore_ptr[smp_processor_id()]->ipib)); +#else + asm volatile("stura %0,%1" + :: "a" ((unsigned long) reipl_block_actual), + "a" (&lowcore_ptr[smp_processor_id()]->ipib)); +#endif + asm volatile("stura %0,%1" + :: "a" (cksm(reipl_block_actual, reipl_block_actual->hdr.len)), + "a" (&lowcore_ptr[smp_processor_id()]->ipib_checksum)); + preempt_enable(); + dump_run(trigger); +} + +static int __init dump_reipl_init(void) +{ + if (!diag308_set_works) + return -EOPNOTSUPP; + else + return 0; +} + +static struct shutdown_action dump_reipl_action = { + .name = SHUTDOWN_ACTION_DUMP_REIPL_STR, + .fn = dump_reipl_run, + .init = dump_reipl_init, +}; + /* * vmcmd shutdown action: Trigger vm command on shutdown. */ @@ -1453,7 +1500,8 @@ static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, /* action list */ static struct shutdown_action *shutdown_actions_list[] = { - &ipl_action, &reipl_action, &dump_action, &vmcmd_action, &stop_action}; + &ipl_action, &reipl_action, &dump_reipl_action, &dump_action, + &vmcmd_action, &stop_action}; #define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *)) /* @@ -1465,12 +1513,18 @@ static decl_subsys(shutdown_actions, NULL, NULL); static int set_trigger(const char *buf, struct shutdown_trigger *trigger, size_t len) { - int i; + int i, buflen; + + if (buf[strlen(buf) - 1] == '\n') + buflen = strlen(buf) - 1; + else + buflen = strlen(buf); for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { if (!shutdown_actions_list[i]) continue; - if (strncmp(buf, shutdown_actions_list[i]->name, - strlen(shutdown_actions_list[i]->name)) == 0) { + if (strlen(shutdown_actions_list[i]->name) != buflen) + continue; + if (strncmp(buf, shutdown_actions_list[i]->name, buflen) == 0) { trigger->action = shutdown_actions_list[i]; return len; } diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 0c71557..5cd2733 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -630,6 +630,7 @@ setup_lowcore(void) } #endif set_prefix((u32)(unsigned long) lc); + lowcore_ptr[0] = lc; } static void __init diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 29148ec..12c27c2 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -239,9 +239,6 @@ static inline void do_store_status(void) */ void smp_send_stop(void) { - /* write magic number to zero page (absolute 0) */ - lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC; - /* stop other processors. */ do_send_stop(); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 056dfb5..d68c831 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -5,7 +5,7 @@ * * For more information please refer to Documentation/s390/zfcpdump.txt * - * Copyright IBM Corp. 2003,2007 + * Copyright IBM Corp. 2003,2008 * Author(s): Michael Holzheu */ @@ -48,11 +48,18 @@ struct sys_info { union save_area lc_mask; }; +struct ipib_info { + unsigned long ipib; + u32 checksum; +} __attribute__((packed)); + static struct sys_info sys_info; static struct debug_info *zcore_dbf; static int hsa_available; static struct dentry *zcore_dir; static struct dentry *zcore_file; +static struct dentry *zcore_reipl_file; +static struct ipl_parameter_block *ipl_block; /* * Copy memory from HSA to kernel or user memory (not reentrant): @@ -521,6 +528,33 @@ static struct file_operations zcore_fops = { .release = zcore_release, }; +static ssize_t zcore_reipl_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (ipl_block) { + diag308(DIAG308_SET, ipl_block); + diag308(DIAG308_IPL, NULL); + } + return count; +} + +static int zcore_reipl_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int zcore_reipl_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static const struct file_operations zcore_reipl_fops = { + .owner = THIS_MODULE, + .write = zcore_reipl_write, + .open = zcore_reipl_open, + .release = zcore_reipl_release, +}; + static void __init set_s390_lc_mask(union save_area *map) { @@ -612,6 +646,39 @@ static void __init zcore_header_init(int arch, struct zcore_header *hdr) get_cpu_id(&hdr->cpu_id); } +/* + * Provide IPL parameter information block from either HSA or memory + * for future reipl + */ +static int __init zcore_reipl_init(void) +{ + struct ipib_info ipib_info; + int rc; + + rc = memcpy_hsa_kernel(&ipib_info, __LC_DUMP_REIPL, sizeof(ipib_info)); + if (rc) + return rc; + if (ipib_info.ipib == 0) + return 0; + ipl_block = (void *) __get_free_page(GFP_KERNEL); + if (!ipl_block) + return -ENOMEM; + if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE) + rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE); + else + rc = memcpy_real(ipl_block, ipib_info.ipib, PAGE_SIZE); + if (rc) { + free_page((unsigned long) ipl_block); + return rc; + } + if (cksm(ipl_block, ipl_block->hdr.len) != ipib_info.checksum) { + TRACE("Checksum does not match\n"); + free_page((unsigned long) ipl_block); + ipl_block = NULL; + } + return 0; +} + static int __init zcore_init(void) { unsigned char arch; @@ -660,6 +727,10 @@ static int __init zcore_init(void) zcore_header_init(arch, &zcore_header); + rc = zcore_reipl_init(); + if (rc) + goto fail; + zcore_dir = debugfs_create_dir("zcore" , NULL); if (!zcore_dir) { rc = -ENOMEM; @@ -670,11 +741,21 @@ static int __init zcore_init(void) if (!zcore_file) { debugfs_remove(zcore_dir); rc = -ENOMEM; - goto fail; + goto fail_dir; + } + zcore_reipl_file = debugfs_create_file("reipl", S_IRUSR, zcore_dir, + NULL, &zcore_reipl_fops); + if (!zcore_reipl_file) { + rc = -ENOMEM; + goto fail_file; } hsa_available = 1; return 0; +fail_file: + debugfs_remove(zcore_file); +fail_dir: + debugfs_remove(zcore_dir); fail: diag308(DIAG308_REL_HSA, NULL); return rc; @@ -684,10 +765,14 @@ static void __exit zcore_exit(void) { debug_unregister(zcore_dbf); sclp_sdias_exit(); + free_page((unsigned long) ipl_block); + debugfs_remove(zcore_reipl_file); + debugfs_remove(zcore_file); + debugfs_remove(zcore_dir); diag308(DIAG308_REL_HSA, NULL); } -MODULE_AUTHOR("Copyright IBM Corp. 2003,2007"); +MODULE_AUTHOR("Copyright IBM Corp. 2003,2008"); MODULE_DESCRIPTION("zcore module for zfcpdump support"); MODULE_LICENSE("GPL"); diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h index 1171e6d..70fcffe 100644 --- a/include/asm-s390/ipl.h +++ b/include/asm-s390/ipl.h @@ -164,5 +164,6 @@ enum diag308_rc { }; extern int diag308(unsigned long subcode, void *addr); +extern u32 cksm(void *addr, unsigned long len); #endif /* _ASM_S390_IPL_H */ diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index 57af032..bca9946 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -109,7 +109,7 @@ #endif /* __s390x__ */ -#define __LC_PANIC_MAGIC 0xE00 +#define __LC_DUMP_REIPL 0xE00 #ifndef __s390x__ #define __LC_PFAULT_INTPARM 0x080 #define __LC_CPU_TIMER_SAVE_AREA 0x0D8 @@ -283,12 +283,14 @@ struct _lowcore __u64 int_clock; /* 0xc98 */ __u8 pad11[0xe00-0xca0]; /* 0xca0 */ - /* 0xe00 is used as indicator for dump tools */ - /* whether the kernel died with panic() or not */ - __u32 panic_magic; /* 0xe00 */ + /* 0xe00 contains the address of the IPL Parameter */ + /* Information block. Dump tools need IPIB for IPL */ + /* after dump. */ + __u32 ipib; /* 0xe00 */ + __u32 ipib_checksum; /* 0xe04 */ /* Align to the top 1k of prefix area */ - __u8 pad12[0x1000-0xe04]; /* 0xe04 */ + __u8 pad12[0x1000-0xe08]; /* 0xe08 */ #else /* !__s390x__ */ /* prefix area: defined by architecture */ __u32 ccw1[2]; /* 0x000 */ @@ -374,11 +376,13 @@ struct _lowcore __u64 int_clock; /* 0xde8 */ __u8 pad12[0xe00-0xdf0]; /* 0xdf0 */ - /* 0xe00 is used as indicator for dump tools */ - /* whether the kernel died with panic() or not */ - __u32 panic_magic; /* 0xe00 */ + /* 0xe00 contains the address of the IPL Parameter */ + /* Information block. Dump tools need IPIB for IPL */ + /* after dump. */ + __u64 ipib; /* 0xe00 */ + __u32 ipib_checksum; /* 0xe08 */ - __u8 pad13[0x1200-0xe04]; /* 0xe04 */ + __u8 pad13[0x1200-0xe0c]; /* 0xe0c */ /* System info area */ @@ -418,8 +422,6 @@ static inline __u32 store_prefix(void) return address; } -#define __PANIC_MAGIC 0xDEADC0DE - #endif #endif diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index e029f17..63c4df0 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -478,6 +478,22 @@ extern void smp_ctl_clear_bit(int cr, int bit); #endif /* CONFIG_SMP */ +static inline u32 cksm(void *addr, unsigned long len) +{ + register unsigned long _addr asm("0") = (unsigned long) addr; + register unsigned long _len asm("1") = len; + unsigned long accu = 0; + + asm volatile( + "0:\n" + " cksm %0,%1\n" + " jnz 0b\n" + : "+d" (accu), "+d" (_addr), "+d" (_len) + : + : "cc", "memory"); + return accu; +} + extern void (*_machine_restart)(char *command); extern void (*_machine_halt)(void); extern void (*_machine_power_off)(void);