// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Cambridge Greys Ltd
* Copyright (C) 2015-2016 Anton Ivanov (aivanov@brocade.com)
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
*/
/* 2001-09-28...2002-04-17
* Partition stuff by James_McMechan@hotmail.com
* old style ubd by setting UBD_SHIFT to 0
* 2002-09-27...2002-10-18 massive tinkering for 2.5
* partitions have changed in 2.5
* 2003-01-29 more tinkering for 2.5.59-1
* This should now address the sysfs problems and has
* the symlink for devfs to allow for booting with
* the common /dev/ubd/discX/... names rather than
* only /dev/ubdN/discN this version also has lots of
* clean ups preparing for ubd-many.
* James McMechan
*/
#define UBD_SHIFT 4
#include <linux/module.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/blk-mq.h>
#include <linux/ata.h>
#include <linux/hdreg.h>
#include <linux/major.h>
#include <linux/cdrom.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <kern_util.h>
#include "mconsole_kern.h"
#include <init.h>
#include <irq_kern.h>
#include "ubd.h"
#include <os.h>
#include "cow.h"
/* Max request size is determined by sector mask - 32K */
#define UBD_MAX_REQUEST (8 * sizeof(long))
struct io_desc {
char *buffer;
unsigned long length;
unsigned long sector_mask;
unsigned long long cow_offset;
unsigned long bitmap_words[2];
};
struct io_thread_req {
struct request *req;
int fds[2];
unsigned long offsets[2];
unsigned long long offset;
int sectorsize;
int error;
int desc_cnt;
/* io_desc has to be the last element of the struct */
struct io_desc io_desc[];
};
static struct io_thread_req * (*irq_req_buffer)[];
static struct io_thread_req *irq_remainder;
static int irq_remainder_size;
static struct io_thread_req * (*io_req_buffer)[];
static struct io_thread_req *io_remainder;
static int io_remainder_size;
static inline int ubd_test_bit(__u64 bit, unsigned char *data)
{
__u64 n;
int bits, off;
bits = sizeof(data[0]) * 8;
n = bit / bits;
off = bit % bits;
return (data[n] & (1 << off)) != 0;
}
static inline void ubd_set_bit(__u64 bit, unsigned char *data)
{
__u64 n;
int bits, off;
bits = sizeof(data[0]) * 8;
n = bit / bits;
off = bit % bits;
data[n] |= (1 << off);
}
/*End stuff from ubd_user.h*/
#define DRIVER_NAME "uml-blkdev"
static DEFINE_MUTEX(ubd_lock);
static int ubd_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg);
static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
#define MAX_DEV (16)
static const struct block_device_operations ubd_blops = {
.owner = THIS_MODULE,
.ioctl = ubd_ioctl,
.compat_ioctl = blkdev_compat_ptr_ioctl,
.getgeo = ubd_getgeo,
};
#ifdef CONFIG_BLK_DEV_UBD_SYNC
#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
.cl = 1 })
#else
#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
.cl = 1 })
#endif
static struct openflags global_openflags = OPEN_FLAGS;
struct cow {
/* backing file name */
char *file;
/* backing file fd */
int fd;
unsigned long *bitmap;
unsigned long bitmap_len;
int bitmap_offset;
int data_offset;
};
#define MAX_SG 64
struct ubd {
/* name (and fd, below) of the file opened for writing, either the
* backing or the cow file. */
char *file;
char *serial;
int fd;
__u64 size;
struct openflags boot_openflags;
struct openflags openflags;
unsigned shared:1;
unsigned no_cow:1;
unsigned no_trim:1;
struct cow cow;
struct platform_device pdev;
struct gendisk *disk;
struct blk_mq_tag_set tag_set;
spinlock_t lock;
};
#define DEFAULT_COW { \
.file = NULL, \
.fd = -1, \
.bitmap = NULL, \
.bitmap_offset = 0, \
.data_offset = 0, \
}
#define DEFAULT_UBD { \
.file = NULL, \
.serial = NULL, \
.fd = -1, \
.size = -1, \
.boot_openflags = OPEN_FLAGS, \
.openflags = OPEN_FLAGS, \
.no_cow = 0, \
.no_trim = 0, \
.shared = 0, \
.cow = DEFAULT_COW, \
.lock = __SPIN_LOCK_UNLOCKED(ubd_devs.lock), \
}
/* Protected by ubd_lock */
static struct ubd ubd_devs[MAX_DEV] = { [0 ... MAX_DEV - 1] = DEFAULT_UBD };
static blk_status_t ubd_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd);
static int fake_ide_setup(char *str)
{
pr_warn("The fake_ide option has been removed\n");
return 1;
}
__setup("fake_ide", fake_ide_setup);
__uml_help(fake_ide_setup,
"fake_ide\n"
" Obsolete stub.\n\n"
);
static int parse_unit(char **ptr)
{
char *str = *ptr, *end;
int n = -1;
if(isdigit(*str)) {
n = simple_strtoul(str, &end, 0);
if(end == str)
return -1;
*ptr = end;
}
else if (('a' <= *str) && (*str <= 'z')) {
n = *str - 'a';
str++;
*ptr = str;
}
return n;
}
/* If *index_out == -1 at exit, the passed option was a general one;
* 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, char **error_out)
{
struct ubd *ubd_dev;
struct openflags flags = global_openflags;
char *file, *backing_file, *serial;
int n, err = 0, i;
if(index_out) *index_out = -1;
n = *str;
if(n == '='){
str++;
if(!strcmp(str, "sync")){
global_openflags = of_sync(global_openflags);
return err;
}
pr_warn("fake major not supported any more\n");
return 0;
}
n = parse_unit(&str);
if(n < 0){
*error_out = "Couldn't parse device number";
return -EINVAL;
}
if(n >= MAX_DEV){
*error_out = "Device number out of range";
return 1;
}
err = -EBUSY;
mutex_lock(&ubd_lock);
ubd_dev = &ubd_devs[n];
if(ubd_dev->file != NULL){
*error_out = "Device is already configured";
goto out;
}
if (index_out)
*index_out = n;
err = -EINVAL;
for (i = 0; i < sizeof("rscdt="); i++) {
switch (*str) {
case 'r':
flags.w = 0;
break;
case 's':
flags.s = 1;
break;
case 'd':
ubd_dev->no_cow = 1;
break;
case 'c':
ubd_dev->shared = 1;
break;
case 't':
ubd_dev->no_trim = 1;
break;
case '=':
str++;
goto break_loop;
default:
*error_out = "Expected '=' or flag letter "
"(r, s, c, t or d)";
goto out;
}
str++;
}
if (*str == '=')
*error_out = "Too many flags specified";
else
*error_out = "Missing '='";
goto out;
break_loop:
file = strsep(&str, ",:");
if (*file == '\0')
file = NULL;
backing_file = strsep(&str, ",:");
if (backing_file && *backing_file == '\0')
backing_file = NULL;
serial = strsep(&str, ",:");
if (serial && *serial == '\0')
serial = NULL;
if (backing_file && ubd_dev->no_cow) {
*error_out = "Can't specify both 'd' and a cow file";
goto out;
}
err = 0;
ubd_dev->file = file;
ubd_dev->cow.file = backing_file;
ubd_dev->serial = serial;
ubd_dev->boot_openflags = flags;
out:
mutex_unlock(&ubd_lock);
return err;
}
static int ubd_setup(char *str)
{
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;
}
__s
|