📄 hwc_rw.c
字号:
/* * drivers/s390/char/hwc_rw.c * driver: reading from and writing to system console on S/390 via HWC * * S390 version * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Peschke <peschke@fh-brandenburg.de> * * * * * * */#include <linux/config.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/ctype.h>#include <linux/mm.h>#include <linux/timer.h>#include <linux/bootmem.h>#include <asm/ebcdic.h>#include <asm/uaccess.h>#include <asm/types.h>#include <asm/bitops.h>#include <asm/setup.h>#include <asm/page.h>#ifndef MIN#define MIN(a,b) ((a<b) ? a : b)#endif#define HWC_RW_PRINT_HEADER "hwc low level driver: "#define USE_VM_DETECTION#define DEFAULT_CASE_DELIMITER '%'#define DUMP_HWC_INIT_ERROR#define DUMP_HWC_WRITE_ERROR#define DUMP_HWC_WRITE_LIST_ERROR#define DUMP_HWC_READ_ERROR#undef DUMP_HWCB_INPUT#undef BUFFER_STRESS_TESTtypedef struct { unsigned char *next; unsigned short int mto_char_sum; unsigned char mto_number; unsigned char times_lost; unsigned short int mto_number_lost; unsigned long int mto_char_sum_lost;} __attribute__ ((packed)) hwcb_list_t;#define MAX_HWCB_ROOM (PAGE_SIZE - sizeof(hwcb_list_t))#define MAX_MESSAGE_SIZE (MAX_HWCB_ROOM - sizeof(write_hwcb_t))#define BUF_HWCB hwc_data.hwcb_list_tail#define OUT_HWCB hwc_data.hwcb_list_head#define ALL_HWCB_MTO hwc_data.mto_number#define ALL_HWCB_CHAR hwc_data.mto_char_sum#define _LIST(hwcb) ((hwcb_list_t*)(&(hwcb)[PAGE_SIZE-sizeof(hwcb_list_t)]))#define _HWCB_CHAR(hwcb) (_LIST(hwcb)->mto_char_sum)#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number)#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost)#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost)#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost)#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next)#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB)#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB)#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB)#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB)#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB)#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB)#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB)#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB)#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB)#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB)#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB)#include "hwc.h"#define __HWC_RW_C__#include "hwc_rw.h"#undef __HWC_RW_C__static unsigned char _obuf[MAX_HWCB_ROOM];static unsigned char _page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));typedef u32 kmem_pages_t;#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)#define HWC_TIMER_RUNS 1#define FLUSH_HWCBS 2static struct { hwc_ioctls_t ioctls; hwc_ioctls_t init_ioctls; unsigned char *hwcb_list_head; unsigned char *hwcb_list_tail; unsigned short int mto_number; unsigned int mto_char_sum; unsigned char hwcb_count; unsigned long kmem_start; unsigned long kmem_end; kmem_pages_t kmem_pages; unsigned char *obuf; unsigned short int obuf_cursor; unsigned short int obuf_count; unsigned short int obuf_start; unsigned char *page; u32 current_servc; unsigned char *current_hwcb; unsigned char write_nonprio:1; unsigned char write_prio:1; unsigned char read_nonprio:1; unsigned char read_prio:1; unsigned char flags; spinlock_t lock; struct timer_list write_timer;} hwc_data ={ { }, { 8, 0, 80, CODE_ASCII, 1, 50, MAX_KMEM_PAGES, 0, 0x6c }, NULL, NULL, 0, 0, 0, 0, 0, 0, _obuf, 0, 0, 0, _page, 0, NULL, 0, 0, 0, 0, 0};#define DELAYED_WRITE 0#define IMMEDIATE_WRITE 1static signed int do_hwc_write (int from_user, unsigned char *, unsigned int, unsigned char, unsigned char);static asmlinkage int internal_print (char write_time, const char *fmt,...){ va_list args; int i; unsigned char buf[512]; va_start (args, fmt); i = vsprintf (buf, fmt, args); va_end (args); return do_hwc_write (0, buf, i, CODE_ASCII, write_time);}int hwc_printk (const char *fmt,...){ va_list args; int i; unsigned char buf[512]; unsigned long flags; int retval; spin_lock_irqsave (&hwc_data.lock, flags); i = vsprintf (buf, fmt, args); va_end (args); retval = do_hwc_write (0, buf, i, CODE_ASCII, IMMEDIATE_WRITE); spin_unlock_irqrestore (&hwc_data.lock, flags); return retval;}#ifdef DUMP_HWCB_INPUTstatic void dump_storage_area (unsigned char *area, unsigned short int count){ unsigned short int index; ioctl_nl_t old_final_nl; if (!area || !count) return; old_final_nl = hwc_data.ioctls.final_nl; hwc_data.ioctls.final_nl = 1; internal_print (DELAYED_WRITE, "\n%8x ", area); for (index = 0; index < count; index++) { if (area[index] <= 0xF) internal_print (DELAYED_WRITE, "0%x", area[index]); else internal_print (DELAYED_WRITE, "%x", area[index]); if ((index & 0xF) == 0xF) internal_print (DELAYED_WRITE, "\n%8x ", &area[index + 1]); else if ((index & 3) == 3) internal_print (DELAYED_WRITE, " "); } internal_print (IMMEDIATE_WRITE, "\n"); hwc_data.ioctls.final_nl = old_final_nl;}#endifstatic inline u32 service_call ( u32 hwc_command_word, unsigned char hwcb[]){ unsigned int condition_code = 1; __asm__ __volatile__ ("L 1, 0(0,%0) \n\t" "LRA 2, 0(0,%1) \n\t" ".long 0xB2200012 \n\t" : :"a" (&hwc_command_word), "a" (hwcb) :"1", "2", "memory"); __asm__ __volatile__ ("IPM %0 \n\t" "SRL %0, 28 \n\t" :"=r" (condition_code)); return condition_code;}static inline unsigned char *ext_int_param (void){ u32 param; __asm__ __volatile__ ("L %0,128(0,0)\n\t" :"=r" (param)); return ((unsigned char *) param);}static int prepare_write_hwcb (void){ write_hwcb_t *hwcb; if (!BUF_HWCB) return -ENOMEM; BUF_HWCB_MTO = 0; BUF_HWCB_CHAR = 0; hwcb = (write_hwcb_t *) BUF_HWCB; memcpy (hwcb, &write_hwcb_template, sizeof (write_hwcb_t)); if (!hwc_data.write_nonprio && hwc_data.write_prio) hwcb->msgbuf.type = ET_PMsgCmd; return 0;}static int sane_write_hwcb (void){ unsigned short int lost_msg; unsigned int lost_char; unsigned char lost_hwcb; unsigned char *bad_addr; unsigned long page; int page_nr; if (!OUT_HWCB) return -ENOMEM; if ((unsigned long) OUT_HWCB & 0xFFF) { bad_addr = OUT_HWCB;#ifdef DUMP_HWC_WRITE_LIST_ERROR __asm__ ("LHI 1,0xe30\n\t" "LRA 2,0(0,%0) \n\t" "J .+0 \n\t" : : "a" (bad_addr) : "1", "2");#endif hwc_data.kmem_pages = 0; if ((unsigned long) BUF_HWCB & 0xFFF) { lost_hwcb = hwc_data.hwcb_count; lost_msg = ALL_HWCB_MTO; lost_char = ALL_HWCB_CHAR; OUT_HWCB = NULL; BUF_HWCB = NULL; ALL_HWCB_MTO = 0; ALL_HWCB_CHAR = 0; hwc_data.hwcb_count = 0; } else { lost_hwcb = hwc_data.hwcb_count - 1; lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO; lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR; OUT_HWCB = BUF_HWCB; ALL_HWCB_MTO = BUF_HWCB_MTO; ALL_HWCB_CHAR = BUF_HWCB_CHAR; hwc_data.hwcb_count = 1; page = (unsigned long) BUF_HWCB; if (page >= hwc_data.kmem_start && page < hwc_data.kmem_end) { page_nr = (int) ((page - hwc_data.kmem_start) >> 12); set_bit (page_nr, &hwc_data.kmem_pages); } } internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "found invalid HWCB at address 0x%x. List corrupted. " "Lost %i HWCBs with %i characters within up to %i " "messages. Saved %i HWCB with last %i characters i" "within up to %i messages.\n", (unsigned int) bad_addr, lost_hwcb, lost_char, lost_msg, hwc_data.hwcb_count, ALL_HWCB_CHAR, ALL_HWCB_MTO); } return 0;}static int reuse_write_hwcb (void){ int retval; if (hwc_data.hwcb_count < 2)#ifdef DUMP_HWC_WRITE_LIST_ERROR __asm__ ("LHI 1,0xe31\n\t" "LRA 2,0(0,%0)\n\t" "LRA 3,0(0,%1)\n\t" "J .+0 \n\t" : : "a" (BUF_HWCB), "a" (OUT_HWCB) : "1", "2", "3");#else return -EPERM;#endif if (hwc_data.current_hwcb == OUT_HWCB) { if (hwc_data.hwcb_count > 2) { BUF_HWCB_NEXT = OUT_HWCB_NEXT; BUF_HWCB = OUT_HWCB_NEXT; OUT_HWCB_NEXT = BUF_HWCB_NEXT; BUF_HWCB_NEXT = NULL; } } else { BUF_HWCB_NEXT = OUT_HWCB; BUF_HWCB = OUT_HWCB; OUT_HWCB = OUT_HWCB_NEXT; BUF_HWCB_NEXT = NULL; } BUF_HWCB_TIMES_LOST += 1; BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR; BUF_HWCB_MTO_LOST += BUF_HWCB_MTO; ALL_HWCB_MTO -= BUF_HWCB_MTO; ALL_HWCB_CHAR -= BUF_HWCB_CHAR; retval = prepare_write_hwcb (); if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb) internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "reached my own limit of " "allowed buffer space for output (%i HWCBs = %li " "bytes), skipped content of oldest HWCB %i time(s) " "(%i lines = %i characters)\n", hwc_data.ioctls.max_hwcb, hwc_data.ioctls.max_hwcb * PAGE_SIZE, BUF_HWCB_TIMES_LOST, BUF_HWCB_MTO_LOST, BUF_HWCB_CHAR_LOST); else internal_print ( DELAYED_WRITE, HWC_RW_PRINT_HEADER "page allocation failed, " "could not expand buffer for output (currently in " "use: %i HWCBs = %li bytes), skipped content of " "oldest HWCB %i time(s) (%i lines = %i characters)\n", hwc_data.hwcb_count, hwc_data.hwcb_count * PAGE_SIZE, BUF_HWCB_TIMES_LOST, BUF_HWCB_MTO_LOST, BUF_HWCB_CHAR_LOST); return retval;}static int allocate_write_hwcb (void){ unsigned char *page; int page_nr; if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb) return -ENOMEM; page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES); if (page_nr < hwc_data.ioctls.kmem_hwcb) { page = (unsigned char *) (hwc_data.kmem_start + (page_nr << 12)); set_bit (page_nr, &hwc_data.kmem_pages); } else page = (unsigned char *) __get_free_page (GFP_ATOMIC); if (!page) return -ENOMEM; if (!OUT_HWCB) OUT_HWCB = page; else BUF_HWCB_NEXT = page; BUF_HWCB = page; BUF_HWCB_NEXT = NULL; hwc_data.hwcb_count++; prepare_write_hwcb (); BUF_HWCB_TIMES_LOST = 0; BUF_HWCB_MTO_LOST = 0; BUF_HWCB_CHAR_LOST = 0;#ifdef BUFFER_STRESS_TEST internal_print ( DELAYED_WRITE, "*** " HWC_RW_PRINT_HEADER "page #%i at 0x%x for buffering allocated. ***\n", hwc_data.hwcb_count, page);#endif return 0;}static int release_write_hwcb (void){ unsigned long page; int page_nr; if (!hwc_data.hwcb_count) return -ENODATA; if (hwc_data.hwcb_count == 1) { prepare_write_hwcb (); ALL_HWCB_CHAR = 0; ALL_HWCB_MTO = 0; BUF_HWCB_TIMES_LOST = 0; BUF_HWCB_MTO_LOST = 0; BUF_HWCB_CHAR_LOST = 0; } else { page = (unsigned long) OUT_HWCB; ALL_HWCB_MTO -= OUT_HWCB_MTO; ALL_HWCB_CHAR -= OUT_HWCB_CHAR; hwc_data.hwcb_count--; OUT_HWCB = OUT_HWCB_NEXT; if (page >= hwc_data.kmem_start && page < hwc_data.kmem_end) { memset ((void *) page, 0, PAGE_SIZE); page_nr = (int) ((page - hwc_data.kmem_start) >> 12); clear_bit (page_nr, &hwc_data.kmem_pages); } else free_page (page);#ifdef BUFFER_STRESS_TEST internal_print ( DELAYED_WRITE, "*** " HWC_RW_PRINT_HEADER "page at 0x%x released, %i pages still in use ***\n", page, hwc_data.hwcb_count);#endif } return 0;}static int add_mto ( unsigned char *message, unsigned short int count){ unsigned short int mto_size; write_hwcb_t *hwcb; mto_t *mto; void *dest; if (!BUF_HWCB) return -ENOMEM; if (BUF_HWCB == hwc_data.current_hwcb) return -ENOMEM; mto_size = sizeof (mto_t) + count; hwcb = (write_hwcb_t *) BUF_HWCB; if ((MAX_HWCB_ROOM - hwcb->length) < mto_size) return -ENOMEM; mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length); memcpy (mto, &mto_template, sizeof (mto_t)); dest = (void *) (((unsigned long) mto) + sizeof (mto_t)); memcpy (dest, message, count); mto->length += count; hwcb->length += mto_size; hwcb->msgbuf.length += mto_size; hwcb->msgbuf.mdb.length += mto_size; BUF_HWCB_MTO++; ALL_HWCB_MTO++; BUF_HWCB_CHAR += count; ALL_HWCB_CHAR += count; return count;}static int write_event_data_1 (void){ unsigned short int condition_code; int retval; if ((!hwc_data.write_prio) && (!hwc_data.write_nonprio)) return -EPERM; if (hwc_data.current_servc) return -EBUSY; retval = sane_write_hwcb (); if (retval < 0) return retval; if (!OUT_HWCB_MTO) return -ENODATA; condition_code = service_call (HWC_CMDW_WRITEDATA, OUT_HWCB);#ifdef DUMP_HWC_WRITE_ERROR if (condition_code != HWC_COMMAND_INITIATED) __asm__ ("LHI 1,0xe20\n\t" "L 2,0(0,%0)\n\t" "LRA 3,0(0,%1)\n\t" "J .+0 \n\t" : : "a" (&condition_code), "a" (OUT_HWCB) : "1", "2", "3");#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -