#include "line.h"
 #include "os.h"
 
-/* XXX: could well be moved to somewhere else, if needed. */
-static int my_printf(const char * fmt, ...)
-       __attribute__ ((format (printf, 1, 2)));
-
-static int my_printf(const char * fmt, ...)
-{
-       /* Yes, can be called on atomic context.*/
-       char *buf = kmalloc(4096, GFP_ATOMIC);
-       va_list args;
-       int r;
-
-       if (!buf) {
-               /* We print directly fmt.
-                * Yes, yes, yes, feel free to complain. */
-               r = strlen(fmt);
-       } else {
-               va_start(args, fmt);
-               r = vsprintf(buf, fmt, args);
-               va_end(args);
-               fmt = buf;
-       }
-
-       if (r)
-               r = os_write_file(1, fmt, r);
-       return r;
-
-}
-
 #ifdef CONFIG_NOCONFIG_CHAN
-/* Despite its name, there's no added trailing newline. */
-static int my_puts(const char * buf)
+static void *not_configged_init(char *str, int device,
+                               const struct chan_opts *opts)
 {
-       return os_write_file(1, buf, strlen(buf));
-}
-
-static void *not_configged_init(char *str, int device, struct chan_opts *opts)
-{
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
        return NULL;
 }
 static int not_configged_open(int input, int output, int primary, void *data,
                              char **dev_out)
 {
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
        return -ENODEV;
 }
 
 static void not_configged_close(int fd, void *data)
 {
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
 }
 
 static int not_configged_read(int fd, char *c_out, void *data)
 {
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
        return -EIO;
 }
 
 static int not_configged_write(int fd, const char *buf, int len, void *data)
 {
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
        return -EIO;
 }
 
 static int not_configged_console_write(int fd, const char *buf, int len)
 {
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
        return -EIO;
 }
 static int not_configged_window_size(int fd, void *data, unsigned short *rows,
                                     unsigned short *cols)
 {
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
        return -ENODEV;
 }
 
 static void not_configged_free(void *data)
 {
-       my_puts("Using a channel type which is configured out of "
+       printk("Using a channel type which is configured out of "
               "UML\n");
 }
 
 };
 
 static struct chan *parse_chan(struct line *line, char *str, int device,
-                              const struct chan_opts *opts)
+                              const struct chan_opts *opts, char **error_out)
 {
        const struct chan_type *entry;
        const struct chan_ops *ops;
                }
        }
        if(ops == NULL){
-               my_printf("parse_chan couldn't parse \"%s\"\n",
-                      str);
+               *error_out = "No match for configured backends";
                return NULL;
        }
-       if(ops->init == NULL)
-               return NULL;
+
        data = (*ops->init)(str, device, opts);
-       if(data == NULL)
+       if(data == NULL){
+               *error_out = "Configuration failed";
                return NULL;
+       }
 
        chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
-       if(chan == NULL)
+       if(chan == NULL){
+               *error_out = "Memory allocation failed";
                return NULL;
+       }
        *chan = ((struct chan) { .list          = LIST_HEAD_INIT(chan->list),
                                 .free_list     =
                                        LIST_HEAD_INIT(chan->free_list),
 }
 
 int parse_chan_pair(char *str, struct line *line, int device,
-                   const struct chan_opts *opts)
+                   const struct chan_opts *opts, char **error_out)
 {
        struct list_head *chans = &line->chan_list;
        struct chan *new, *chan;
                in = str;
                *out = '\0';
                out++;
-               new = parse_chan(line, in, device, opts);
+               new = parse_chan(line, in, device, opts, error_out);
                if(new == NULL)
                        return -1;
 
                new->input = 1;
                list_add(&new->list, chans);
 
-               new = parse_chan(line, out, device, opts);
+               new = parse_chan(line, out, device, opts, error_out);
                if(new == NULL)
                        return -1;
 
                new->output = 1;
        }
        else {
-               new = parse_chan(line, str, device, opts);
+               new = parse_chan(line, str, device, opts, error_out);
                if(new == NULL)
                        return -1;
 
 
                close_chan(&lines[i].chan_list, 0);
 }
 
-static void setup_one_line(struct line *lines, int n, char *init, int init_prio)
+static int setup_one_line(struct line *lines, int n, char *init, int init_prio,
+                         char **error_out)
 {
        struct line *line = &lines[n];
+       int err = -EINVAL;
 
        spin_lock(&line->count_lock);
 
        if(line->tty != NULL){
-               printk("line_setup - device %d is open\n", n);
+               *error_out = "Device is already open";
                goto out;
        }
 
                        line->valid = 1;
                }
        }
+       err = 0;
 out:
        spin_unlock(&line->count_lock);
+       return err;
 }
 
 /* Common setup code for both startup command line and mconsole initialization.
  * @lines contains the array (of size @num) to modify;
  * @init is the setup string;
+ * @error_out is an error string in the case of failure;
  */
 
-int line_setup(struct line *lines, unsigned int num, char *init)
+int line_setup(struct line *lines, unsigned int num, char *init,
+              char **error_out)
 {
-       int i, n;
+       int i, n, err;
        char *end;
 
        if(*init == '=') {
        else {
                n = simple_strtoul(init, &end, 0);
                if(*end != '='){
-                       printk(KERN_ERR "line_setup failed to parse \"%s\"\n",
-                              init);
-                       return 0;
+                       *error_out = "Couldn't parse device number";
+                       return -EINVAL;
                }
                init = end;
        }
        init++;
 
        if (n >= (signed int) num) {
-               printk("line_setup - %d out of range ((0 ... %d) allowed)\n",
-                      n, num - 1);
-               return 0;
+               *error_out = "Device number out of range";
+               return -EINVAL;
+       }
+       else if (n >= 0){
+               err = setup_one_line(lines, n, init, INIT_ONE, error_out);
+               if(err)
+                       return err;
        }
-       else if (n >= 0)
-               setup_one_line(lines, n, init, INIT_ONE);
        else {
-               for(i = 0; i < num; i++)
-                       setup_one_line(lines, i, init, INIT_ALL);
+               for(i = 0; i < num; i++){
+                       err = setup_one_line(lines, i, init, INIT_ALL,
+                                            error_out);
+                       if(err)
+                               return err;
+               }
        }
        return n == -1 ? num : n;
 }
 
 int line_config(struct line *lines, unsigned int num, char *str,
-               const struct chan_opts *opts)
+               const struct chan_opts *opts, char **error_out)
 {
        struct line *line;
        char *new;
        int n;
 
        if(*str == '='){
-               printk("line_config - can't configure all devices from "
-                      "mconsole\n");
-               return 1;
+               *error_out = "Can't configure all devices from mconsole";
+               return -EINVAL;
        }
 
        new = kstrdup(str, GFP_KERNEL);
        if(new == NULL){
-               printk("line_config - kstrdup failed\n");
-               return 1;
+               *error_out = "Failed to allocate memory";
+               return -ENOMEM;
        }
-       n = line_setup(lines, num, new);
+       n = line_setup(lines, num, new, error_out);
        if(n < 0)
-               return 1;
+               return n;
 
        line = &lines[n];
-       return parse_chan_pair(line->init_str, line, n, opts);
+       return parse_chan_pair(line->init_str, line, n, opts, error_out);
 }
 
 int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
         return n;
 }
 
-int line_remove(struct line *lines, unsigned int num, int n)
+int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
 {
        int err;
        char config[sizeof("conxxxx=none\0")];
 
        sprintf(config, "%d=none", n);
-       err = line_setup(lines, num, config);
+       err = line_setup(lines, num, config, error_out);
        if(err >= 0)
                err = 0;
        return err;
 void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
 {
        struct line *line;
+       char *error;
        int i;
 
        for(i = 0; i < nlines; i++){
                if(line->init_str == NULL)
                        printk("lines_init - kstrdup returned NULL\n");
 
-               if(parse_chan_pair(line->init_str, line, i, opts)){
-                       printk("parse_chan_pair failed for device %d\n", i);
+               if(parse_chan_pair(line->init_str, line, i, opts, &error)){
+                       printk("parse_chan_pair failed for device %d : %s\n",
+                              i, error);
                        line->valid = 0;
                }
        }
 
 static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
 static int unplug_index = UNPLUGGED_PER_PAGE;
 
-static int mem_config(char *str)
+static int mem_config(char *str, char **error_out)
 {
        unsigned long long diff;
        int err = -EINVAL, i, add;
        char *ret;
 
-       if(str[0] != '=')
+       if(str[0] != '='){
+               *error_out = "Expected '=' after 'mem'";
                goto out;
+       }
 
        str++;
        if(str[0] == '-')
        else if(str[0] == '+'){
                add = 1;
        }
-       else goto out;
+       else {
+               *error_out = "Expected increment to start with '-' or '+'";
+               goto out;
+       }
 
        str++;
        diff = memparse(str, &ret);
-       if(*ret != '\0')
+       if(*ret != '\0'){
+               *error_out = "Failed to parse memory increment";
                goto out;
+       }
 
        diff /= PAGE_SIZE;
 
                                unplugged = list_entry(entry,
                                                       struct unplugged_pages,
                                                       list);
-                               unplugged->pages[unplug_index++] = addr;
                                err = os_drop_memory(addr, PAGE_SIZE);
-                               if(err)
+                               if(err){
                                        printk("Failed to release memory - "
                                               "errno = %d\n", err);
+                                       *error_out = "Failed to release memory";
+                                       goto out;
+                               }
+                               unplugged->pages[unplug_index++] = addr;
                        }
 
                        unplugged_pages_count++;
        return 0;
 }
 
-static int mem_remove(int n)
+static int mem_remove(int n, char **error_out)
 {
+       *error_out = "Memory doesn't support the remove operation";
        return -EBUSY;
 }
 
 void mconsole_config(struct mc_request *req)
 {
        struct mc_device *dev;
-       char *ptr = req->request.data, *name;
+       char *ptr = req->request.data, *name, *error_string = "";
        int err;
 
        ptr += strlen("config");
                ptr++;
 
        if(*ptr == '='){
-               err = (*dev->config)(name);
-               mconsole_reply(req, "", err, 0);
+               err = (*dev->config)(name, &error_string);
+               mconsole_reply(req, error_string, err, 0);
        }
        else mconsole_get_config(dev->get_config, req, name);
 }
                goto out;
        }
 
-       err = (*dev->remove)(n);
+       err_msg = NULL;
+       err = (*dev->remove)(n, &err_msg);
        switch(err){
        case -ENODEV:
-               err_msg = "Device doesn't exist";
+               if(err_msg == NULL)
+                       err_msg = "Device doesn't exist";
                break;
        case -EBUSY:
-               err_msg = "Device is currently open";
+               if(err_msg == NULL)
+                       err_msg = "Device is currently open";
                break;
        default:
                break;
 
 /*
- * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and 
+ * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
  * James Leu (jleu@mindspring.net).
  * Copyright (C) 2001 by various other people who didn't put their name here.
  * Licensed under the GPL.
        spin_lock(&lp->lock);
        while((err = uml_net_rx(dev)) > 0) ;
        if(err < 0) {
-               printk(KERN_ERR 
-                      "Device '%s' read returned %d, shutting it down\n", 
+               printk(KERN_ERR
+                      "Device '%s' read returned %d, shutting it down\n",
                       dev->name, err);
                /* dev_close can't be called in interrupt context, and takes
                 * again lp->lock.
 static int uml_net_close(struct net_device *dev)
 {
        struct uml_net_private *lp = dev->priv;
-       
+
        netif_stop_queue(dev);
 
        free_irq(dev->irq, dev);
 
                /* this is normally done in the interrupt when tx finishes */
                netif_wake_queue(dev);
-       } 
+       }
        else if(len == 0){
                netif_start_queue(dev);
                lp->stats.tx_dropped++;
        struct uml_net_private *lp;
        int save, err, size;
 
-       size = transport->private_size + sizeof(struct uml_net_private) + 
+       size = transport->private_size + sizeof(struct uml_net_private) +
                sizeof(((struct uml_net_private *) 0)->user);
 
        device = kzalloc(sizeof(*device), GFP_KERNEL);
        lp->tl.function = uml_net_user_timer_expire;
        memcpy(lp->mac, device->mac, sizeof(lp->mac));
 
-       if (transport->user->init) 
+       if (transport->user->init)
                (*transport->user->init)(&lp->user, dev);
 
        set_ether_mac(dev, device->mac);
        return(device);
 }
 
-static int eth_parse(char *str, int *index_out, char **str_out)
+static int eth_parse(char *str, int *index_out, char **str_out,
+                    char **error_out)
 {
        char *end;
-       int n;
+       int n, err = -EINVAL;;
 
        n = simple_strtoul(str, &end, 0);
        if(end == str){
-               printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str);
-               return(1);
-       }
-       if(n < 0){
-               printk(KERN_ERR "eth_setup: device %d is negative\n", n);
-               return(1);
+               *error_out = "Bad device number";
+               return err;
        }
+
        str = end;
        if(*str != '='){
-               printk(KERN_ERR 
-                      "eth_setup: expected '=' after device number\n");
-               return(1);
+               *error_out = "Expected '=' after device number";
+               return err;
        }
+
        str++;
        if(find_device(n)){
-               printk(KERN_ERR "eth_setup: Device %d already configured\n",
-                      n);
-               return(1);
+               *error_out = "Device already configured";
+               return err;
        }
-       if(index_out) *index_out = n;
+
+       *index_out = n;
        *str_out = str;
-       return(0);
+       return 0;
 }
 
 struct eth_init {
 static int eth_setup(char *str)
 {
        struct eth_init *new;
+       char *error;
        int n, err;
 
-       err = eth_parse(str, &n, &str);
-       if(err)
+       err = eth_parse(str, &n, &str, &error);
+       if(err){
+               printk(KERN_ERR "eth_setup - Couldn't parse '%s' : %s\n",
+                      str, error);
                return 1;
+       }
 
        new = alloc_bootmem(sizeof(*new));
        if (new == NULL){
                if(eth_setup_common(eth->init, eth->index))
                        list_del(ð->list);
        }
-       
+
        return(1);
 }
 __initcall(eth_init);
 #endif
 
-static int net_config(char *str)
+static int net_config(char *str, char **error_out)
 {
        int n, err;
 
-       err = eth_parse(str, &n, &str);
-       if(err) return(err);
+       err = eth_parse(str, &n, &str, error_out);
+       if(err)
+               return err;
 
+       /* This string is broken up and the pieces used by the underlying
+        * driver.  So, it is freed only if eth_setup_common fails.
+        */
        str = kstrdup(str, GFP_KERNEL);
        if(str == NULL){
-               printk(KERN_ERR "net_config failed to strdup string\n");
-               return(-1);
+               *error_out = "net_config failed to strdup string";
+               return -ENOMEM;
        }
        err = !eth_setup_common(str, n);
-       if(err) 
+       if(err)
                kfree(str);
        return(err);
 }
         return n;
 }
 
-static int net_remove(int n)
+static int net_remove(int n, char **error_out)
 {
        struct uml_net *device;
        struct net_device *dev;
 static int uml_net_init(void)
 {
        struct list_head *ele;
-       struct uml_net_private *lp;     
+       struct uml_net_private *lp;
        struct in_device *ip;
        struct in_ifaddr *in;
 
                        uml_inetaddr_event(NULL, NETDEV_UP, in);
                        in = in->ifa_next;
                }
-       }       
+       }
 
        return(0);
 }
        return(skb);
 }
 
-void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, 
-                                       void *), 
+void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *,
+                                       void *),
                    void *arg)
 {
        struct net_device *dev = d;
        struct in_ifaddr *in;
        __be32 *mask_out = m;
 
-       if(ip == NULL) 
+       if(ip == NULL)
                return(1);
 
        in = ip->ifa_list;
-       if(in == NULL) 
+       if(in == NULL)
                return(1);
 
        *mask_out = in->ifa_mask;
        free_pages((unsigned long) buffer, 0);
 }
 
-int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out, 
+int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out,
                     char **gate_addr)
 {
        char *remain;
 {
        return(eth_type_trans(skb, skb->dev));
 }
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
 
        .in_kernel      = 1,
 };
 
-static int ssl_config(char *str);
+static int ssl_config(char *str, char **error_out);
 static int ssl_get_config(char *dev, char *str, int size, char **error_out);
-static int ssl_remove(int n);
+static int ssl_remove(int n, char **error_out);
 
 static struct line_driver driver = {
        .name                   = "UML serial line",
 
 static struct lines lines = LINES_INIT(NR_PORTS);
 
-static int ssl_config(char *str)
+static int ssl_config(char *str, char **error_out)
 {
-       return line_config(serial_lines, ARRAY_SIZE(serial_lines), str, &opts);
+       return line_config(serial_lines, ARRAY_SIZE(serial_lines), str, &opts,
+                          error_out);
 }
 
 static int ssl_get_config(char *dev, char *str, int size, char **error_out)
                               size, error_out);
 }
 
-static int ssl_remove(int n)
+static int ssl_remove(int n, char **error_out)
 {
-       return line_remove(serial_lines, ARRAY_SIZE(serial_lines), n);
+       return line_remove(serial_lines, ARRAY_SIZE(serial_lines), n,
+                          error_out);
 }
 
 static int ssl_open(struct tty_struct *tty, struct file *filp)
 
 static int ssl_chan_setup(char *str)
 {
-       return line_setup(serial_lines, ARRAY_SIZE(serial_lines), str);
+       char *error;
+       int ret;
+
+       ret = line_setup(serial_lines, ARRAY_SIZE(serial_lines), str, &error);
+       if(ret < 0)
+               printk(KERN_ERR "Failed to set up serial line with "
+                      "configuration string \"%s\" : %s\n", str, error);
+
+       return 1;
 }
 
 __setup("ssl", ssl_chan_setup);
 
        .in_kernel      = 1,
 };
 
-static int con_config(char *str);
+static int con_config(char *str, char **error_out);
 static int con_get_config(char *dev, char *str, int size, char **error_out);
-static int con_remove(int n);
+static int con_remove(int n, char **con_remove);
 
 static struct line_driver driver = {
        .name                   = "UML console",
                                     [ 1 ... MAX_TTYS - 1 ] =
                                     LINE_INIT(CONFIG_CON_CHAN, &driver) };
 
-static int con_config(char *str)
+static int con_config(char *str, char **error_out)
 {
-       return line_config(vts, ARRAY_SIZE(vts), str, &opts);
+       return line_config(vts, ARRAY_SIZE(vts), str, &opts, error_out);
 }
 
 static int con_get_config(char *dev, char *str, int size, char **error_out)
        return line_get_config(dev, vts, ARRAY_SIZE(vts), str, size, error_out);
 }
 
-static int con_remove(int n)
+static int con_remove(int n, char **error_out)
 {
-       return line_remove(vts, ARRAY_SIZE(vts), n);
+       return line_remove(vts, ARRAY_SIZE(vts), n, error_out);
 }
 
 static int con_open(struct tty_struct *tty, struct file *filp)
 
 static int console_chan_setup(char *str)
 {
-       return line_setup(vts, ARRAY_SIZE(vts), str);
+       char *error;
+       int ret;
+
+       ret = line_setup(vts, ARRAY_SIZE(vts), str, &error);
+       if(ret < 0)
+               printk(KERN_ERR "Failed to set up console with "
+                      "configuration string \"%s\" : %s\n", str, error);
+
+       return 1;
 }
 __setup("con", console_chan_setup);
 __channel_help(console_chan_setup, "con");
 
  * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
  * should not be freed on exit.
  */
-static int ubd_setup_common(char *str, int *index_out)
+static int ubd_setup_common(char *str, int *index_out, char **error_out)
 {
        struct ubd *ubd_dev;
        struct openflags flags = global_openflags;
                str++;
                if(!strcmp(str, "sync")){
                        global_openflags = of_sync(global_openflags);
-                       return(0);
+                       return 0;
                }
                major = simple_strtoul(str, &end, 0);
                if((*end != '\0') || (end == str)){
-                       printk(KERN_ERR
-                              "ubd_setup : didn't parse major number\n");
-                       return(1);
+                       *error_out = "Didn't parse major number";
+                       return -EINVAL;
                }
 
-               err = 1;
-               mutex_lock(&ubd_lock);
-               if(fake_major != MAJOR_NR){
-                       printk(KERN_ERR "Can't assign a fake major twice\n");
-                       goto out1;
-               }
+               err = -EINVAL;
+               mutex_lock(&ubd_lock);
+               if(fake_major != MAJOR_NR){
+                       *error_out = "Can't assign a fake major twice";
+                       goto out1;
+               }
 
-               fake_major = major;
+               fake_major = major;
 
                printk(KERN_INFO "Setting extra ubd major number to %d\n",
                       major);
-               err = 0;
-       out1:
-               mutex_unlock(&ubd_lock);
-               return(err);
+               err = 0;
+       out1:
+               mutex_unlock(&ubd_lock);
+               return err;
        }
 
        n = parse_unit(&str);
        if(n < 0){
-               printk(KERN_ERR "ubd_setup : couldn't parse unit number "
-                      "'%s'\n", str);
-               return(1);
+               *error_out = "Couldn't parse device number";
+               return -EINVAL;
        }
        if(n >= MAX_DEV){
-               printk(KERN_ERR "ubd_setup : index %d out of range "
-                      "(%d devices, from 0 to %d)\n", n, MAX_DEV, MAX_DEV - 1);
-               return(1);
+               *error_out = "Device number out of range";
+               return 1;
        }
 
-       err = 1;
+       err = -EBUSY;
        mutex_lock(&ubd_lock);
 
        ubd_dev = &ubd_devs[n];
        if(ubd_dev->file != NULL){
-               printk(KERN_ERR "ubd_setup : device already configured\n");
+               *error_out = "Device is already configured";
                goto out;
        }
 
        if (index_out)
                *index_out = n;
 
+       err = -EINVAL;
        for (i = 0; i < sizeof("rscd="); i++) {
                switch (*str) {
                case 'r':
                        str++;
                        goto break_loop;
                default:
-                       printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r, s, c, or d)\n");
+                       *error_out = "Expected '=' or flag letter "
+                               "(r, s, c, or d)";
                        goto out;
                }
                str++;
        }
 
-        if (*str == '=')
-               printk(KERN_ERR "ubd_setup : Too many flags specified\n");
-        else
-               printk(KERN_ERR "ubd_setup : Expected '='\n");
+       if (*str == '=')
+               *error_out = "Too many flags specified";
+       else
+               *error_out = "Missing '='";
        goto out;
 
 break_loop:
-       err = 0;
        backing_file = strchr(str, ',');
 
-       if (!backing_file) {
+       if (backing_file == NULL)
                backing_file = strchr(str, ':');
-       }
 
-       if(backing_file){
-               if(ubd_dev->no_cow)
-                       printk(KERN_ERR "Can't specify both 'd' and a "
-                              "cow file\n");
+       if(backing_file != NULL){
+               if(ubd_dev->no_cow){
+                       *error_out = "Can't specify both 'd' and a cow file";
+                       goto out;
+               }
                else {
                        *backing_file = '\0';
                        backing_file++;
                }
        }
+       err = 0;
        ubd_dev->file = str;
        ubd_dev->cow.file = backing_file;
        ubd_dev->boot_openflags = flags;
 out:
        mutex_unlock(&ubd_lock);
-       return(err);
+       return err;
 }
 
 static int ubd_setup(char *str)
 {
-       ubd_setup_common(str, NULL);
-       return(1);
+       char *error;
+       int err;
+
+       err = ubd_setup_common(str, NULL, &error);
+       if(err)
+               printk(KERN_ERR "Failed to initialize device with \"%s\" : "
+                      "%s\n", str, error);
+       return 1;
 }
 
 __setup("ubd", ubd_setup);
 "    use either a ':' or a ',': the first one allows writing things like;\n"
 "      ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
 "    while with a ',' the shell would not expand the 2nd '~'.\n"
-"    When using only one filename, UML will detect whether to thread it like\n"
+"    When using only one filename, UML will detect whether to treat it like\n"
 "    a COW file or a backing file. To override this detection, add the 'd'\n"
 "    flag:\n"
 "      ubd0d=BackingFile\n"
 
 #define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
 
-static int ubd_add(int n)
+static int ubd_add(int n, char **error_out)
 {
        struct ubd *ubd_dev = &ubd_devs[n];
-       int err;
+       int err = 0;
 
-       err = -ENODEV;
        if(ubd_dev->file == NULL)
                goto out;
 
        err = ubd_file_size(ubd_dev, &ubd_dev->size);
-       if(err < 0)
+       if(err < 0){
+               *error_out = "Couldn't determine size of device's file";
                goto out;
+       }
 
        ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
 
        return err;
 }
 
-static int ubd_config(char *str)
+static int ubd_config(char *str, char **error_out)
 {
        int n, ret;
 
+       /* This string is possibly broken up and stored, so it's only
+        * freed if ubd_setup_common fails, or if only general options
+        * were set.
+        */
        str = kstrdup(str, GFP_KERNEL);
        if (str == NULL) {
-               printk(KERN_ERR "ubd_config failed to strdup string\n");
-               ret = 1;
-               goto out;
+               *error_out = "Failed to allocate memory";
+               return -ENOMEM;
        }
-       ret = ubd_setup_common(str, &n);
-       if (ret) {
-               ret = -1;
+
+       ret = ubd_setup_common(str, &n, error_out);
+       if (ret)
                goto err_free;
-       }
+
        if (n == -1) {
                ret = 0;
                goto err_free;
        }
 
        mutex_lock(&ubd_lock);
-       ret = ubd_add(n);
+       ret = ubd_add(n, error_out);
        if (ret)
                ubd_devs[n].file = NULL;
        mutex_unlock(&ubd_lock);
         return n;
 }
 
-static int ubd_remove(int n)
+static int ubd_remove(int n, char **error_out)
 {
        struct ubd *ubd_dev;
        int err = -ENODEV;
        return err;
 }
 
-/* All these are called by mconsole in process context and without ubd-specific locks. */
+/* All these are called by mconsole in process context and without
+ * ubd-specific locks.
+ */
 static struct mc_device ubd_mc = {
        .name           = "ubd",
        .config         = ubd_config,
 
 static int __init ubd_init(void)
 {
-        int i;
+       char *error;
+       int i, err;
 
        if (register_blkdev(MAJOR_NR, "ubd"))
                return -1;
                        return -1;
        }
        platform_driver_register(&ubd_driver);
-       for (i = 0; i < MAX_DEV; i++)
-               ubd_add(i);
+       for (i = 0; i < MAX_DEV; i++){
+               err = ubd_add(i, &error);
+               if(err)
+                       printk(KERN_ERR "Failed to initialize ubd device %d :"
+                              "%s\n", i, error);
+       }
        return 0;
 }
 
 
 extern void chan_interrupt(struct list_head *chans, struct delayed_work *task,
                           struct tty_struct *tty, int irq);
 extern int parse_chan_pair(char *str, struct line *line, int device,
-                          const struct chan_opts *opts);
+                          const struct chan_opts *opts, char **error_out);
 extern int open_chan(struct list_head *chans);
 extern int write_chan(struct list_head *chans, const char *buf, int len,
                             int write_irq);
 
 extern void line_close(struct tty_struct *tty, struct file * filp);
 extern int line_open(struct line *lines, struct tty_struct *tty);
 extern int line_setup(struct line *lines, unsigned int sizeof_lines,
-                     char *init);
+                     char *init, char **error_out);
 extern int line_write(struct tty_struct *tty, const unsigned char *buf,
                      int len);
 extern void line_put_char(struct tty_struct *tty, unsigned char ch);
 extern void close_lines(struct line *lines, int nlines);
 
 extern int line_config(struct line *lines, unsigned int sizeof_lines,
-                      char *str, const struct chan_opts *opts);
+                      char *str, const struct chan_opts *opts,
+                      char **error_out);
 extern int line_id(char **str, int *start_out, int *end_out);
-extern int line_remove(struct line *lines, unsigned int sizeof_lines, int n);
+extern int line_remove(struct line *lines, unsigned int sizeof_lines, int n,
+                      char **error_out);
 extern int line_get_config(char *dev, struct line *lines,
                           unsigned int sizeof_lines, char *str,
                           int size, char **error_out);
 
 struct mc_device {
        struct list_head list;
        char *name;
-       int (*config)(char *);
+       int (*config)(char *, char **);
        int (*get_config)(char *, char *, int, char **);
         int (*id)(char **, int *, int *);
-       int (*remove)(int);
+       int (*remove)(int, char **);
 };
 
 #define CONFIG_CHUNK(str, size, current, chunk, end) \
 #endif
 
 #endif
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
 
        init_proxy(debugger_pid, 0, 0);
 }
 
-int gdb_config(char *str)
+int gdb_config(char *str, char **error_out)
 {
        struct gdb_data data;
 
        exit_debugger_cb(NULL);
 }
 
-int gdb_remove(int unused)
+int gdb_remove(int unused, char **error_out)
 {
        initial_thread_cb(remove_gdb_cb, NULL);
         return 0;
 
 
 #ifdef CONFIG_MCONSOLE
 
-extern int gdb_config(char *str);
-extern int gdb_remove(int n);
+extern int gdb_config(char *str, char **error_out);
+extern int gdb_remove(int n, char **error_out);
 
 static struct mc_device gdb_mc = {
        .name           = "gdb",