Commit 4e3acbea authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: console driver.

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Console driver fixes:
 - Fix ^n at end-of-line check in 3215 and sclp driver.
 - Fix copy_from_user/might_sleep/spinlock problem in sclp.
parent be457375
...@@ -454,7 +454,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -454,7 +454,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
memcpy(tty->flip.char_buf_ptr, memcpy(tty->flip.char_buf_ptr,
raw->inbuf, count); raw->inbuf, count);
if (count < 2 || if (count < 2 ||
(strncmp(raw->inbuf+count-2, "^n", 2) || (strncmp(raw->inbuf+count-2, "^n", 2) &&
strncmp(raw->inbuf+count-2, "\252n", 2)) ) { strncmp(raw->inbuf+count-2, "\252n", 2)) ) {
/* don't add the auto \n */ /* don't add the auto \n */
tty->flip.char_buf_ptr[count] = '\n'; tty->flip.char_buf_ptr[count] = '\n';
......
...@@ -529,8 +529,8 @@ sclp_tty_input(unsigned char* buf, unsigned int count) ...@@ -529,8 +529,8 @@ sclp_tty_input(unsigned char* buf, unsigned int count)
/* send (normal) input to line discipline */ /* send (normal) input to line discipline */
memcpy(sclp_tty->flip.char_buf_ptr, buf, count); memcpy(sclp_tty->flip.char_buf_ptr, buf, count);
if (count < 2 || if (count < 2 ||
strncmp ((const char *) buf + count - 2, "^n", 2) || (strncmp ((const char *) buf + count - 2, "^n", 2) &&
strncmp ((const char *) buf + count - 2, "\0252n", 2)) { strncmp ((const char *) buf + count - 2, "\0252n", 2))) {
sclp_tty->flip.char_buf_ptr[count] = '\n'; sclp_tty->flip.char_buf_ptr[count] = '\n';
count++; count++;
} else } else
...@@ -636,7 +636,7 @@ find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id) ...@@ -636,7 +636,7 @@ find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id)
{ {
struct gds_vector *vec; struct gds_vector *vec;
for (vec = start; vec < end; (void *) vec += vec->length) for (vec = start; vec < end; vec = (void *) vec + vec->length)
if (vec->gds_id == id) if (vec->gds_id == id)
return vec; return vec;
return NULL; return NULL;
...@@ -648,7 +648,8 @@ find_gds_subvector(struct gds_subvector *start, ...@@ -648,7 +648,8 @@ find_gds_subvector(struct gds_subvector *start,
{ {
struct gds_subvector *subvec; struct gds_subvector *subvec;
for (subvec = start; subvec < end; (void *) subvec += subvec->length) for (subvec = start; subvec < end;
subvec = (void *) subvec + subvec->length)
if (subvec->key == key) if (subvec->key == key)
return subvec; return subvec;
return NULL; return NULL;
...@@ -667,7 +668,7 @@ sclp_eval_selfdeftextmsg(struct gds_subvector *start, ...@@ -667,7 +668,7 @@ sclp_eval_selfdeftextmsg(struct gds_subvector *start,
break; break;
sclp_get_input((unsigned char *)(subvec + 1), sclp_get_input((unsigned char *)(subvec + 1),
(unsigned char *) subvec + subvec->length); (unsigned char *) subvec + subvec->length);
(void *) subvec += subvec->length; subvec = (void *) subvec + subvec->length;
} }
} }
...@@ -685,7 +686,7 @@ sclp_eval_textcmd(struct gds_subvector *start, ...@@ -685,7 +686,7 @@ sclp_eval_textcmd(struct gds_subvector *start,
break; break;
sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
(void *)subvec + subvec->length); (void *)subvec + subvec->length);
(void *) subvec += subvec->length; subvec = (void *) subvec + subvec->length;
} }
} }
...@@ -701,7 +702,7 @@ sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end) ...@@ -701,7 +702,7 @@ sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
break; break;
sclp_eval_textcmd((struct gds_subvector *)(vec + 1), sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
(void *) vec + vec->length); (void *) vec + vec->length);
(void *) vec += vec->length; vec = (void *) vec + vec->length;
} }
} }
......
...@@ -32,9 +32,10 @@ ...@@ -32,9 +32,10 @@
#define SCLP_VT220_MAJOR TTY_MAJOR #define SCLP_VT220_MAJOR TTY_MAJOR
#define SCLP_VT220_MINOR 65 #define SCLP_VT220_MINOR 65
#define SCLP_VT220_DRIVER_NAME "sclp_vt220" #define SCLP_VT220_DRIVER_NAME "sclp_vt220"
#define SCLP_VT220_DEVICE_NAME "sclp_vt" #define SCLP_VT220_DEVICE_NAME "ttysclp"
#define SCLP_VT220_CONSOLE_NAME "ttyS" #define SCLP_VT220_CONSOLE_NAME "ttyS"
#define SCLP_VT220_CONSOLE_INDEX 1 /* console=ttyS1 */ #define SCLP_VT220_CONSOLE_INDEX 1 /* console=ttyS1 */
#define SCLP_VT220_BUF_SIZE 80
/* Representation of a single write request */ /* Representation of a single write request */
struct sclp_vt220_request { struct sclp_vt220_request {
...@@ -336,12 +337,11 @@ sclp_vt220_chars_stored(struct sclp_vt220_request *request) ...@@ -336,12 +337,11 @@ sclp_vt220_chars_stored(struct sclp_vt220_request *request)
/* /*
* Add msg to buffer associated with request. Return the number of characters * Add msg to buffer associated with request. Return the number of characters
* added or -EFAULT on error. * added.
*/ */
static int static int
sclp_vt220_add_msg(struct sclp_vt220_request *request, sclp_vt220_add_msg(struct sclp_vt220_request *request,
const unsigned char *msg, int count, int from_user, const unsigned char *msg, int count, int convertlf)
int convertlf)
{ {
struct sclp_vt220_sccb *sccb; struct sclp_vt220_sccb *sccb;
void *buffer; void *buffer;
...@@ -363,11 +363,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request, ...@@ -363,11 +363,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request,
(from < count) && (to < sclp_vt220_space_left(request)); (from < count) && (to < sclp_vt220_space_left(request));
from++) { from++) {
/* Retrieve character */ /* Retrieve character */
if (from_user) { c = msg[from];
if (get_user(c, msg + from) != 0)
return -EFAULT;
} else
c = msg[from];
/* Perform conversion */ /* Perform conversion */
if (c == 0x0a) { if (c == 0x0a) {
if (to + 1 < sclp_vt220_space_left(request)) { if (to + 1 < sclp_vt220_space_left(request)) {
...@@ -383,12 +379,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request, ...@@ -383,12 +379,7 @@ sclp_vt220_add_msg(struct sclp_vt220_request *request,
sccb->evbuf.length += to; sccb->evbuf.length += to;
return from; return from;
} else { } else {
if (from_user) { memcpy(buffer, (const void *) msg, count);
if (copy_from_user(buffer, (void *) msg, count) != 0)
return -EFAULT;
}
else
memcpy(buffer, (const void *) msg, count);
sccb->header.length += count; sccb->header.length += count;
sccb->evbuf.length += count; sccb->evbuf.length += count;
return count; return count;
...@@ -408,7 +399,7 @@ sclp_vt220_timeout(unsigned long data) ...@@ -408,7 +399,7 @@ sclp_vt220_timeout(unsigned long data)
/* /*
* Internal implementation of the write function. Write COUNT bytes of data * Internal implementation of the write function. Write COUNT bytes of data
* from memory at BUF which may reside in user space (specified by FROM_USER) * from memory at BUF
* to the SCLP interface. In case that the data does not fit into the current * to the SCLP interface. In case that the data does not fit into the current
* write buffer, emit the current one and allocate a new one. If there are no * write buffer, emit the current one and allocate a new one. If there are no
* more empty buffers available, wait until one gets emptied. If DO_SCHEDULE * more empty buffers available, wait until one gets emptied. If DO_SCHEDULE
...@@ -419,8 +410,8 @@ sclp_vt220_timeout(unsigned long data) ...@@ -419,8 +410,8 @@ sclp_vt220_timeout(unsigned long data)
* of bytes written. * of bytes written.
*/ */
static int static int
__sclp_vt220_write(int from_user, const unsigned char *buf, int count, __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,
int do_schedule, int convertlf) int convertlf)
{ {
unsigned long flags; unsigned long flags;
void *page; void *page;
...@@ -451,10 +442,9 @@ __sclp_vt220_write(int from_user, const unsigned char *buf, int count, ...@@ -451,10 +442,9 @@ __sclp_vt220_write(int from_user, const unsigned char *buf, int count,
} }
/* Try to write the string to the current request buffer */ /* Try to write the string to the current request buffer */
written = sclp_vt220_add_msg(sclp_vt220_current_request, written = sclp_vt220_add_msg(sclp_vt220_current_request,
buf, count, from_user, convertlf); buf, count, convertlf);
if (written > 0) overall_written += written;
overall_written += written; if (written == count)
if (written == -EFAULT || written == count)
break; break;
/* /*
* Not all characters could be written to the current * Not all characters could be written to the current
...@@ -489,7 +479,29 @@ static int ...@@ -489,7 +479,29 @@ static int
sclp_vt220_write(struct tty_struct *tty, int from_user, sclp_vt220_write(struct tty_struct *tty, int from_user,
const unsigned char *buf, int count) const unsigned char *buf, int count)
{ {
return __sclp_vt220_write(from_user, buf, count, 1, 0); int length;
int ret;
if (!from_user)
return __sclp_vt220_write(buf, count, 1, 0);
/* Use intermediate buffer to prevent calling copy_from_user() while
* holding a lock. */
ret = 0;
while (count > 0) {
length = count < SCLP_VT220_BUF_SIZE ?
count : SCLP_VT220_BUF_SIZE;
length -= copy_from_user(tty->driver_data, buf, length);
if (length == 0) {
if (!ret)
return -EFAULT;
break;
}
length = __sclp_vt220_write(tty->driver_data, length, 1, 0);
buf += length;
count -= length;
ret += length;
}
return ret;
} }
#define SCLP_VT220_SESSION_ENDED 0x01 #define SCLP_VT220_SESSION_ENDED 0x01
...@@ -541,9 +553,13 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf) ...@@ -541,9 +553,13 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
static int static int
sclp_vt220_open(struct tty_struct *tty, struct file *filp) sclp_vt220_open(struct tty_struct *tty, struct file *filp)
{ {
sclp_vt220_tty = tty; if (tty->count == 1) {
tty->driver_data = NULL; sclp_vt220_tty = tty;
tty->low_latency = 0; tty->driver_data = kmalloc(SCLP_VT220_BUF_SIZE, GFP_KERNEL);
if (tty->driver_data == NULL)
return -ENOMEM;
tty->low_latency = 0;
}
return 0; return 0;
} }
...@@ -553,9 +569,11 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp) ...@@ -553,9 +569,11 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp)
static void static void
sclp_vt220_close(struct tty_struct *tty, struct file *filp) sclp_vt220_close(struct tty_struct *tty, struct file *filp)
{ {
if (tty->count > 1) if (tty->count == 1) {
return; sclp_vt220_tty = NULL;
sclp_vt220_tty = NULL; kfree(tty->driver_data);
tty->driver_data = NULL;
}
} }
/* /*
...@@ -571,7 +589,7 @@ sclp_vt220_close(struct tty_struct *tty, struct file *filp) ...@@ -571,7 +589,7 @@ sclp_vt220_close(struct tty_struct *tty, struct file *filp)
static void static void
sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch) sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
{ {
__sclp_vt220_write(0, &ch, 1, 0, 0); __sclp_vt220_write(&ch, 1, 0, 0);
} }
/* /*
...@@ -765,7 +783,7 @@ module_init(sclp_vt220_tty_init); ...@@ -765,7 +783,7 @@ module_init(sclp_vt220_tty_init);
static void static void
sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count) sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count)
{ {
__sclp_vt220_write(0, (const unsigned char *) buf, count, 1, 1); __sclp_vt220_write((const unsigned char *) buf, count, 1, 1);
} }
static struct tty_driver * static struct tty_driver *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment