Skip to content

Commit

Permalink
Add experimental support for SATA boot on system lacking native SATA …
Browse files Browse the repository at this point in the history
…DOM support

This method is rather hacky and may be unstable in the future.
There's no danger of using it, but it may simply not work on
boot. Despite all the safeguards in place it's recommended to
use USB boot when possible over the fake-SATA boot device.
Unlike native SATA DOM shimming this method is not officially
supported by the syno kernel and relies on clever assumptions.
  • Loading branch information
ttg-public committed Jan 1, 1970
1 parent 1a3039f commit de5099c
Show file tree
Hide file tree
Showing 9 changed files with 578 additions and 55 deletions.
135 changes: 135 additions & 0 deletions compat/toolkit/drivers/usb/storage/usb.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* Cherry-picked USB.h internal structures from Linux v4.4.x. If possible avoid using anything from this file like fire.
*
* ORIGINAL FILE HEADER PRESERVED BELOW
* ------------------------------------
* Driver for USB Mass Storage compliant devices
* Main Header File
*
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
* Initial work by:
* (c) 1999 Michael Gee (michael@linuxspecific.com)
*
* This driver is based on the 'USB Mass Storage Class' document. This
* describes in detail the protocol used to communicate with such
* devices. Clearly, the designers had SCSI and ATAPI commands in
* mind when they created this document. The commands are all very
* similar to commands in the SCSI-II and ATAPI specifications.
*
* It is important to note that in a number of cases this class
* exhibits class-specific exemptions from the USB specification.
* Notably the usage of NAK, STALL and ACK differs from the norm, in
* that they are used to communicate wait, failed and OK on commands.
*
* Also, for certain devices, the interrupt endpoint is used to convey
* status of a command.
*
* Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
* information about this driver.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef REDPILL_USB_H
#define REDPILL_USB_H

#warning "Using compatibility file for drivers/usb/storage/usb.h - if possible do NOT compile using toolkit"

//This structure didn't change substantially since v2.6 days; 5.14 is simply the newest one we checked - it will
// probably remain unchanged for years to come
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,14,0) //v3.3 - v5.14
#include <linux/usb.h> //struct usb_sg_request

struct us_data;
typedef int (*trans_cmnd)(struct scsi_cmnd *, struct us_data*);
typedef int (*trans_reset)(struct us_data*);
typedef void (*proto_cmnd)(struct scsi_cmnd*, struct us_data*);
typedef void (*extra_data_destructor)(void *); /* extra data destructor */
typedef void (*pm_hook)(struct us_data *, int); /* power management hook */

struct us_data {
/* The device we're working with
* It's important to note:
* (o) you must hold dev_mutex to change pusb_dev
*/
struct mutex dev_mutex; /* protect pusb_dev */
struct usb_device *pusb_dev; /* this usb_device */
struct usb_interface *pusb_intf; /* this interface */
struct us_unusual_dev *unusual_dev; /* device-filter entry */
unsigned long fflags; /* fixed flags from filter */
unsigned long dflags; /* dynamic atomic bitflags */
unsigned int send_bulk_pipe; /* cached pipe values */
unsigned int recv_bulk_pipe;
unsigned int send_ctrl_pipe;
unsigned int recv_ctrl_pipe;
unsigned int recv_intr_pipe;

/* information about the device */
char *transport_name;
char *protocol_name;
__le32 bcs_signature;
u8 subclass;
u8 protocol;
u8 max_lun;

u8 ifnum; /* interface number */
u8 ep_bInterval; /* interrupt interval */

/* function pointers for this device */
trans_cmnd transport; /* transport function */
trans_reset transport_reset; /* transport device reset */
proto_cmnd proto_handler; /* protocol handler */

/* SCSI interfaces */
struct scsi_cmnd *srb; /* current srb */
unsigned int tag; /* current dCBWTag */
char scsi_name[32]; /* scsi_host name */

/* control and bulk communications data */
struct urb *current_urb; /* USB requests */
struct usb_ctrlrequest *cr; /* control requests */
struct usb_sg_request current_sg; /* scatter-gather req. */
unsigned char *iobuf; /* I/O buffer */
dma_addr_t iobuf_dma; /* buffer DMA addresses */
struct task_struct *ctl_thread; /* the control thread */

/* mutual exclusion and synchronization structures */
struct completion cmnd_ready; /* to sleep thread on */
struct completion notify; /* thread begin/end */
wait_queue_head_t delay_wait; /* wait during reset */
struct delayed_work scan_dwork; /* for async scanning */

/* subdriver information */
void *extra; /* Any extra data */
extra_data_destructor extra_destructor;/* extra data destructor */
#ifdef CONFIG_PM
pm_hook suspend_resume_hook;
#endif

/* hacks for READ CAPACITY bug handling */
int use_last_sector_hacks;
int last_sector_retries;
};
#endif //LINUX_VERSION_CODE check


struct Scsi_Host;
static inline struct us_data *host_to_us(struct Scsi_Host *host) {
return (struct us_data *) host->hostdata;
}

#endif //REDPILL_USB_H
58 changes: 37 additions & 21 deletions config/cmdline_delegate.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,26 @@ static bool extract_boot_media_type(struct boot_media *boot_media, const char *p
ensure_cmdline_param(CMDLINE_KT_SATADOM);

char value = param_pointer[strlen_static(CMDLINE_KT_SATADOM)];
if (likely(value == '1')) {
boot_media->type = BOOT_MEDIA_SATA;
pr_loc_dbg("Boot media SATADOM requested");
return true;
}

if(value == '0') {
//There's no point to set that option but it's not an error
pr_loc_wrn("Boot media SATADOM disabled (default will be used, %s0 is a noop)", CMDLINE_KT_SATADOM);
return true; //setting it to 0 doesn't really make any sense but it's not an error
}
switch (value) {
case CMDLINE_KT_SATADOM_NATIVE:
boot_media->type = BOOT_MEDIA_SATA_DOM;
pr_loc_dbg("Boot media SATADOM (native) requested");
break;

case CMDLINE_KT_SATADOM_FAKE:
boot_media->type = BOOT_MEDIA_SATA_DISK;
pr_loc_dbg("Boot media SATADISK (fake) requested");
break;

case CMDLINE_KT_SATADOM_DISABLED:
//There's no point to set that option but it's not an error
pr_loc_wrn("SATA-based boot media disabled (default will be used, %s0 is a noop)", CMDLINE_KT_SATADOM);
break;

pr_loc_err("Option \"%s%c\" is invalid (value should be 0 or 1)", CMDLINE_KT_SATADOM, value);
default:
pr_loc_err("Option \"%s%c\" is invalid (value should be 0/1/2)", CMDLINE_KT_SATADOM, value);
}

return true;
}
Expand Down Expand Up @@ -353,23 +360,30 @@ int populate_cmdline_blacklist(cmdline_token *cmdline_blacklist[MAX_BLACKLISTED_
ADD_BLACKLIST_ENTRY(0, CMDLINE_CT_VID);
ADD_BLACKLIST_ENTRY(1, CMDLINE_CT_PID);
ADD_BLACKLIST_ENTRY(2, CMDLINE_CT_MFG);
ADD_BLACKLIST_ENTRY(3, CMDLINE_KT_ELEVATOR);
ADD_BLACKLIST_ENTRY(4, CMDLINE_KT_LOGLEVEL);
ADD_BLACKLIST_ENTRY(5, CMDLINE_KT_PK_BUFFER);
ADD_BLACKLIST_ENTRY(6, CMDLINE_KT_EARLY_PK);
ADD_BLACKLIST_ENTRY(7, CMDLINE_KT_THAW);
ADD_BLACKLIST_ENTRY(3, CMDLINE_CT_DOM_SZMAX);
ADD_BLACKLIST_ENTRY(4, CMDLINE_KT_ELEVATOR);
ADD_BLACKLIST_ENTRY(5, CMDLINE_KT_LOGLEVEL);
ADD_BLACKLIST_ENTRY(6, CMDLINE_KT_PK_BUFFER);
ADD_BLACKLIST_ENTRY(7, CMDLINE_KT_EARLY_PK);
ADD_BLACKLIST_ENTRY(8, CMDLINE_KT_THAW);

#ifndef NATIVE_SATA_DOM_SUPPORTED //on kernels without SATA DOM support we shouldn't reveal that it's a SATA DOM-boot
ADD_BLACKLIST_ENTRY(9, CMDLINE_KT_SATADOM);
#endif

return 0;
}

int extract_config_from_cmdline(struct runtime_config *config)
{
int out = 0;
char *cmdline_txt;
kzalloc_or_exit_int(cmdline_txt, strlen_to_size(CMDLINE_MAX));

if(get_kernel_cmdline(cmdline_txt, CMDLINE_MAX) <= 0) {
pr_loc_crt("Failed to extract cmdline");
return -EIO;
out = -EIO;
goto exit_free;
}

pr_loc_dbg("Cmdline: %s", cmdline_txt);
Expand Down Expand Up @@ -400,12 +414,14 @@ int extract_config_from_cmdline(struct runtime_config *config)
report_unrecognized_option(single_param_chunk) ;
}

if (populate_cmdline_blacklist(config->cmdline_blacklist, &config->hw) != 0)
return -EIO;
if (populate_cmdline_blacklist(config->cmdline_blacklist, &config->hw) != 0) {
out = -EIO;
goto exit_free;
}

pr_loc_inf("CmdLine processed successfully, tokens=%d", param_counter);

exit_free:
kfree(cmdline_txt);

return 0;
return out;
}
97 changes: 66 additions & 31 deletions config/runtime_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,49 +32,84 @@ static inline bool validate_sn(const serial_no *sn) {
return true;
}

static inline bool validate_boot_dev(const struct boot_media *boot)
static __always_inline bool validate_boot_dev_usb(const struct boot_media *boot)
{
if (likely(boot->type == BOOT_MEDIA_USB)) {
if (boot->vid == VID_PID_EMPTY && boot->pid == VID_PID_EMPTY) {
pr_loc_wrn("Empty/no \"%s\" and \"%s\" specified - first USB storage device will be used", CMDLINE_CT_VID,
CMDLINE_CT_PID);
return true; //this isn't necessarily an error (e.g. running under a VM with only a single USB port)
}

if (boot->vid == VID_PID_EMPTY) { //PID=0 is valid, but the VID is not
pr_loc_err("Empty/no \"%s\" specified", CMDLINE_CT_VID);
return false;
}
if (boot->vid == VID_PID_EMPTY && boot->pid == VID_PID_EMPTY) {
pr_loc_wrn("Empty/no \"%s\" and \"%s\" specified - first USB storage device will be used", CMDLINE_CT_VID,
CMDLINE_CT_PID);
return true; //this isn't necessarily an error (e.g. running under a VM with only a single USB port)
}

pr_loc_dbg("Configured boot device type to USB");
return true;
//not checking for >VID_PID_MAX as vid type is already ushort
if (boot->vid == VID_PID_EMPTY) { //PID=0 is valid, but the VID is not
pr_loc_err("Empty/no \"%s\" specified", CMDLINE_CT_VID);
return false;
}

if (boot->type == BOOT_MEDIA_SATA) {
pr_loc_dbg("Configured boot device type to USB");
return true;
//not checking for >VID_PID_MAX as vid type is already ushort
}

static __always_inline bool validate_boot_dev_sata_dom(const struct boot_media *boot)
{
#ifndef NATIVE_SATA_DOM_SUPPORTED
pr_loc_err("Kernel you are running was built without SATA DoM support, you cannot use %s", CMDLINE_KT_SATADOM);
pr_loc_err("Kernel you are running a kernel was built without SATA DoM support, you cannot use %s%c. "
"You can try booting with %s%c to enable experimental fake-SATA DoM.",
CMDLINE_KT_SATADOM, CMDLINE_KT_SATADOM_NATIVE,
CMDLINE_KT_SATADOM, CMDLINE_KT_SATADOM_FAKE);
return false;
#endif

if (boot->vid != VID_PID_EMPTY || boot->pid != VID_PID_EMPTY)
pr_loc_wrn("Using native SATA-DoM boot - %s and %s parameter values will be ignored",
CMDLINE_CT_VID, CMDLINE_CT_PID);

//this config is impossible as there's no equivalent for force-reinstall boot on SATA, so it's better to detect
//that rather than causing WTFs for someone who falsely assuming that it's possible
//However, it does work with fake-SATA boot (as it emulates USB disk anyway)
if (boot->mfg_mode) {
pr_loc_err("You cannot combine %s%c with %s - the OS supports force-reinstall on USB and fake SATA disk only",
CMDLINE_KT_SATADOM, CMDLINE_KT_SATADOM_NATIVE, CMDLINE_CT_MFG);
return false;
}

pr_loc_dbg("Configured boot device type to fake-SATA DOM");
return true;
}

static __always_inline bool validate_boot_dev_sata_disk(const struct boot_media *boot)
{
#ifdef NATIVE_SATA_DOM_SUPPORTED
pr_loc_wrn("The kernel you are running supports native SATA DoM (%s%c). You're currently using an experimental "
"fake-SATA DoM (%s%c) - consider switching to native SATA DoM (%s%c) for more stable operation.",
CMDLINE_KT_SATADOM, CMDLINE_KT_SATADOM_NATIVE,
CMDLINE_KT_SATADOM, CMDLINE_KT_SATADOM_FAKE,
CMDLINE_KT_SATADOM, CMDLINE_KT_SATADOM_NATIVE);
#endif

if (boot->vid != VID_PID_EMPTY || boot->pid != VID_PID_EMPTY)
pr_loc_wrn("Using SATA-DoM boot - %s and %s parameter values will be ignored",
CMDLINE_CT_VID, CMDLINE_CT_PID);
if (boot->vid != VID_PID_EMPTY || boot->pid != VID_PID_EMPTY)
pr_loc_wrn("Using fake SATA disk boot - %s and %s parameter values will be ignored",
CMDLINE_CT_VID, CMDLINE_CT_PID);

pr_loc_dbg("Configured boot device type to fake-SATA DOM");
return true;
}

//this config is impossible as there's no equivalent for force-reinstall boot on SATA, so it's better to detect
//that rather than causing WTFs for someone who falsely assuming that it's possible
if (boot->mfg_mode) {
pr_loc_err("You cannot combine %s=1 with %s - the OS supports force-reinstall on USB only",
CMDLINE_KT_SATADOM, CMDLINE_CT_MFG);
static inline bool validate_boot_dev(const struct boot_media *boot)
{
switch (boot->type) {
case BOOT_MEDIA_USB:
return validate_boot_dev_usb(boot);
case BOOT_MEDIA_SATA_DOM:
return validate_boot_dev_sata_dom(boot);
case BOOT_MEDIA_SATA_DISK:
return validate_boot_dev_sata_disk(boot);
default:
pr_loc_bug("Got unknown boot type - did you forget to update %s after changing cmdline parsing?",
__FUNCTION__);
return false;
}

pr_loc_dbg("Configured boot device type to SATA");
return true;
}

pr_loc_bug("Got unknown boot type - did you forget to update %s?", __FUNCTION__);
return false;
}

static inline bool validate_nets(const unsigned short if_num, mac_address * const macs[MAX_NET_IFACES])
Expand Down
3 changes: 2 additions & 1 deletion config/runtime_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ typedef char cmdline_token[];

enum boot_media_type {
BOOT_MEDIA_USB,
BOOT_MEDIA_SATA
BOOT_MEDIA_SATA_DOM,
BOOT_MEDIA_SATA_DISK,
};

struct boot_media {
Expand Down
1 change: 1 addition & 0 deletions internal/call_protected.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ DEFINE_UNEXPORTED_SHIM(struct filename *, getname, CP_LIST(const char __user *na
#endif

DEFINE_UNEXPORTED_SHIM(int, scsi_scan_host_selected, CP_LIST(struct Scsi_Host *shost, unsigned int channel, unsigned int id, u64 lun, int rescan), CP_LIST(shost, channel, id, lun, rescan), -EIO);
DEFINE_UNEXPORTED_SHIM(int, ida_pre_get, CP_LIST(struct ida *ida, gfp_t gfp_mask), CP_LIST(ida, gfp_mask), -EINVAL);

DEFINE_UNEXPORTED_SHIM(int, early_serial_setup, CP_LIST(struct uart_port *port), port, -EIO);
DEFINE_UNEXPORTED_SHIM(int, serial8250_find_port, CP_LIST(struct uart_port *p), CP_LIST(p), -EIO);
Expand Down
3 changes: 3 additions & 0 deletions internal/call_protected.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ struct Scsi_Host;
CP_DECLARE_SHIM(int, scsi_scan_host_selected,
CP_LIST(struct Scsi_Host *shost, unsigned int channel, unsigned int id, u64 lun, int rescan));

struct ida;
CP_DECLARE_SHIM(int, ida_pre_get, CP_LIST(struct ida *ida, gfp_t gfp_mask));

struct notifier_block;
CP_DECLARE_SHIM(void, usb_register_notify, CP_LIST(struct notifier_block *nb));
CP_DECLARE_SHIM(void, usb_unregister_notify, CP_LIST(struct notifier_block *nb));
Expand Down
Loading

0 comments on commit de5099c

Please sign in to comment.