From mboxrd@z Thu Jan 1 00:00:00 1970 From: Corey Minyard Date: Thu, 10 Mar 2022 19:19:42 -0600 Subject: [PATCH v6 1/4] ipmi: ssif_bmc: Add SSIF BMC driver In-Reply-To: <20220310114119.13736-2-quan@os.amperecomputing.com> References: <20220310114119.13736-1-quan@os.amperecomputing.com> <20220310114119.13736-2-quan@os.amperecomputing.com> Message-ID: <20220311011942.GX3457@minyard.net> List-Id: To: linux-aspeed@lists.ozlabs.org MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit On Thu, Mar 10, 2022 at 06:41:16PM +0700, Quan Nguyen wrote: > The SMBus system interface (SSIF) IPMI BMC driver can be used to perform > in-band IPMI communication with their host in management (BMC) side. Thanks for the update, and removing slave_enable(). I spent some quality time with this, and it looks like there are places where you don't check lengths before indexing arrays. I've marked several of these, but you need to check it all through this. I have a few other comments, too. -corey > > Signed-off-by: Quan Nguyen > --- > v6: > + Drop the use of slave_enable() [Wolfram] > + Make i2c-aspeed to issue RxCmdLast command on all > I2C_SLAVE_WRITE_REQUESTED event to assert NAK when slave busy [Quan] > + Make i2c slave to return -EBUSY when it's busy [Quan] > + Drop the aborting feature as return Completion Code 0xFF may stop > host to retry and make ipmi_ssif.so fails to load [Quan] > + Add timer to recover slave from busy state when no response [Quan] > + Clean request/response buffer appropriately [Quan] > + Add some minor change on error and warning messages [Quan] > > v5: > + None > > v4: > + Send response with Completion code 0xFF when aborting [Quan] > + Added bounding check on SMBus writes and the whole request [Dan] > + Moved buffer to end of struct ssif_bmc_ctx to avoid context > corruption if somehow buffer is written past the end [Dan] > + Return -EINVAL if userspace buffer too small, dont > silence truncate [Corey, Joel] > + Not necessary to check NONBLOCK in lock [Corey] > + Enforce one user at a time [Joel] > + Reject write with invalid response length from userspace [Corey] > + Add state machines for better ssif bmc state handling [Quan] > + Drop ssif_bmc_aspeed.c and make ssif_bmc.c is generic > SSIF BMC driver [Quan] > + Change compatible string "aspeed,ast2500-ssif-bmc" to > "ampere,ssif-bmc" [Quan] > + Abort current request with invalid SMBus write or > invalid command [Quan] > + Abort all request if there is pending response [Quan] > + Changed validate_pec() to validate_request() [Quan] > + Add unsupported_smbus_cmd() to handle unknown SMBus command [Quan] > + Print internal state string for ease investigating issue [Quan] > + Move to READY state on SLAVE_STOP event [Quan] > + Change initilize_transfer() to process_smbus_cmd() [Quan] > + Introduce functions for each slave event [Quan] > > v3: > + Removed redundant license info [Joel] > + Switched to use traditional if-else [Joel] > + Removed unused ssif_bmc_ioctl() [Joel] > + Made handle_request()/complete_response() to return void [Joel] > + Refactored send_ssif_bmc_response() and > receive_ssif_bmc_request() [Corey] > + Removed mutex [Corey] > + Use spin_lock/unlock_irqsave/restore in callback [Corey] > + Removed the unnecessary memset [Corey] > + Switch to use dev_err() [Corey] > > v2: > + Fixed compiling error with COMPILE_TEST for arc > > drivers/char/ipmi/Kconfig | 11 + > drivers/char/ipmi/Makefile | 1 + > drivers/char/ipmi/ssif_bmc.c | 791 +++++++++++++++++++++++++++++++++++ > drivers/char/ipmi/ssif_bmc.h | 102 +++++ > 4 files changed, 905 insertions(+) > create mode 100644 drivers/char/ipmi/ssif_bmc.c > create mode 100644 drivers/char/ipmi/ssif_bmc.h > > diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig > index b061e6b513ed..18a89093d64e 100644 > --- a/drivers/char/ipmi/Kconfig > +++ b/drivers/char/ipmi/Kconfig > @@ -169,6 +169,17 @@ config ASPEED_BT_IPMI_BMC > found on Aspeed SOCs (AST2400 and AST2500). The driver > implements the BMC side of the BT interface. > > +config SSIF_IPMI_BMC > + tristate "SSIF IPMI BMC driver" > + select I2C > + select I2C_SLAVE > + help > + This enables the IPMI SMBus system interface (SSIF) at the > + management (BMC) side. > + > + The driver implements the BMC side of the SMBus system > + interface (SSIF). > + > config IPMB_DEVICE_INTERFACE > tristate 'IPMB Interface handler' > depends on I2C > diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile > index 7ce790efad92..cb6138b8ded9 100644 > --- a/drivers/char/ipmi/Makefile > +++ b/drivers/char/ipmi/Makefile > @@ -30,3 +30,4 @@ obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o > obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o > +obj-$(CONFIG_SSIF_IPMI_BMC) += ssif_bmc.o > diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c > new file mode 100644 > index 000000000000..62db97773654 > --- /dev/null > +++ b/drivers/char/ipmi/ssif_bmc.c > @@ -0,0 +1,791 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * The driver for BMC side of SSIF interface > + * > + * Copyright (c) 2022, Ampere Computing LLC > + * > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "ssif_bmc.h" > + > +static const char *state_to_string(enum ssif_state state) > +{ > + switch (state) { > + case SSIF_READY: > + return "SSIF_READY"; > + case SSIF_START: > + return "SSIF_START"; > + case SSIF_SMBUS_CMD: > + return "SSIF_SMBUS_CMD"; > + case SSIF_REQ_RECVING: > + return "SSIF_REQ_RECVING"; > + case SSIF_RES_SENDING: > + return "SSIF_RES_SENDING"; > + case SSIF_BAD_SMBUS: > + return "SSIF_BAD_SMBUS"; > + default: > + return "SSIF_STATE_UNKNOWN"; > + } > +} > + > +/* Handle SSIF message that will be sent to user */ > +static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + struct ssif_msg msg; > + unsigned long flags; > + ssize_t ret; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + while (!ssif_bmc->request_available) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + if (file->f_flags & O_NONBLOCK) > + return -EAGAIN; > + ret = wait_event_interruptible(ssif_bmc->wait_queue, > + ssif_bmc->request_available); > + if (ret) > + return ret; > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + } > + > + if (count < min_t(ssize_t, ssif_msg_len(&ssif_bmc->request), sizeof(struct ssif_msg))) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + ret = -EINVAL; > + } else { > + count = min_t(ssize_t, ssif_msg_len(&ssif_bmc->request), sizeof(struct ssif_msg)); > + memcpy(&msg, &ssif_bmc->request, count); > + ssif_bmc->request_available = false; > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + ret = copy_to_user(buf, &msg, count); > + } > + > + return (ret < 0) ? ret : count; > +} > + > +/* Handle SSIF message that is written by user */ > +static ssize_t ssif_bmc_write(struct file *file, const char __user *buf, size_t count, > + loff_t *ppos) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + struct ssif_msg msg; > + unsigned long flags; > + ssize_t ret; > + > + if (count > sizeof(struct ssif_msg)) > + return -EINVAL; > + > + ret = copy_from_user(&msg, buf, count); > + if (ret) > + return ret; > + > + if (!msg.len || count < ssif_msg_len(&msg)) > + return -EINVAL; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + while (ssif_bmc->response_in_progress) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + if (file->f_flags & O_NONBLOCK) > + return -EAGAIN; > + ret = wait_event_interruptible(ssif_bmc->wait_queue, > + !ssif_bmc->response_in_progress); > + if (ret) > + return ret; > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + } > + > + memcpy(&ssif_bmc->response, &msg, count); > + ssif_bmc->is_singlepart_read = (ssif_msg_len(&msg) <= MAX_PAYLOAD_PER_TRANSACTION + 1); > + > + ssif_bmc->response_in_progress = true; > + > + /* ssif_bmc not busy */ > + ssif_bmc->busy = false; > + > + if (ssif_bmc->response_timer_inited) { > + del_timer(&ssif_bmc->response_timer); > + ssif_bmc->response_timer_inited = false; > + } > + /* Clean old request buffer */ > + memset(&ssif_bmc->request, 0, sizeof(struct ssif_msg)); > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + return count; > +} > + > +static int ssif_bmc_open(struct inode *inode, struct file *file) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + int ret = 0; > + > + spin_lock_irq(&ssif_bmc->lock); > + if (!ssif_bmc->running) > + ssif_bmc->running = 1; > + else > + ret = -EBUSY; > + spin_unlock_irq(&ssif_bmc->lock); > + > + return ret; > +} > + > +static unsigned int ssif_bmc_poll(struct file *file, poll_table *wait) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + unsigned int mask = 0; > + > + poll_wait(file, &ssif_bmc->wait_queue, wait); > + > + spin_lock_irq(&ssif_bmc->lock); > + /* The request is available, userspace application can get the request */ > + if (ssif_bmc->request_available) > + mask |= POLLIN; > + > + spin_unlock_irq(&ssif_bmc->lock); > + > + return mask; > +} > + > +static int ssif_bmc_release(struct inode *inode, struct file *file) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + > + spin_lock_irq(&ssif_bmc->lock); > + ssif_bmc->running = 0; > + spin_unlock_irq(&ssif_bmc->lock); > + > + return 0; > +} > + > +/* > + * System calls to device interface for user apps > + */ > +static const struct file_operations ssif_bmc_fops = { > + .owner = THIS_MODULE, > + .open = ssif_bmc_open, > + .read = ssif_bmc_read, > + .write = ssif_bmc_write, > + .release = ssif_bmc_release, > + .poll = ssif_bmc_poll, > +}; > + > +/* Called with ssif_bmc->lock held. */ > +static void complete_response(struct ssif_bmc_ctx *ssif_bmc) > +{ > + /* Invalidate response in buffer to denote it having been sent. */ > + ssif_bmc->response.len = 0; > + ssif_bmc->response_in_progress = false; > + ssif_bmc->nbytes_processed = 0; > + ssif_bmc->remain_len = 0; > + ssif_bmc->busy = false; > + memset(&ssif_bmc->response_buf, 0, MAX_PAYLOAD_PER_TRANSACTION); > + wake_up_all(&ssif_bmc->wait_queue); > +} > + > +static void response_timeout(struct timer_list *t) > +{ > + struct ssif_bmc_ctx *ssif_bmc = from_timer(ssif_bmc, t, response_timer); > + unsigned long flags; > + Is there a possible race here? The timeout can happen at the same time as a received message, will something bad happen if that's the case? > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + > + /* Recover ssif_bmc from busy */ > + ssif_bmc->busy = false; > + del_timer(&ssif_bmc->response_timer); You don't need to delete the timer, it's in the timeout. > + ssif_bmc->response_timer_inited = false; > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > +} > + > +/* Called with ssif_bmc->lock held. */ > +static void handle_request(struct ssif_bmc_ctx *ssif_bmc) > +{ > + /* set ssif_bmc to busy waiting for response */ > + ssif_bmc->busy = true; > + > + /* Request message is available to process */ > + ssif_bmc->request_available = true; > + > + /* Clean old response buffer */ > + memset(&ssif_bmc->response, 0, sizeof(struct ssif_msg)); > + > + /* This is the new READ request.*/ > + wake_up_all(&ssif_bmc->wait_queue); > + > + /* Armed timer to recover slave from busy state in case of no response */ > + if (!ssif_bmc->response_timer_inited) { > + timer_setup(&ssif_bmc->response_timer, response_timeout, 0); > + ssif_bmc->response_timer_inited = true; > + } > + mod_timer(&ssif_bmc->response_timer, jiffies + msecs_to_jiffies(RESPONSE_TIMEOUT)); > +} > + > +static void set_multipart_response_buffer(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 response_len = 0; > + int idx = 0; > + u8 data_len; > + > + data_len = ssif_bmc->response.len; > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_MULTIPART_READ_START: > + /* > + * Read Start length is 32 bytes. > + * Read Start transfer first 30 bytes of IPMI response > + * and 2 special code 0x00, 0x01. > + */ > + *val = MAX_PAYLOAD_PER_TRANSACTION; > + ssif_bmc->remain_len = data_len - MAX_IPMI_DATA_PER_START_TRANSACTION; > + ssif_bmc->block_num = 0; Do you need to validate the data length before using this? This applies for lots of places through here. > + > + ssif_bmc->response_buf[idx++] = 0x00; /* Start Flag */ > + ssif_bmc->response_buf[idx++] = 0x01; /* Start Flag */ > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.netfn_lun; > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.cmd; > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.payload[0]; > + > + response_len = MAX_PAYLOAD_PER_TRANSACTION - idx; > + > + memcpy(&ssif_bmc->response_buf[idx], &ssif_bmc->response.payload[1], > + response_len); > + break; > + > + case SSIF_IPMI_MULTIPART_READ_MIDDLE: > + /* > + * IPMI READ Middle or READ End messages can carry up to 31 bytes > + * IPMI data plus block number byte. > + */ > + if (ssif_bmc->remain_len < MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION) { > + /* > + * This is READ End message > + * Return length is the remaining response data length > + * plus block number > + * Block number 0xFF is to indicate this is last message > + * > + */ > + *val = ssif_bmc->remain_len + 1; > + ssif_bmc->block_num = 0xFF; > + ssif_bmc->response_buf[idx++] = ssif_bmc->block_num; > + response_len = ssif_bmc->remain_len; > + /* Clean the buffer */ > + memset(&ssif_bmc->response_buf[idx], 0, MAX_PAYLOAD_PER_TRANSACTION - idx); > + } else { > + /* > + * This is READ Middle message > + * Response length is the maximum SMBUS transfer length > + * Block number byte is incremented > + * Return length is maximum SMBUS transfer length > + */ > + *val = MAX_PAYLOAD_PER_TRANSACTION; > + ssif_bmc->remain_len -= MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION; > + response_len = MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION; > + ssif_bmc->response_buf[idx++] = ssif_bmc->block_num; > + ssif_bmc->block_num++; > + } > + > + memcpy(&ssif_bmc->response_buf[idx], > + ssif_bmc->response.payload + 1 + ssif_bmc->nbytes_processed, > + response_len); > + break; > + > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > + > + ssif_bmc->nbytes_processed += response_len; > +} > + > +/* Process the IPMI response that will be read by master */ > +static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 *buf; > + u8 pec_len, addr, len; > + u8 pec = 0; > + > + pec_len = ssif_bmc->pec_support ? 1 : 0; > + /* PEC - Start Read Address */ > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + pec = i2c_smbus_pec(pec, &addr, 1); > + /* PEC - SSIF Command */ > + pec = i2c_smbus_pec(pec, &ssif_bmc->smbus_cmd, 1); > + /* PEC - Restart Write Address */ > + addr = addr | 0x01; > + pec = i2c_smbus_pec(pec, &addr, 1); > + > + if (ssif_bmc->is_singlepart_read) { > + /* Single-part Read processing */ > + buf = (u8 *)&ssif_bmc->response; > + > + if (ssif_bmc->response.len && ssif_bmc->msg_idx < ssif_bmc->response.len) { > + ssif_bmc->msg_idx++; > + *val = buf[ssif_bmc->msg_idx]; > + } else if (ssif_bmc->response.len && ssif_bmc->msg_idx == ssif_bmc->response.len) { > + ssif_bmc->msg_idx++; > + *val = i2c_smbus_pec(pec, buf, ssif_msg_len(&ssif_bmc->response)); > + } else { I thought for a second that this was wrong, but I think it's correct. Supply zero if you don't have data. > + *val = 0; > + } > + /* Invalidate response buffer to denote it is sent */ > + if (ssif_bmc->msg_idx + 1 >= (ssif_msg_len(&ssif_bmc->response) + pec_len)) > + complete_response(ssif_bmc); > + } else { > + /* Multi-part Read processing */ You don't check the length here like you did above. I think that's required. > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_MULTIPART_READ_START: > + case SSIF_IPMI_MULTIPART_READ_MIDDLE: > + buf = (u8 *)&ssif_bmc->response_buf; > + *val = buf[ssif_bmc->msg_idx]; > + ssif_bmc->msg_idx++; > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > + > + len = (ssif_bmc->block_num == 0xFF) ? > + ssif_bmc->remain_len + 1 : MAX_PAYLOAD_PER_TRANSACTION; > + if (ssif_bmc->msg_idx == (len + 1)) { > + pec = i2c_smbus_pec(pec, &len, 1); > + *val = i2c_smbus_pec(pec, ssif_bmc->response_buf, len); > + } > + /* Invalidate response buffer to denote last response is sent */ > + if (ssif_bmc->block_num == 0xFF && > + ssif_bmc->msg_idx > (ssif_bmc->remain_len + pec_len)) { > + complete_response(ssif_bmc); > + } > + } > +} > + > +static void handle_write_received(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 *buf = (u8 *)&ssif_bmc->request; > + > + if (ssif_bmc->msg_idx >= sizeof(struct ssif_msg)) > + return; > + > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_SINGLEPART_WRITE: > + buf[ssif_bmc->msg_idx - 1] = *val; > + ssif_bmc->msg_idx++; > + > + break; > + case SSIF_IPMI_MULTIPART_WRITE_START: > + if (ssif_bmc->msg_idx == 1) > + ssif_bmc->request.len = 0; > + > + fallthrough; > + case SSIF_IPMI_MULTIPART_WRITE_MIDDLE: > + /* The len should always be 32 */ > + if (ssif_bmc->msg_idx == 1 && *val != MAX_PAYLOAD_PER_TRANSACTION) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid Multipart Write len"); > + > + fallthrough; > + case SSIF_IPMI_MULTIPART_WRITE_END: > + /* Multi-part write, 2nd byte received is length */ > + if (ssif_bmc->msg_idx == 1) { > + if (*val > MAX_PAYLOAD_PER_TRANSACTION) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid Multipart Write End len"); > + > + ssif_bmc->request.len += *val; > + ssif_bmc->recv_len = *val; > + > + /* request len should never exceeded 255 bytes */ > + if (ssif_bmc->request.len > 255) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid request len"); > + > + } else { You check msg_idx above, but I'm not sure that check will cover this operation. > + buf[ssif_bmc->msg_idx - 1 + > + ssif_bmc->request.len - ssif_bmc->recv_len] = *val; > + } > + > + ssif_bmc->msg_idx++; > + > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > +} > + > +static bool validate_request(struct ssif_bmc_ctx *ssif_bmc) > +{ > + u8 rpec = 0, cpec = 0; > + bool ret = true; > + u8 addr, index; > + u8 *buf; > + Is there any length validation for using buf below? It looks like you are accessing without checking length, but maybe I missed something. > + buf = (u8 *)&ssif_bmc->request; > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_SINGLEPART_WRITE: > + if ((ssif_bmc->msg_idx - 1) == ssif_msg_len(&ssif_bmc->request)) { > + /* PEC is not included */ > + ssif_bmc->pec_support = false; > + ret = true; > + goto exit; > + } > + > + if ((ssif_bmc->msg_idx - 1) != (ssif_msg_len(&ssif_bmc->request) + 1)) { > + dev_err(&ssif_bmc->client->dev, "Error: Unexpected length received %d\n", > + ssif_msg_len(&ssif_bmc->request)); > + ret = false; > + goto exit; > + } > + > + /* PEC is included */ > + ssif_bmc->pec_support = true; > + rpec = buf[ssif_bmc->msg_idx - 2]; > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + cpec = i2c_smbus_pec(cpec, &addr, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->smbus_cmd, 1); > + cpec = i2c_smbus_pec(cpec, buf, ssif_msg_len(&ssif_bmc->request)); > + if (rpec != cpec) { > + dev_err(&ssif_bmc->client->dev, "Bad PEC 0x%02x vs. 0x%02x\n", rpec, cpec); > + ret = false; > + } > + > + break; > + case SSIF_IPMI_MULTIPART_WRITE_START: > + case SSIF_IPMI_MULTIPART_WRITE_MIDDLE: > + case SSIF_IPMI_MULTIPART_WRITE_END: > + index = ssif_bmc->request.len - ssif_bmc->recv_len; > + if ((ssif_bmc->msg_idx - 1 + index) == ssif_msg_len(&ssif_bmc->request)) { > + /* PEC is not included */ > + ssif_bmc->pec_support = false; > + ret = true; > + goto exit; > + } > + > + if ((ssif_bmc->msg_idx - 1 + index) != (ssif_msg_len(&ssif_bmc->request) + 1)) { > + dev_err(&ssif_bmc->client->dev, "Error: Unexpected length received %d\n", > + ssif_msg_len(&ssif_bmc->request)); > + ret = false; > + goto exit; > + } > + > + /* PEC is included */ > + ssif_bmc->pec_support = true; > + rpec = buf[ssif_bmc->msg_idx - 2 + index]; > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + cpec = i2c_smbus_pec(cpec, &addr, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->smbus_cmd, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->recv_len, 1); Just curious, I'm not sure the client side PEC in the Linux driver has ever been validated. Have you tested both sides? > + /* As SMBus specification does not allow the length > + * (byte count) in the Write-Block protocol to be zero. > + * Therefore, it is illegal to have the last Middle > + * transaction in the sequence carry 32-byte and have > + * a length of ?0? in the End transaction. > + * But some users may try to use this way and we should > + * prevent ssif_bmc driver broken in this case. > + */ > + if (ssif_bmc->recv_len != 0) > + cpec = i2c_smbus_pec(cpec, buf + 1 + index, ssif_bmc->recv_len); > + > + if (rpec != cpec) { > + dev_err(&ssif_bmc->client->dev, "Bad PEC 0x%02x vs. 0x%02x\n", rpec, cpec); > + ret = false; > + } > + > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + ret = false; > + break; > + } > + > +exit: > + return ret; > +} > + Just a nit, more a general coding style comment. It's almost always a bad idea to put a negative (unsupported) into a check function. You often end up with something like: if (!unsupported_smbus_cmd(c)).... which looks a little strange. Double negatives can make it hard to follow and lead to mistakes. This one isn't too bad, but sometimes it can be. It's better to do: if (supported_smbus_cmd(c)).... or if (!supported_smbus_cmd(c)).... > +static bool unsupported_smbus_cmd(u8 cmd) > +{ > + if (cmd == SSIF_IPMI_SINGLEPART_READ || > + cmd == SSIF_IPMI_SINGLEPART_WRITE || > + cmd == SSIF_IPMI_MULTIPART_WRITE_START || > + cmd == SSIF_IPMI_MULTIPART_WRITE_MIDDLE || > + cmd == SSIF_IPMI_MULTIPART_WRITE_END || > + cmd == SSIF_IPMI_MULTIPART_READ_START || > + cmd == SSIF_IPMI_MULTIPART_READ_MIDDLE) > + return false; > + > + return true; > +} > + > +static void process_smbus_cmd(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + /* SMBUS command can vary (single or multi-part) */ > + ssif_bmc->smbus_cmd = *val; > + ssif_bmc->msg_idx++; > + > + if (unsupported_smbus_cmd(*val)) > + dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus command"); > + > + if (*val == SSIF_IPMI_SINGLEPART_WRITE || > + *val == SSIF_IPMI_MULTIPART_WRITE_START) { > + /* > + * The response maybe not come in-time, causing host SSIF driver > + * to timeout and resend a new request. In such case check for > + * pending response and clear it > + */ > + if (ssif_bmc->response_in_progress) > + complete_response(ssif_bmc); > + } > +} > + > +static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_RES_SENDING) { > + ssif_bmc->state = SSIF_BAD_SMBUS; > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected READ REQUESTED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_RES_SENDING; > + } > + > + ssif_bmc->msg_idx = 0; > + if (ssif_bmc->is_singlepart_read) > + *val = ssif_bmc->response.len; > + else > + set_multipart_response_buffer(ssif_bmc, val); > +} > + > +static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_SMBUS_CMD) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected READ PROCESSED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } > + > + handle_read_processed(ssif_bmc, val); > +} > + > +static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + ssif_bmc->msg_idx = 0; > + > + if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_START; > + > + } else if (ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_RES_SENDING) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected WRITE REQUEST in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } > +} > + > +static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_RES_SENDING) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected WRITE RECEIVED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } else if (ssif_bmc->state == SSIF_START) { > + ssif_bmc->state = SSIF_SMBUS_CMD; > + } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_REQ_RECVING; > + } > + > + /* This is response sending state */ > + if (ssif_bmc->state == SSIF_REQ_RECVING) > + handle_write_received(ssif_bmc, val); > + else if (ssif_bmc->state == SSIF_SMBUS_CMD) > + process_smbus_cmd(ssif_bmc, val); > +} > + > +static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_SMBUS_CMD) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected SLAVE STOP in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_BAD_SMBUS) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s received SLAVE STOP from bad state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_REQ_RECVING) { > + /* A BMC that receives an invalid request drop the data for the write > + * transaction and any further transactions (read or write) until > + * the next valid read or write Start transaction is received > + */ > + if (!validate_request(ssif_bmc)) > + dev_err(&ssif_bmc->client->dev, "Error: invalid pec\n"); > + else if (ssif_bmc->smbus_cmd == SSIF_IPMI_SINGLEPART_WRITE || > + ssif_bmc->smbus_cmd == SSIF_IPMI_MULTIPART_WRITE_END) > + handle_request(ssif_bmc); > + } > + > + ssif_bmc->state = SSIF_READY; > + /* Reset message index */ > + ssif_bmc->msg_idx = 0; > +} > + > +/* > + * Callback function to handle I2C slave events > + */ > +static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8 *val) > +{ > + unsigned long flags; > + struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); > + int ret = 0; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + > + switch (event) { > + case I2C_SLAVE_READ_REQUESTED: > + on_read_requested_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_WRITE_REQUESTED: > + on_write_requested_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_READ_PROCESSED: > + on_read_processed_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_WRITE_RECEIVED: > + on_write_received_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_STOP: > + on_stop_event(ssif_bmc, val); > + break; > + > + default: > + dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n"); > + break; > + } > + > + if (ssif_bmc->busy) > + ret = -EBUSY; > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + return ret; > +} > + > +static int ssif_bmc_probe(struct i2c_client *client, const struct i2c_device_id *id) > +{ > + struct ssif_bmc_ctx *ssif_bmc; > + int ret; > + > + ssif_bmc = devm_kzalloc(&client->dev, sizeof(*ssif_bmc), GFP_KERNEL); > + if (!ssif_bmc) > + return -ENOMEM; > + > + spin_lock_init(&ssif_bmc->lock); > + > + init_waitqueue_head(&ssif_bmc->wait_queue); > + ssif_bmc->request_available = false; > + ssif_bmc->response_in_progress = false; > + ssif_bmc->busy = false; > + ssif_bmc->response_timer_inited = false; > + > + /* Register misc device interface */ > + ssif_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; > + ssif_bmc->miscdev.name = DEVICE_NAME; > + ssif_bmc->miscdev.fops = &ssif_bmc_fops; > + ssif_bmc->miscdev.parent = &client->dev; > + ret = misc_register(&ssif_bmc->miscdev); > + if (ret) > + goto out; > + > + ssif_bmc->client = client; > + ssif_bmc->client->flags |= I2C_CLIENT_SLAVE; > + > + /* Register I2C slave */ > + i2c_set_clientdata(client, ssif_bmc); > + ret = i2c_slave_register(client, ssif_bmc_cb); > + if (ret) { > + misc_deregister(&ssif_bmc->miscdev); > + goto out; > + } > + > + return 0; > +out: > + devm_kfree(&client->dev, ssif_bmc); > + return ret; > +} > + > +static int ssif_bmc_remove(struct i2c_client *client) > +{ > + struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); > + > + i2c_slave_unregister(client); > + misc_deregister(&ssif_bmc->miscdev); > + > + return 0; > +} > + > +static const struct of_device_id ssif_bmc_match[] = { > + { .compatible = "ampere,ssif-bmc" }, > + { }, > +}; > + > +static const struct i2c_device_id ssif_bmc_id[] = { > + { DEVICE_NAME, 0 }, > + { }, > +}; > + > +MODULE_DEVICE_TABLE(i2c, ssif_bmc_id); > + > +static struct i2c_driver ssif_bmc_driver = { > + .driver = { > + .name = DEVICE_NAME, > + .of_match_table = ssif_bmc_match, > + }, > + .probe = ssif_bmc_probe, > + .remove = ssif_bmc_remove, > + .id_table = ssif_bmc_id, > +}; > + > +module_i2c_driver(ssif_bmc_driver); > + > +MODULE_AUTHOR("Quan Nguyen "); > +MODULE_AUTHOR("Chuong Tran "); > +MODULE_DESCRIPTION("Linux device driver of the BMC IPMI SSIF interface."); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/char/ipmi/ssif_bmc.h b/drivers/char/ipmi/ssif_bmc.h > new file mode 100644 > index 000000000000..9a26f3c252cc > --- /dev/null > +++ b/drivers/char/ipmi/ssif_bmc.h > @@ -0,0 +1,102 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * The driver for BMC side of SSIF interface > + * > + * Copyright (c) 2022, Ampere Computing LLC > + * > + */ > +#ifndef __SSIF_BMC_H__ > +#define __SSIF_BMC_H__ > + > +#define DEVICE_NAME "ipmi-ssif-host" > + > +#define GET_8BIT_ADDR(addr_7bit) (((addr_7bit) << 1) & 0xff) > + > +/* A standard SMBus Transaction is limited to 32 data bytes */ > +#define MAX_PAYLOAD_PER_TRANSACTION 32 > + > +#define MAX_IPMI_DATA_PER_START_TRANSACTION 30 > +#define MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION 31 > + > +#define SSIF_IPMI_SINGLEPART_WRITE 0x2 > +#define SSIF_IPMI_SINGLEPART_READ 0x3 > +#define SSIF_IPMI_MULTIPART_WRITE_START 0x6 > +#define SSIF_IPMI_MULTIPART_WRITE_MIDDLE 0x7 > +#define SSIF_IPMI_MULTIPART_WRITE_END 0x8 > +#define SSIF_IPMI_MULTIPART_READ_START 0x3 > +#define SSIF_IPMI_MULTIPART_READ_MIDDLE 0x9 > + > +#define MSG_PAYLOAD_LEN_MAX 252 > +/* > + * IPMI 2.0 Spec, section 12.7 SSIF Timing, > + * Request-to-Response Time is T6max(250ms) - T1max(20ms) - 3ms = 227ms > + * Recover ssif_bmc from busy state if it takes upto 500ms > + */ > +#define RESPONSE_TIMEOUT 500 /* ms */ > + > +struct ssif_msg { > + u8 len; > + u8 netfn_lun; > + u8 cmd; > + u8 payload[MSG_PAYLOAD_LEN_MAX]; > +} __packed; > + > +static inline u32 ssif_msg_len(struct ssif_msg *ssif_msg) > +{ > + return ssif_msg->len + 1; > +} > + > +/* > + * SSIF internal states: > + * SSIF_READY 0x00 : Ready state > + * SSIF_START 0x01 : Start smbus transaction > + * SSIF_SMBUS_CMD 0x02 : Received SMBus command > + * SSIF_REQ_RECVING 0x03 : Receiving request > + * SSIF_RES_SENDING 0x04 : Sending response > + * SSIF_BAD_SMBUS 0x05 : Bad SMbus transaction > + */ > +enum ssif_state { > + SSIF_READY, > + SSIF_START, > + SSIF_SMBUS_CMD, > + SSIF_REQ_RECVING, > + SSIF_RES_SENDING, > + SSIF_BAD_SMBUS, > + SSIF_STATE_MAX > +}; > + > +struct ssif_bmc_ctx { > + struct i2c_client *client; > + struct miscdevice miscdev; > + size_t msg_idx; > + bool pec_support; > + /* ssif bmc spinlock */ > + spinlock_t lock; > + wait_queue_head_t wait_queue; > + u8 running; > + enum ssif_state state; > + u8 smbus_cmd; > + /* Timeout waiting for response */ > + struct timer_list response_timer; > + bool response_timer_inited; > + /* Flag to identify a Multi-part Read Transaction */ > + bool is_singlepart_read; > + u8 nbytes_processed; > + u8 remain_len; > + u8 recv_len; > + /* Block Number of a Multi-part Read Transaction */ > + u8 block_num; > + bool request_available; > + bool response_in_progress; > + bool busy; > + /* Response buffer for Multi-part Read Transaction */ > + u8 response_buf[MAX_PAYLOAD_PER_TRANSACTION]; > + struct ssif_msg response; > + struct ssif_msg request; > +}; > + > +static inline struct ssif_bmc_ctx *to_ssif_bmc(struct file *file) > +{ > + return container_of(file->private_data, struct ssif_bmc_ctx, miscdev); > +} > +#endif /* __SSIF_BMC_H__ */ > -- > 2.35.1 > > From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 494B7C433FE for ; Fri, 11 Mar 2022 01:19:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345296AbiCKBUu (ORCPT ); Thu, 10 Mar 2022 20:20:50 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60700 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239939AbiCKBUu (ORCPT ); Thu, 10 Mar 2022 20:20:50 -0500 Received: from mail-qv1-xf30.google.com (mail-qv1-xf30.google.com [IPv6:2607:f8b0:4864:20::f30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A4EA3E2364; Thu, 10 Mar 2022 17:19:46 -0800 (PST) Received: by mail-qv1-xf30.google.com with SMTP id j5so5905659qvs.13; Thu, 10 Mar 2022 17:19:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=sender:date:from:to:cc:subject:message-id:reply-to:references :mime-version:content-disposition:content-transfer-encoding :in-reply-to; bh=BWLuHkhuLRrMWa8N+fItg7v8ydmq3TO/fXGntCYkO6Y=; b=SuZFZlMlkQQ4S0kNlTh30UByunM4Zhvp+V3yvzH/qoDsqzjS5ejE4svpd4iljAoCpe 05CWTKkCU4/1y1Oer0zWzRR+rzc9qsKnfi/+O+5APKfE5w4C6685gA6L5M+eQr8VcMRU WoxEtvQGfQVnstvr0PVvn/XMJfnxpvQ7ppOqx/ijctdDbEWfeeu8sLFlCWe8zd8SiiR8 bcyxFgqVt448XrBh1iTQACZZIpaZ+TQLBirITlFNPLl+RpX+lct2fy7UvwuNop1xCfvd IbXt454ECUwlSL6Hm9jm0BUcxvNpK46BayRtG2HvJGrt98Qjx/K64P+ZwN3DovFmbFzR labg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:sender:date:from:to:cc:subject:message-id :reply-to:references:mime-version:content-disposition :content-transfer-encoding:in-reply-to; bh=BWLuHkhuLRrMWa8N+fItg7v8ydmq3TO/fXGntCYkO6Y=; b=4/w/4uCysV38TTs39LVwssYnRCgl7dgiU/TZBQXZ9VcyzL0cOLtTWq5SZhCjf6JQ7v dVZf1vdsDu2w7B5guucJRLruAp2K1U+JcLL4VcGqjdmURQWnfjLwikPzje+gJWXNE0aY OQ8OF8u0H28hOyRFmq8CNxNGIaJTPCe/GuXbfq5dKMsqVFGMr4ZdJtFLqg2oBNXzHwaR vNCPu5AVrYOHgGqJH8gc2mcTyIBMou8w8iBS0lFQQKCj/tJ4EsTmzq2Q3RfjipPa9JPB 7emr8DLcaZPKIgHxz6q+3ifaAyQdab+Pcsnh7OZuyRnn4HD5vF19YOn3nLA4++BzK+lb DcIg== X-Gm-Message-State: AOAM532dCwvBHHQeBCd+bxlXeF7zsRf0i7XaotVbhB4AUmH0idXMgQwr rnHAryXoKzup3xPBAt6vfQ== X-Google-Smtp-Source: ABdhPJwL3ZWJehYxMf6yO6S5Io+T49q/ukI/FjtuleC1YSlSekImxG5KL9P6qWMS6cqySdCIpaq6Ig== X-Received: by 2002:a05:6214:76b:b0:435:d12f:628f with SMTP id f11-20020a056214076b00b00435d12f628fmr5403209qvz.53.1646961585262; Thu, 10 Mar 2022 17:19:45 -0800 (PST) Received: from serve.minyard.net (serve.minyard.net. [2001:470:b8f6:1b::1]) by smtp.gmail.com with ESMTPSA id z6-20020ae9c106000000b0067d3b9ef387sm2727890qki.28.2022.03.10.17.19.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Mar 2022 17:19:44 -0800 (PST) Sender: Corey Minyard Received: from minyard.net (unknown [IPv6:2001:470:b8f6:1b:dc9d:d155:a010:ac11]) by serve.minyard.net (Postfix) with ESMTPSA id 2CC7E1800BC; Fri, 11 Mar 2022 01:19:43 +0000 (UTC) Date: Thu, 10 Mar 2022 19:19:42 -0600 From: Corey Minyard To: Quan Nguyen Cc: Rob Herring , Krzysztof Kozlowski , Joel Stanley , Andrew Jeffery , Brendan Higgins , Benjamin Herrenschmidt , Wolfram Sang , openipmi-developer@lists.sourceforge.net, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, openbmc@lists.ozlabs.org, Open Source Submission , Phong Vo , "Thang Q . Nguyen" Subject: Re: [PATCH v6 1/4] ipmi: ssif_bmc: Add SSIF BMC driver Message-ID: <20220311011942.GX3457@minyard.net> Reply-To: minyard@acm.org References: <20220310114119.13736-1-quan@os.amperecomputing.com> <20220310114119.13736-2-quan@os.amperecomputing.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20220310114119.13736-2-quan@os.amperecomputing.com> Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org On Thu, Mar 10, 2022 at 06:41:16PM +0700, Quan Nguyen wrote: > The SMBus system interface (SSIF) IPMI BMC driver can be used to perform > in-band IPMI communication with their host in management (BMC) side. Thanks for the update, and removing slave_enable(). I spent some quality time with this, and it looks like there are places where you don't check lengths before indexing arrays. I've marked several of these, but you need to check it all through this. I have a few other comments, too. -corey > > Signed-off-by: Quan Nguyen > --- > v6: > + Drop the use of slave_enable() [Wolfram] > + Make i2c-aspeed to issue RxCmdLast command on all > I2C_SLAVE_WRITE_REQUESTED event to assert NAK when slave busy [Quan] > + Make i2c slave to return -EBUSY when it's busy [Quan] > + Drop the aborting feature as return Completion Code 0xFF may stop > host to retry and make ipmi_ssif.so fails to load [Quan] > + Add timer to recover slave from busy state when no response [Quan] > + Clean request/response buffer appropriately [Quan] > + Add some minor change on error and warning messages [Quan] > > v5: > + None > > v4: > + Send response with Completion code 0xFF when aborting [Quan] > + Added bounding check on SMBus writes and the whole request [Dan] > + Moved buffer to end of struct ssif_bmc_ctx to avoid context > corruption if somehow buffer is written past the end [Dan] > + Return -EINVAL if userspace buffer too small, dont > silence truncate [Corey, Joel] > + Not necessary to check NONBLOCK in lock [Corey] > + Enforce one user at a time [Joel] > + Reject write with invalid response length from userspace [Corey] > + Add state machines for better ssif bmc state handling [Quan] > + Drop ssif_bmc_aspeed.c and make ssif_bmc.c is generic > SSIF BMC driver [Quan] > + Change compatible string "aspeed,ast2500-ssif-bmc" to > "ampere,ssif-bmc" [Quan] > + Abort current request with invalid SMBus write or > invalid command [Quan] > + Abort all request if there is pending response [Quan] > + Changed validate_pec() to validate_request() [Quan] > + Add unsupported_smbus_cmd() to handle unknown SMBus command [Quan] > + Print internal state string for ease investigating issue [Quan] > + Move to READY state on SLAVE_STOP event [Quan] > + Change initilize_transfer() to process_smbus_cmd() [Quan] > + Introduce functions for each slave event [Quan] > > v3: > + Removed redundant license info [Joel] > + Switched to use traditional if-else [Joel] > + Removed unused ssif_bmc_ioctl() [Joel] > + Made handle_request()/complete_response() to return void [Joel] > + Refactored send_ssif_bmc_response() and > receive_ssif_bmc_request() [Corey] > + Removed mutex [Corey] > + Use spin_lock/unlock_irqsave/restore in callback [Corey] > + Removed the unnecessary memset [Corey] > + Switch to use dev_err() [Corey] > > v2: > + Fixed compiling error with COMPILE_TEST for arc > > drivers/char/ipmi/Kconfig | 11 + > drivers/char/ipmi/Makefile | 1 + > drivers/char/ipmi/ssif_bmc.c | 791 +++++++++++++++++++++++++++++++++++ > drivers/char/ipmi/ssif_bmc.h | 102 +++++ > 4 files changed, 905 insertions(+) > create mode 100644 drivers/char/ipmi/ssif_bmc.c > create mode 100644 drivers/char/ipmi/ssif_bmc.h > > diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig > index b061e6b513ed..18a89093d64e 100644 > --- a/drivers/char/ipmi/Kconfig > +++ b/drivers/char/ipmi/Kconfig > @@ -169,6 +169,17 @@ config ASPEED_BT_IPMI_BMC > found on Aspeed SOCs (AST2400 and AST2500). The driver > implements the BMC side of the BT interface. > > +config SSIF_IPMI_BMC > + tristate "SSIF IPMI BMC driver" > + select I2C > + select I2C_SLAVE > + help > + This enables the IPMI SMBus system interface (SSIF) at the > + management (BMC) side. > + > + The driver implements the BMC side of the SMBus system > + interface (SSIF). > + > config IPMB_DEVICE_INTERFACE > tristate 'IPMB Interface handler' > depends on I2C > diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile > index 7ce790efad92..cb6138b8ded9 100644 > --- a/drivers/char/ipmi/Makefile > +++ b/drivers/char/ipmi/Makefile > @@ -30,3 +30,4 @@ obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o > obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o > +obj-$(CONFIG_SSIF_IPMI_BMC) += ssif_bmc.o > diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c > new file mode 100644 > index 000000000000..62db97773654 > --- /dev/null > +++ b/drivers/char/ipmi/ssif_bmc.c > @@ -0,0 +1,791 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * The driver for BMC side of SSIF interface > + * > + * Copyright (c) 2022, Ampere Computing LLC > + * > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "ssif_bmc.h" > + > +static const char *state_to_string(enum ssif_state state) > +{ > + switch (state) { > + case SSIF_READY: > + return "SSIF_READY"; > + case SSIF_START: > + return "SSIF_START"; > + case SSIF_SMBUS_CMD: > + return "SSIF_SMBUS_CMD"; > + case SSIF_REQ_RECVING: > + return "SSIF_REQ_RECVING"; > + case SSIF_RES_SENDING: > + return "SSIF_RES_SENDING"; > + case SSIF_BAD_SMBUS: > + return "SSIF_BAD_SMBUS"; > + default: > + return "SSIF_STATE_UNKNOWN"; > + } > +} > + > +/* Handle SSIF message that will be sent to user */ > +static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + struct ssif_msg msg; > + unsigned long flags; > + ssize_t ret; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + while (!ssif_bmc->request_available) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + if (file->f_flags & O_NONBLOCK) > + return -EAGAIN; > + ret = wait_event_interruptible(ssif_bmc->wait_queue, > + ssif_bmc->request_available); > + if (ret) > + return ret; > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + } > + > + if (count < min_t(ssize_t, ssif_msg_len(&ssif_bmc->request), sizeof(struct ssif_msg))) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + ret = -EINVAL; > + } else { > + count = min_t(ssize_t, ssif_msg_len(&ssif_bmc->request), sizeof(struct ssif_msg)); > + memcpy(&msg, &ssif_bmc->request, count); > + ssif_bmc->request_available = false; > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + ret = copy_to_user(buf, &msg, count); > + } > + > + return (ret < 0) ? ret : count; > +} > + > +/* Handle SSIF message that is written by user */ > +static ssize_t ssif_bmc_write(struct file *file, const char __user *buf, size_t count, > + loff_t *ppos) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + struct ssif_msg msg; > + unsigned long flags; > + ssize_t ret; > + > + if (count > sizeof(struct ssif_msg)) > + return -EINVAL; > + > + ret = copy_from_user(&msg, buf, count); > + if (ret) > + return ret; > + > + if (!msg.len || count < ssif_msg_len(&msg)) > + return -EINVAL; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + while (ssif_bmc->response_in_progress) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + if (file->f_flags & O_NONBLOCK) > + return -EAGAIN; > + ret = wait_event_interruptible(ssif_bmc->wait_queue, > + !ssif_bmc->response_in_progress); > + if (ret) > + return ret; > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + } > + > + memcpy(&ssif_bmc->response, &msg, count); > + ssif_bmc->is_singlepart_read = (ssif_msg_len(&msg) <= MAX_PAYLOAD_PER_TRANSACTION + 1); > + > + ssif_bmc->response_in_progress = true; > + > + /* ssif_bmc not busy */ > + ssif_bmc->busy = false; > + > + if (ssif_bmc->response_timer_inited) { > + del_timer(&ssif_bmc->response_timer); > + ssif_bmc->response_timer_inited = false; > + } > + /* Clean old request buffer */ > + memset(&ssif_bmc->request, 0, sizeof(struct ssif_msg)); > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + return count; > +} > + > +static int ssif_bmc_open(struct inode *inode, struct file *file) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + int ret = 0; > + > + spin_lock_irq(&ssif_bmc->lock); > + if (!ssif_bmc->running) > + ssif_bmc->running = 1; > + else > + ret = -EBUSY; > + spin_unlock_irq(&ssif_bmc->lock); > + > + return ret; > +} > + > +static unsigned int ssif_bmc_poll(struct file *file, poll_table *wait) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + unsigned int mask = 0; > + > + poll_wait(file, &ssif_bmc->wait_queue, wait); > + > + spin_lock_irq(&ssif_bmc->lock); > + /* The request is available, userspace application can get the request */ > + if (ssif_bmc->request_available) > + mask |= POLLIN; > + > + spin_unlock_irq(&ssif_bmc->lock); > + > + return mask; > +} > + > +static int ssif_bmc_release(struct inode *inode, struct file *file) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + > + spin_lock_irq(&ssif_bmc->lock); > + ssif_bmc->running = 0; > + spin_unlock_irq(&ssif_bmc->lock); > + > + return 0; > +} > + > +/* > + * System calls to device interface for user apps > + */ > +static const struct file_operations ssif_bmc_fops = { > + .owner = THIS_MODULE, > + .open = ssif_bmc_open, > + .read = ssif_bmc_read, > + .write = ssif_bmc_write, > + .release = ssif_bmc_release, > + .poll = ssif_bmc_poll, > +}; > + > +/* Called with ssif_bmc->lock held. */ > +static void complete_response(struct ssif_bmc_ctx *ssif_bmc) > +{ > + /* Invalidate response in buffer to denote it having been sent. */ > + ssif_bmc->response.len = 0; > + ssif_bmc->response_in_progress = false; > + ssif_bmc->nbytes_processed = 0; > + ssif_bmc->remain_len = 0; > + ssif_bmc->busy = false; > + memset(&ssif_bmc->response_buf, 0, MAX_PAYLOAD_PER_TRANSACTION); > + wake_up_all(&ssif_bmc->wait_queue); > +} > + > +static void response_timeout(struct timer_list *t) > +{ > + struct ssif_bmc_ctx *ssif_bmc = from_timer(ssif_bmc, t, response_timer); > + unsigned long flags; > + Is there a possible race here? The timeout can happen at the same time as a received message, will something bad happen if that's the case? > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + > + /* Recover ssif_bmc from busy */ > + ssif_bmc->busy = false; > + del_timer(&ssif_bmc->response_timer); You don't need to delete the timer, it's in the timeout. > + ssif_bmc->response_timer_inited = false; > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > +} > + > +/* Called with ssif_bmc->lock held. */ > +static void handle_request(struct ssif_bmc_ctx *ssif_bmc) > +{ > + /* set ssif_bmc to busy waiting for response */ > + ssif_bmc->busy = true; > + > + /* Request message is available to process */ > + ssif_bmc->request_available = true; > + > + /* Clean old response buffer */ > + memset(&ssif_bmc->response, 0, sizeof(struct ssif_msg)); > + > + /* This is the new READ request.*/ > + wake_up_all(&ssif_bmc->wait_queue); > + > + /* Armed timer to recover slave from busy state in case of no response */ > + if (!ssif_bmc->response_timer_inited) { > + timer_setup(&ssif_bmc->response_timer, response_timeout, 0); > + ssif_bmc->response_timer_inited = true; > + } > + mod_timer(&ssif_bmc->response_timer, jiffies + msecs_to_jiffies(RESPONSE_TIMEOUT)); > +} > + > +static void set_multipart_response_buffer(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 response_len = 0; > + int idx = 0; > + u8 data_len; > + > + data_len = ssif_bmc->response.len; > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_MULTIPART_READ_START: > + /* > + * Read Start length is 32 bytes. > + * Read Start transfer first 30 bytes of IPMI response > + * and 2 special code 0x00, 0x01. > + */ > + *val = MAX_PAYLOAD_PER_TRANSACTION; > + ssif_bmc->remain_len = data_len - MAX_IPMI_DATA_PER_START_TRANSACTION; > + ssif_bmc->block_num = 0; Do you need to validate the data length before using this? This applies for lots of places through here. > + > + ssif_bmc->response_buf[idx++] = 0x00; /* Start Flag */ > + ssif_bmc->response_buf[idx++] = 0x01; /* Start Flag */ > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.netfn_lun; > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.cmd; > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.payload[0]; > + > + response_len = MAX_PAYLOAD_PER_TRANSACTION - idx; > + > + memcpy(&ssif_bmc->response_buf[idx], &ssif_bmc->response.payload[1], > + response_len); > + break; > + > + case SSIF_IPMI_MULTIPART_READ_MIDDLE: > + /* > + * IPMI READ Middle or READ End messages can carry up to 31 bytes > + * IPMI data plus block number byte. > + */ > + if (ssif_bmc->remain_len < MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION) { > + /* > + * This is READ End message > + * Return length is the remaining response data length > + * plus block number > + * Block number 0xFF is to indicate this is last message > + * > + */ > + *val = ssif_bmc->remain_len + 1; > + ssif_bmc->block_num = 0xFF; > + ssif_bmc->response_buf[idx++] = ssif_bmc->block_num; > + response_len = ssif_bmc->remain_len; > + /* Clean the buffer */ > + memset(&ssif_bmc->response_buf[idx], 0, MAX_PAYLOAD_PER_TRANSACTION - idx); > + } else { > + /* > + * This is READ Middle message > + * Response length is the maximum SMBUS transfer length > + * Block number byte is incremented > + * Return length is maximum SMBUS transfer length > + */ > + *val = MAX_PAYLOAD_PER_TRANSACTION; > + ssif_bmc->remain_len -= MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION; > + response_len = MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION; > + ssif_bmc->response_buf[idx++] = ssif_bmc->block_num; > + ssif_bmc->block_num++; > + } > + > + memcpy(&ssif_bmc->response_buf[idx], > + ssif_bmc->response.payload + 1 + ssif_bmc->nbytes_processed, > + response_len); > + break; > + > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > + > + ssif_bmc->nbytes_processed += response_len; > +} > + > +/* Process the IPMI response that will be read by master */ > +static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 *buf; > + u8 pec_len, addr, len; > + u8 pec = 0; > + > + pec_len = ssif_bmc->pec_support ? 1 : 0; > + /* PEC - Start Read Address */ > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + pec = i2c_smbus_pec(pec, &addr, 1); > + /* PEC - SSIF Command */ > + pec = i2c_smbus_pec(pec, &ssif_bmc->smbus_cmd, 1); > + /* PEC - Restart Write Address */ > + addr = addr | 0x01; > + pec = i2c_smbus_pec(pec, &addr, 1); > + > + if (ssif_bmc->is_singlepart_read) { > + /* Single-part Read processing */ > + buf = (u8 *)&ssif_bmc->response; > + > + if (ssif_bmc->response.len && ssif_bmc->msg_idx < ssif_bmc->response.len) { > + ssif_bmc->msg_idx++; > + *val = buf[ssif_bmc->msg_idx]; > + } else if (ssif_bmc->response.len && ssif_bmc->msg_idx == ssif_bmc->response.len) { > + ssif_bmc->msg_idx++; > + *val = i2c_smbus_pec(pec, buf, ssif_msg_len(&ssif_bmc->response)); > + } else { I thought for a second that this was wrong, but I think it's correct. Supply zero if you don't have data. > + *val = 0; > + } > + /* Invalidate response buffer to denote it is sent */ > + if (ssif_bmc->msg_idx + 1 >= (ssif_msg_len(&ssif_bmc->response) + pec_len)) > + complete_response(ssif_bmc); > + } else { > + /* Multi-part Read processing */ You don't check the length here like you did above. I think that's required. > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_MULTIPART_READ_START: > + case SSIF_IPMI_MULTIPART_READ_MIDDLE: > + buf = (u8 *)&ssif_bmc->response_buf; > + *val = buf[ssif_bmc->msg_idx]; > + ssif_bmc->msg_idx++; > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > + > + len = (ssif_bmc->block_num == 0xFF) ? > + ssif_bmc->remain_len + 1 : MAX_PAYLOAD_PER_TRANSACTION; > + if (ssif_bmc->msg_idx == (len + 1)) { > + pec = i2c_smbus_pec(pec, &len, 1); > + *val = i2c_smbus_pec(pec, ssif_bmc->response_buf, len); > + } > + /* Invalidate response buffer to denote last response is sent */ > + if (ssif_bmc->block_num == 0xFF && > + ssif_bmc->msg_idx > (ssif_bmc->remain_len + pec_len)) { > + complete_response(ssif_bmc); > + } > + } > +} > + > +static void handle_write_received(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 *buf = (u8 *)&ssif_bmc->request; > + > + if (ssif_bmc->msg_idx >= sizeof(struct ssif_msg)) > + return; > + > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_SINGLEPART_WRITE: > + buf[ssif_bmc->msg_idx - 1] = *val; > + ssif_bmc->msg_idx++; > + > + break; > + case SSIF_IPMI_MULTIPART_WRITE_START: > + if (ssif_bmc->msg_idx == 1) > + ssif_bmc->request.len = 0; > + > + fallthrough; > + case SSIF_IPMI_MULTIPART_WRITE_MIDDLE: > + /* The len should always be 32 */ > + if (ssif_bmc->msg_idx == 1 && *val != MAX_PAYLOAD_PER_TRANSACTION) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid Multipart Write len"); > + > + fallthrough; > + case SSIF_IPMI_MULTIPART_WRITE_END: > + /* Multi-part write, 2nd byte received is length */ > + if (ssif_bmc->msg_idx == 1) { > + if (*val > MAX_PAYLOAD_PER_TRANSACTION) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid Multipart Write End len"); > + > + ssif_bmc->request.len += *val; > + ssif_bmc->recv_len = *val; > + > + /* request len should never exceeded 255 bytes */ > + if (ssif_bmc->request.len > 255) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid request len"); > + > + } else { You check msg_idx above, but I'm not sure that check will cover this operation. > + buf[ssif_bmc->msg_idx - 1 + > + ssif_bmc->request.len - ssif_bmc->recv_len] = *val; > + } > + > + ssif_bmc->msg_idx++; > + > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > +} > + > +static bool validate_request(struct ssif_bmc_ctx *ssif_bmc) > +{ > + u8 rpec = 0, cpec = 0; > + bool ret = true; > + u8 addr, index; > + u8 *buf; > + Is there any length validation for using buf below? It looks like you are accessing without checking length, but maybe I missed something. > + buf = (u8 *)&ssif_bmc->request; > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_SINGLEPART_WRITE: > + if ((ssif_bmc->msg_idx - 1) == ssif_msg_len(&ssif_bmc->request)) { > + /* PEC is not included */ > + ssif_bmc->pec_support = false; > + ret = true; > + goto exit; > + } > + > + if ((ssif_bmc->msg_idx - 1) != (ssif_msg_len(&ssif_bmc->request) + 1)) { > + dev_err(&ssif_bmc->client->dev, "Error: Unexpected length received %d\n", > + ssif_msg_len(&ssif_bmc->request)); > + ret = false; > + goto exit; > + } > + > + /* PEC is included */ > + ssif_bmc->pec_support = true; > + rpec = buf[ssif_bmc->msg_idx - 2]; > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + cpec = i2c_smbus_pec(cpec, &addr, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->smbus_cmd, 1); > + cpec = i2c_smbus_pec(cpec, buf, ssif_msg_len(&ssif_bmc->request)); > + if (rpec != cpec) { > + dev_err(&ssif_bmc->client->dev, "Bad PEC 0x%02x vs. 0x%02x\n", rpec, cpec); > + ret = false; > + } > + > + break; > + case SSIF_IPMI_MULTIPART_WRITE_START: > + case SSIF_IPMI_MULTIPART_WRITE_MIDDLE: > + case SSIF_IPMI_MULTIPART_WRITE_END: > + index = ssif_bmc->request.len - ssif_bmc->recv_len; > + if ((ssif_bmc->msg_idx - 1 + index) == ssif_msg_len(&ssif_bmc->request)) { > + /* PEC is not included */ > + ssif_bmc->pec_support = false; > + ret = true; > + goto exit; > + } > + > + if ((ssif_bmc->msg_idx - 1 + index) != (ssif_msg_len(&ssif_bmc->request) + 1)) { > + dev_err(&ssif_bmc->client->dev, "Error: Unexpected length received %d\n", > + ssif_msg_len(&ssif_bmc->request)); > + ret = false; > + goto exit; > + } > + > + /* PEC is included */ > + ssif_bmc->pec_support = true; > + rpec = buf[ssif_bmc->msg_idx - 2 + index]; > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + cpec = i2c_smbus_pec(cpec, &addr, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->smbus_cmd, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->recv_len, 1); Just curious, I'm not sure the client side PEC in the Linux driver has ever been validated. Have you tested both sides? > + /* As SMBus specification does not allow the length > + * (byte count) in the Write-Block protocol to be zero. > + * Therefore, it is illegal to have the last Middle > + * transaction in the sequence carry 32-byte and have > + * a length of ‘0’ in the End transaction. > + * But some users may try to use this way and we should > + * prevent ssif_bmc driver broken in this case. > + */ > + if (ssif_bmc->recv_len != 0) > + cpec = i2c_smbus_pec(cpec, buf + 1 + index, ssif_bmc->recv_len); > + > + if (rpec != cpec) { > + dev_err(&ssif_bmc->client->dev, "Bad PEC 0x%02x vs. 0x%02x\n", rpec, cpec); > + ret = false; > + } > + > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + ret = false; > + break; > + } > + > +exit: > + return ret; > +} > + Just a nit, more a general coding style comment. It's almost always a bad idea to put a negative (unsupported) into a check function. You often end up with something like: if (!unsupported_smbus_cmd(c)).... which looks a little strange. Double negatives can make it hard to follow and lead to mistakes. This one isn't too bad, but sometimes it can be. It's better to do: if (supported_smbus_cmd(c)).... or if (!supported_smbus_cmd(c)).... > +static bool unsupported_smbus_cmd(u8 cmd) > +{ > + if (cmd == SSIF_IPMI_SINGLEPART_READ || > + cmd == SSIF_IPMI_SINGLEPART_WRITE || > + cmd == SSIF_IPMI_MULTIPART_WRITE_START || > + cmd == SSIF_IPMI_MULTIPART_WRITE_MIDDLE || > + cmd == SSIF_IPMI_MULTIPART_WRITE_END || > + cmd == SSIF_IPMI_MULTIPART_READ_START || > + cmd == SSIF_IPMI_MULTIPART_READ_MIDDLE) > + return false; > + > + return true; > +} > + > +static void process_smbus_cmd(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + /* SMBUS command can vary (single or multi-part) */ > + ssif_bmc->smbus_cmd = *val; > + ssif_bmc->msg_idx++; > + > + if (unsupported_smbus_cmd(*val)) > + dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus command"); > + > + if (*val == SSIF_IPMI_SINGLEPART_WRITE || > + *val == SSIF_IPMI_MULTIPART_WRITE_START) { > + /* > + * The response maybe not come in-time, causing host SSIF driver > + * to timeout and resend a new request. In such case check for > + * pending response and clear it > + */ > + if (ssif_bmc->response_in_progress) > + complete_response(ssif_bmc); > + } > +} > + > +static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_RES_SENDING) { > + ssif_bmc->state = SSIF_BAD_SMBUS; > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected READ REQUESTED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_RES_SENDING; > + } > + > + ssif_bmc->msg_idx = 0; > + if (ssif_bmc->is_singlepart_read) > + *val = ssif_bmc->response.len; > + else > + set_multipart_response_buffer(ssif_bmc, val); > +} > + > +static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_SMBUS_CMD) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected READ PROCESSED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } > + > + handle_read_processed(ssif_bmc, val); > +} > + > +static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + ssif_bmc->msg_idx = 0; > + > + if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_START; > + > + } else if (ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_RES_SENDING) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected WRITE REQUEST in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } > +} > + > +static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_RES_SENDING) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected WRITE RECEIVED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } else if (ssif_bmc->state == SSIF_START) { > + ssif_bmc->state = SSIF_SMBUS_CMD; > + } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_REQ_RECVING; > + } > + > + /* This is response sending state */ > + if (ssif_bmc->state == SSIF_REQ_RECVING) > + handle_write_received(ssif_bmc, val); > + else if (ssif_bmc->state == SSIF_SMBUS_CMD) > + process_smbus_cmd(ssif_bmc, val); > +} > + > +static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_SMBUS_CMD) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected SLAVE STOP in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_BAD_SMBUS) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s received SLAVE STOP from bad state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_REQ_RECVING) { > + /* A BMC that receives an invalid request drop the data for the write > + * transaction and any further transactions (read or write) until > + * the next valid read or write Start transaction is received > + */ > + if (!validate_request(ssif_bmc)) > + dev_err(&ssif_bmc->client->dev, "Error: invalid pec\n"); > + else if (ssif_bmc->smbus_cmd == SSIF_IPMI_SINGLEPART_WRITE || > + ssif_bmc->smbus_cmd == SSIF_IPMI_MULTIPART_WRITE_END) > + handle_request(ssif_bmc); > + } > + > + ssif_bmc->state = SSIF_READY; > + /* Reset message index */ > + ssif_bmc->msg_idx = 0; > +} > + > +/* > + * Callback function to handle I2C slave events > + */ > +static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8 *val) > +{ > + unsigned long flags; > + struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); > + int ret = 0; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + > + switch (event) { > + case I2C_SLAVE_READ_REQUESTED: > + on_read_requested_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_WRITE_REQUESTED: > + on_write_requested_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_READ_PROCESSED: > + on_read_processed_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_WRITE_RECEIVED: > + on_write_received_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_STOP: > + on_stop_event(ssif_bmc, val); > + break; > + > + default: > + dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n"); > + break; > + } > + > + if (ssif_bmc->busy) > + ret = -EBUSY; > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + return ret; > +} > + > +static int ssif_bmc_probe(struct i2c_client *client, const struct i2c_device_id *id) > +{ > + struct ssif_bmc_ctx *ssif_bmc; > + int ret; > + > + ssif_bmc = devm_kzalloc(&client->dev, sizeof(*ssif_bmc), GFP_KERNEL); > + if (!ssif_bmc) > + return -ENOMEM; > + > + spin_lock_init(&ssif_bmc->lock); > + > + init_waitqueue_head(&ssif_bmc->wait_queue); > + ssif_bmc->request_available = false; > + ssif_bmc->response_in_progress = false; > + ssif_bmc->busy = false; > + ssif_bmc->response_timer_inited = false; > + > + /* Register misc device interface */ > + ssif_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; > + ssif_bmc->miscdev.name = DEVICE_NAME; > + ssif_bmc->miscdev.fops = &ssif_bmc_fops; > + ssif_bmc->miscdev.parent = &client->dev; > + ret = misc_register(&ssif_bmc->miscdev); > + if (ret) > + goto out; > + > + ssif_bmc->client = client; > + ssif_bmc->client->flags |= I2C_CLIENT_SLAVE; > + > + /* Register I2C slave */ > + i2c_set_clientdata(client, ssif_bmc); > + ret = i2c_slave_register(client, ssif_bmc_cb); > + if (ret) { > + misc_deregister(&ssif_bmc->miscdev); > + goto out; > + } > + > + return 0; > +out: > + devm_kfree(&client->dev, ssif_bmc); > + return ret; > +} > + > +static int ssif_bmc_remove(struct i2c_client *client) > +{ > + struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); > + > + i2c_slave_unregister(client); > + misc_deregister(&ssif_bmc->miscdev); > + > + return 0; > +} > + > +static const struct of_device_id ssif_bmc_match[] = { > + { .compatible = "ampere,ssif-bmc" }, > + { }, > +}; > + > +static const struct i2c_device_id ssif_bmc_id[] = { > + { DEVICE_NAME, 0 }, > + { }, > +}; > + > +MODULE_DEVICE_TABLE(i2c, ssif_bmc_id); > + > +static struct i2c_driver ssif_bmc_driver = { > + .driver = { > + .name = DEVICE_NAME, > + .of_match_table = ssif_bmc_match, > + }, > + .probe = ssif_bmc_probe, > + .remove = ssif_bmc_remove, > + .id_table = ssif_bmc_id, > +}; > + > +module_i2c_driver(ssif_bmc_driver); > + > +MODULE_AUTHOR("Quan Nguyen "); > +MODULE_AUTHOR("Chuong Tran "); > +MODULE_DESCRIPTION("Linux device driver of the BMC IPMI SSIF interface."); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/char/ipmi/ssif_bmc.h b/drivers/char/ipmi/ssif_bmc.h > new file mode 100644 > index 000000000000..9a26f3c252cc > --- /dev/null > +++ b/drivers/char/ipmi/ssif_bmc.h > @@ -0,0 +1,102 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * The driver for BMC side of SSIF interface > + * > + * Copyright (c) 2022, Ampere Computing LLC > + * > + */ > +#ifndef __SSIF_BMC_H__ > +#define __SSIF_BMC_H__ > + > +#define DEVICE_NAME "ipmi-ssif-host" > + > +#define GET_8BIT_ADDR(addr_7bit) (((addr_7bit) << 1) & 0xff) > + > +/* A standard SMBus Transaction is limited to 32 data bytes */ > +#define MAX_PAYLOAD_PER_TRANSACTION 32 > + > +#define MAX_IPMI_DATA_PER_START_TRANSACTION 30 > +#define MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION 31 > + > +#define SSIF_IPMI_SINGLEPART_WRITE 0x2 > +#define SSIF_IPMI_SINGLEPART_READ 0x3 > +#define SSIF_IPMI_MULTIPART_WRITE_START 0x6 > +#define SSIF_IPMI_MULTIPART_WRITE_MIDDLE 0x7 > +#define SSIF_IPMI_MULTIPART_WRITE_END 0x8 > +#define SSIF_IPMI_MULTIPART_READ_START 0x3 > +#define SSIF_IPMI_MULTIPART_READ_MIDDLE 0x9 > + > +#define MSG_PAYLOAD_LEN_MAX 252 > +/* > + * IPMI 2.0 Spec, section 12.7 SSIF Timing, > + * Request-to-Response Time is T6max(250ms) - T1max(20ms) - 3ms = 227ms > + * Recover ssif_bmc from busy state if it takes upto 500ms > + */ > +#define RESPONSE_TIMEOUT 500 /* ms */ > + > +struct ssif_msg { > + u8 len; > + u8 netfn_lun; > + u8 cmd; > + u8 payload[MSG_PAYLOAD_LEN_MAX]; > +} __packed; > + > +static inline u32 ssif_msg_len(struct ssif_msg *ssif_msg) > +{ > + return ssif_msg->len + 1; > +} > + > +/* > + * SSIF internal states: > + * SSIF_READY 0x00 : Ready state > + * SSIF_START 0x01 : Start smbus transaction > + * SSIF_SMBUS_CMD 0x02 : Received SMBus command > + * SSIF_REQ_RECVING 0x03 : Receiving request > + * SSIF_RES_SENDING 0x04 : Sending response > + * SSIF_BAD_SMBUS 0x05 : Bad SMbus transaction > + */ > +enum ssif_state { > + SSIF_READY, > + SSIF_START, > + SSIF_SMBUS_CMD, > + SSIF_REQ_RECVING, > + SSIF_RES_SENDING, > + SSIF_BAD_SMBUS, > + SSIF_STATE_MAX > +}; > + > +struct ssif_bmc_ctx { > + struct i2c_client *client; > + struct miscdevice miscdev; > + size_t msg_idx; > + bool pec_support; > + /* ssif bmc spinlock */ > + spinlock_t lock; > + wait_queue_head_t wait_queue; > + u8 running; > + enum ssif_state state; > + u8 smbus_cmd; > + /* Timeout waiting for response */ > + struct timer_list response_timer; > + bool response_timer_inited; > + /* Flag to identify a Multi-part Read Transaction */ > + bool is_singlepart_read; > + u8 nbytes_processed; > + u8 remain_len; > + u8 recv_len; > + /* Block Number of a Multi-part Read Transaction */ > + u8 block_num; > + bool request_available; > + bool response_in_progress; > + bool busy; > + /* Response buffer for Multi-part Read Transaction */ > + u8 response_buf[MAX_PAYLOAD_PER_TRANSACTION]; > + struct ssif_msg response; > + struct ssif_msg request; > +}; > + > +static inline struct ssif_bmc_ctx *to_ssif_bmc(struct file *file) > +{ > + return container_of(file->private_data, struct ssif_bmc_ctx, miscdev); > +} > +#endif /* __SSIF_BMC_H__ */ > -- > 2.35.1 > > From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.ozlabs.org (lists.ozlabs.org [112.213.38.117]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 9CA33C433F5 for ; Fri, 11 Mar 2022 01:20:42 +0000 (UTC) Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4KF7RF0spvz2yPF for ; Fri, 11 Mar 2022 12:20:41 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=SuZFZlMl; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:4864:20::f2c; helo=mail-qv1-xf2c.google.com; envelope-from=tcminyard@gmail.com; receiver=) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20210112 header.b=SuZFZlMl; dkim-atps=neutral Received: from mail-qv1-xf2c.google.com (mail-qv1-xf2c.google.com [IPv6:2607:f8b0:4864:20::f2c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 4KF7QJ4Zfwz2yL6; Fri, 11 Mar 2022 12:19:50 +1100 (AEDT) Received: by mail-qv1-xf2c.google.com with SMTP id gm1so5936579qvb.7; Thu, 10 Mar 2022 17:19:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=sender:date:from:to:cc:subject:message-id:reply-to:references :mime-version:content-disposition:content-transfer-encoding :in-reply-to; bh=BWLuHkhuLRrMWa8N+fItg7v8ydmq3TO/fXGntCYkO6Y=; b=SuZFZlMlkQQ4S0kNlTh30UByunM4Zhvp+V3yvzH/qoDsqzjS5ejE4svpd4iljAoCpe 05CWTKkCU4/1y1Oer0zWzRR+rzc9qsKnfi/+O+5APKfE5w4C6685gA6L5M+eQr8VcMRU WoxEtvQGfQVnstvr0PVvn/XMJfnxpvQ7ppOqx/ijctdDbEWfeeu8sLFlCWe8zd8SiiR8 bcyxFgqVt448XrBh1iTQACZZIpaZ+TQLBirITlFNPLl+RpX+lct2fy7UvwuNop1xCfvd IbXt454ECUwlSL6Hm9jm0BUcxvNpK46BayRtG2HvJGrt98Qjx/K64P+ZwN3DovFmbFzR labg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:sender:date:from:to:cc:subject:message-id :reply-to:references:mime-version:content-disposition :content-transfer-encoding:in-reply-to; bh=BWLuHkhuLRrMWa8N+fItg7v8ydmq3TO/fXGntCYkO6Y=; b=ccul9zcJ1aojPMWAx364LaszVSciJMHbELxWwF+tFFl58OouVz4J0Muktdz6hE7I7T AVJeJOrKAYXDoWEfG9Vbf9xum4Kl5dZa0u0jy4lQaYXTQNFPN3hagIMPQ6sy7QdrSZ3n sGyCEI1pQYM26oVHv92FjOVe1aeTFS2J2ufmx0aiIj6xyY92RXpw7wOXnZeuF8m7fWrN G6uGYQk7kLvNdxUs5GQHVkgfbR1F/e887CDm7AKU26M/0Xuf/L/1bHDiJLecFzexMgPl R5F+57uD3AvEY70A6S5c2/qcAdDXFYVqMScmQ3ZeLUgESUQsyfh5DR2kzn3MyPgYXHLi R+Jg== X-Gm-Message-State: AOAM532+nxPe0ZJ2T0Miz52hzcSWAxuAb2oV1qNLekdQZuoz88klpcYl jBHuURbilcqQIhDRKZ7xDA== X-Google-Smtp-Source: ABdhPJwL3ZWJehYxMf6yO6S5Io+T49q/ukI/FjtuleC1YSlSekImxG5KL9P6qWMS6cqySdCIpaq6Ig== X-Received: by 2002:a05:6214:76b:b0:435:d12f:628f with SMTP id f11-20020a056214076b00b00435d12f628fmr5403209qvz.53.1646961585262; Thu, 10 Mar 2022 17:19:45 -0800 (PST) Received: from serve.minyard.net (serve.minyard.net. [2001:470:b8f6:1b::1]) by smtp.gmail.com with ESMTPSA id z6-20020ae9c106000000b0067d3b9ef387sm2727890qki.28.2022.03.10.17.19.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Mar 2022 17:19:44 -0800 (PST) Received: from minyard.net (unknown [IPv6:2001:470:b8f6:1b:dc9d:d155:a010:ac11]) by serve.minyard.net (Postfix) with ESMTPSA id 2CC7E1800BC; Fri, 11 Mar 2022 01:19:43 +0000 (UTC) Date: Thu, 10 Mar 2022 19:19:42 -0600 From: Corey Minyard To: Quan Nguyen Subject: Re: [PATCH v6 1/4] ipmi: ssif_bmc: Add SSIF BMC driver Message-ID: <20220311011942.GX3457@minyard.net> References: <20220310114119.13736-1-quan@os.amperecomputing.com> <20220310114119.13736-2-quan@os.amperecomputing.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20220310114119.13736-2-quan@os.amperecomputing.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: minyard@acm.org Cc: devicetree@vger.kernel.org, linux-aspeed@lists.ozlabs.org, Krzysztof Kozlowski , Andrew Jeffery , openbmc@lists.ozlabs.org, "Thang Q . Nguyen" , Brendan Higgins , linux-kernel@vger.kernel.org, Phong Vo , Wolfram Sang , Rob Herring , Joel Stanley , openipmi-developer@lists.sourceforge.net, Open Source Submission , linux-arm-kernel@lists.infradead.org, linux-i2c@vger.kernel.org Errors-To: openbmc-bounces+openbmc=archiver.kernel.org@lists.ozlabs.org Sender: "openbmc" On Thu, Mar 10, 2022 at 06:41:16PM +0700, Quan Nguyen wrote: > The SMBus system interface (SSIF) IPMI BMC driver can be used to perform > in-band IPMI communication with their host in management (BMC) side. Thanks for the update, and removing slave_enable(). I spent some quality time with this, and it looks like there are places where you don't check lengths before indexing arrays. I've marked several of these, but you need to check it all through this. I have a few other comments, too. -corey > > Signed-off-by: Quan Nguyen > --- > v6: > + Drop the use of slave_enable() [Wolfram] > + Make i2c-aspeed to issue RxCmdLast command on all > I2C_SLAVE_WRITE_REQUESTED event to assert NAK when slave busy [Quan] > + Make i2c slave to return -EBUSY when it's busy [Quan] > + Drop the aborting feature as return Completion Code 0xFF may stop > host to retry and make ipmi_ssif.so fails to load [Quan] > + Add timer to recover slave from busy state when no response [Quan] > + Clean request/response buffer appropriately [Quan] > + Add some minor change on error and warning messages [Quan] > > v5: > + None > > v4: > + Send response with Completion code 0xFF when aborting [Quan] > + Added bounding check on SMBus writes and the whole request [Dan] > + Moved buffer to end of struct ssif_bmc_ctx to avoid context > corruption if somehow buffer is written past the end [Dan] > + Return -EINVAL if userspace buffer too small, dont > silence truncate [Corey, Joel] > + Not necessary to check NONBLOCK in lock [Corey] > + Enforce one user at a time [Joel] > + Reject write with invalid response length from userspace [Corey] > + Add state machines for better ssif bmc state handling [Quan] > + Drop ssif_bmc_aspeed.c and make ssif_bmc.c is generic > SSIF BMC driver [Quan] > + Change compatible string "aspeed,ast2500-ssif-bmc" to > "ampere,ssif-bmc" [Quan] > + Abort current request with invalid SMBus write or > invalid command [Quan] > + Abort all request if there is pending response [Quan] > + Changed validate_pec() to validate_request() [Quan] > + Add unsupported_smbus_cmd() to handle unknown SMBus command [Quan] > + Print internal state string for ease investigating issue [Quan] > + Move to READY state on SLAVE_STOP event [Quan] > + Change initilize_transfer() to process_smbus_cmd() [Quan] > + Introduce functions for each slave event [Quan] > > v3: > + Removed redundant license info [Joel] > + Switched to use traditional if-else [Joel] > + Removed unused ssif_bmc_ioctl() [Joel] > + Made handle_request()/complete_response() to return void [Joel] > + Refactored send_ssif_bmc_response() and > receive_ssif_bmc_request() [Corey] > + Removed mutex [Corey] > + Use spin_lock/unlock_irqsave/restore in callback [Corey] > + Removed the unnecessary memset [Corey] > + Switch to use dev_err() [Corey] > > v2: > + Fixed compiling error with COMPILE_TEST for arc > > drivers/char/ipmi/Kconfig | 11 + > drivers/char/ipmi/Makefile | 1 + > drivers/char/ipmi/ssif_bmc.c | 791 +++++++++++++++++++++++++++++++++++ > drivers/char/ipmi/ssif_bmc.h | 102 +++++ > 4 files changed, 905 insertions(+) > create mode 100644 drivers/char/ipmi/ssif_bmc.c > create mode 100644 drivers/char/ipmi/ssif_bmc.h > > diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig > index b061e6b513ed..18a89093d64e 100644 > --- a/drivers/char/ipmi/Kconfig > +++ b/drivers/char/ipmi/Kconfig > @@ -169,6 +169,17 @@ config ASPEED_BT_IPMI_BMC > found on Aspeed SOCs (AST2400 and AST2500). The driver > implements the BMC side of the BT interface. > > +config SSIF_IPMI_BMC > + tristate "SSIF IPMI BMC driver" > + select I2C > + select I2C_SLAVE > + help > + This enables the IPMI SMBus system interface (SSIF) at the > + management (BMC) side. > + > + The driver implements the BMC side of the SMBus system > + interface (SSIF). > + > config IPMB_DEVICE_INTERFACE > tristate 'IPMB Interface handler' > depends on I2C > diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile > index 7ce790efad92..cb6138b8ded9 100644 > --- a/drivers/char/ipmi/Makefile > +++ b/drivers/char/ipmi/Makefile > @@ -30,3 +30,4 @@ obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o > obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o > obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o > obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o > +obj-$(CONFIG_SSIF_IPMI_BMC) += ssif_bmc.o > diff --git a/drivers/char/ipmi/ssif_bmc.c b/drivers/char/ipmi/ssif_bmc.c > new file mode 100644 > index 000000000000..62db97773654 > --- /dev/null > +++ b/drivers/char/ipmi/ssif_bmc.c > @@ -0,0 +1,791 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * The driver for BMC side of SSIF interface > + * > + * Copyright (c) 2022, Ampere Computing LLC > + * > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "ssif_bmc.h" > + > +static const char *state_to_string(enum ssif_state state) > +{ > + switch (state) { > + case SSIF_READY: > + return "SSIF_READY"; > + case SSIF_START: > + return "SSIF_START"; > + case SSIF_SMBUS_CMD: > + return "SSIF_SMBUS_CMD"; > + case SSIF_REQ_RECVING: > + return "SSIF_REQ_RECVING"; > + case SSIF_RES_SENDING: > + return "SSIF_RES_SENDING"; > + case SSIF_BAD_SMBUS: > + return "SSIF_BAD_SMBUS"; > + default: > + return "SSIF_STATE_UNKNOWN"; > + } > +} > + > +/* Handle SSIF message that will be sent to user */ > +static ssize_t ssif_bmc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + struct ssif_msg msg; > + unsigned long flags; > + ssize_t ret; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + while (!ssif_bmc->request_available) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + if (file->f_flags & O_NONBLOCK) > + return -EAGAIN; > + ret = wait_event_interruptible(ssif_bmc->wait_queue, > + ssif_bmc->request_available); > + if (ret) > + return ret; > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + } > + > + if (count < min_t(ssize_t, ssif_msg_len(&ssif_bmc->request), sizeof(struct ssif_msg))) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + ret = -EINVAL; > + } else { > + count = min_t(ssize_t, ssif_msg_len(&ssif_bmc->request), sizeof(struct ssif_msg)); > + memcpy(&msg, &ssif_bmc->request, count); > + ssif_bmc->request_available = false; > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + ret = copy_to_user(buf, &msg, count); > + } > + > + return (ret < 0) ? ret : count; > +} > + > +/* Handle SSIF message that is written by user */ > +static ssize_t ssif_bmc_write(struct file *file, const char __user *buf, size_t count, > + loff_t *ppos) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + struct ssif_msg msg; > + unsigned long flags; > + ssize_t ret; > + > + if (count > sizeof(struct ssif_msg)) > + return -EINVAL; > + > + ret = copy_from_user(&msg, buf, count); > + if (ret) > + return ret; > + > + if (!msg.len || count < ssif_msg_len(&msg)) > + return -EINVAL; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + while (ssif_bmc->response_in_progress) { > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + if (file->f_flags & O_NONBLOCK) > + return -EAGAIN; > + ret = wait_event_interruptible(ssif_bmc->wait_queue, > + !ssif_bmc->response_in_progress); > + if (ret) > + return ret; > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + } > + > + memcpy(&ssif_bmc->response, &msg, count); > + ssif_bmc->is_singlepart_read = (ssif_msg_len(&msg) <= MAX_PAYLOAD_PER_TRANSACTION + 1); > + > + ssif_bmc->response_in_progress = true; > + > + /* ssif_bmc not busy */ > + ssif_bmc->busy = false; > + > + if (ssif_bmc->response_timer_inited) { > + del_timer(&ssif_bmc->response_timer); > + ssif_bmc->response_timer_inited = false; > + } > + /* Clean old request buffer */ > + memset(&ssif_bmc->request, 0, sizeof(struct ssif_msg)); > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + return count; > +} > + > +static int ssif_bmc_open(struct inode *inode, struct file *file) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + int ret = 0; > + > + spin_lock_irq(&ssif_bmc->lock); > + if (!ssif_bmc->running) > + ssif_bmc->running = 1; > + else > + ret = -EBUSY; > + spin_unlock_irq(&ssif_bmc->lock); > + > + return ret; > +} > + > +static unsigned int ssif_bmc_poll(struct file *file, poll_table *wait) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + unsigned int mask = 0; > + > + poll_wait(file, &ssif_bmc->wait_queue, wait); > + > + spin_lock_irq(&ssif_bmc->lock); > + /* The request is available, userspace application can get the request */ > + if (ssif_bmc->request_available) > + mask |= POLLIN; > + > + spin_unlock_irq(&ssif_bmc->lock); > + > + return mask; > +} > + > +static int ssif_bmc_release(struct inode *inode, struct file *file) > +{ > + struct ssif_bmc_ctx *ssif_bmc = to_ssif_bmc(file); > + > + spin_lock_irq(&ssif_bmc->lock); > + ssif_bmc->running = 0; > + spin_unlock_irq(&ssif_bmc->lock); > + > + return 0; > +} > + > +/* > + * System calls to device interface for user apps > + */ > +static const struct file_operations ssif_bmc_fops = { > + .owner = THIS_MODULE, > + .open = ssif_bmc_open, > + .read = ssif_bmc_read, > + .write = ssif_bmc_write, > + .release = ssif_bmc_release, > + .poll = ssif_bmc_poll, > +}; > + > +/* Called with ssif_bmc->lock held. */ > +static void complete_response(struct ssif_bmc_ctx *ssif_bmc) > +{ > + /* Invalidate response in buffer to denote it having been sent. */ > + ssif_bmc->response.len = 0; > + ssif_bmc->response_in_progress = false; > + ssif_bmc->nbytes_processed = 0; > + ssif_bmc->remain_len = 0; > + ssif_bmc->busy = false; > + memset(&ssif_bmc->response_buf, 0, MAX_PAYLOAD_PER_TRANSACTION); > + wake_up_all(&ssif_bmc->wait_queue); > +} > + > +static void response_timeout(struct timer_list *t) > +{ > + struct ssif_bmc_ctx *ssif_bmc = from_timer(ssif_bmc, t, response_timer); > + unsigned long flags; > + Is there a possible race here? The timeout can happen at the same time as a received message, will something bad happen if that's the case? > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + > + /* Recover ssif_bmc from busy */ > + ssif_bmc->busy = false; > + del_timer(&ssif_bmc->response_timer); You don't need to delete the timer, it's in the timeout. > + ssif_bmc->response_timer_inited = false; > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > +} > + > +/* Called with ssif_bmc->lock held. */ > +static void handle_request(struct ssif_bmc_ctx *ssif_bmc) > +{ > + /* set ssif_bmc to busy waiting for response */ > + ssif_bmc->busy = true; > + > + /* Request message is available to process */ > + ssif_bmc->request_available = true; > + > + /* Clean old response buffer */ > + memset(&ssif_bmc->response, 0, sizeof(struct ssif_msg)); > + > + /* This is the new READ request.*/ > + wake_up_all(&ssif_bmc->wait_queue); > + > + /* Armed timer to recover slave from busy state in case of no response */ > + if (!ssif_bmc->response_timer_inited) { > + timer_setup(&ssif_bmc->response_timer, response_timeout, 0); > + ssif_bmc->response_timer_inited = true; > + } > + mod_timer(&ssif_bmc->response_timer, jiffies + msecs_to_jiffies(RESPONSE_TIMEOUT)); > +} > + > +static void set_multipart_response_buffer(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 response_len = 0; > + int idx = 0; > + u8 data_len; > + > + data_len = ssif_bmc->response.len; > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_MULTIPART_READ_START: > + /* > + * Read Start length is 32 bytes. > + * Read Start transfer first 30 bytes of IPMI response > + * and 2 special code 0x00, 0x01. > + */ > + *val = MAX_PAYLOAD_PER_TRANSACTION; > + ssif_bmc->remain_len = data_len - MAX_IPMI_DATA_PER_START_TRANSACTION; > + ssif_bmc->block_num = 0; Do you need to validate the data length before using this? This applies for lots of places through here. > + > + ssif_bmc->response_buf[idx++] = 0x00; /* Start Flag */ > + ssif_bmc->response_buf[idx++] = 0x01; /* Start Flag */ > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.netfn_lun; > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.cmd; > + ssif_bmc->response_buf[idx++] = ssif_bmc->response.payload[0]; > + > + response_len = MAX_PAYLOAD_PER_TRANSACTION - idx; > + > + memcpy(&ssif_bmc->response_buf[idx], &ssif_bmc->response.payload[1], > + response_len); > + break; > + > + case SSIF_IPMI_MULTIPART_READ_MIDDLE: > + /* > + * IPMI READ Middle or READ End messages can carry up to 31 bytes > + * IPMI data plus block number byte. > + */ > + if (ssif_bmc->remain_len < MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION) { > + /* > + * This is READ End message > + * Return length is the remaining response data length > + * plus block number > + * Block number 0xFF is to indicate this is last message > + * > + */ > + *val = ssif_bmc->remain_len + 1; > + ssif_bmc->block_num = 0xFF; > + ssif_bmc->response_buf[idx++] = ssif_bmc->block_num; > + response_len = ssif_bmc->remain_len; > + /* Clean the buffer */ > + memset(&ssif_bmc->response_buf[idx], 0, MAX_PAYLOAD_PER_TRANSACTION - idx); > + } else { > + /* > + * This is READ Middle message > + * Response length is the maximum SMBUS transfer length > + * Block number byte is incremented > + * Return length is maximum SMBUS transfer length > + */ > + *val = MAX_PAYLOAD_PER_TRANSACTION; > + ssif_bmc->remain_len -= MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION; > + response_len = MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION; > + ssif_bmc->response_buf[idx++] = ssif_bmc->block_num; > + ssif_bmc->block_num++; > + } > + > + memcpy(&ssif_bmc->response_buf[idx], > + ssif_bmc->response.payload + 1 + ssif_bmc->nbytes_processed, > + response_len); > + break; > + > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > + > + ssif_bmc->nbytes_processed += response_len; > +} > + > +/* Process the IPMI response that will be read by master */ > +static void handle_read_processed(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 *buf; > + u8 pec_len, addr, len; > + u8 pec = 0; > + > + pec_len = ssif_bmc->pec_support ? 1 : 0; > + /* PEC - Start Read Address */ > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + pec = i2c_smbus_pec(pec, &addr, 1); > + /* PEC - SSIF Command */ > + pec = i2c_smbus_pec(pec, &ssif_bmc->smbus_cmd, 1); > + /* PEC - Restart Write Address */ > + addr = addr | 0x01; > + pec = i2c_smbus_pec(pec, &addr, 1); > + > + if (ssif_bmc->is_singlepart_read) { > + /* Single-part Read processing */ > + buf = (u8 *)&ssif_bmc->response; > + > + if (ssif_bmc->response.len && ssif_bmc->msg_idx < ssif_bmc->response.len) { > + ssif_bmc->msg_idx++; > + *val = buf[ssif_bmc->msg_idx]; > + } else if (ssif_bmc->response.len && ssif_bmc->msg_idx == ssif_bmc->response.len) { > + ssif_bmc->msg_idx++; > + *val = i2c_smbus_pec(pec, buf, ssif_msg_len(&ssif_bmc->response)); > + } else { I thought for a second that this was wrong, but I think it's correct. Supply zero if you don't have data. > + *val = 0; > + } > + /* Invalidate response buffer to denote it is sent */ > + if (ssif_bmc->msg_idx + 1 >= (ssif_msg_len(&ssif_bmc->response) + pec_len)) > + complete_response(ssif_bmc); > + } else { > + /* Multi-part Read processing */ You don't check the length here like you did above. I think that's required. > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_MULTIPART_READ_START: > + case SSIF_IPMI_MULTIPART_READ_MIDDLE: > + buf = (u8 *)&ssif_bmc->response_buf; > + *val = buf[ssif_bmc->msg_idx]; > + ssif_bmc->msg_idx++; > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > + > + len = (ssif_bmc->block_num == 0xFF) ? > + ssif_bmc->remain_len + 1 : MAX_PAYLOAD_PER_TRANSACTION; > + if (ssif_bmc->msg_idx == (len + 1)) { > + pec = i2c_smbus_pec(pec, &len, 1); > + *val = i2c_smbus_pec(pec, ssif_bmc->response_buf, len); > + } > + /* Invalidate response buffer to denote last response is sent */ > + if (ssif_bmc->block_num == 0xFF && > + ssif_bmc->msg_idx > (ssif_bmc->remain_len + pec_len)) { > + complete_response(ssif_bmc); > + } > + } > +} > + > +static void handle_write_received(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + u8 *buf = (u8 *)&ssif_bmc->request; > + > + if (ssif_bmc->msg_idx >= sizeof(struct ssif_msg)) > + return; > + > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_SINGLEPART_WRITE: > + buf[ssif_bmc->msg_idx - 1] = *val; > + ssif_bmc->msg_idx++; > + > + break; > + case SSIF_IPMI_MULTIPART_WRITE_START: > + if (ssif_bmc->msg_idx == 1) > + ssif_bmc->request.len = 0; > + > + fallthrough; > + case SSIF_IPMI_MULTIPART_WRITE_MIDDLE: > + /* The len should always be 32 */ > + if (ssif_bmc->msg_idx == 1 && *val != MAX_PAYLOAD_PER_TRANSACTION) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid Multipart Write len"); > + > + fallthrough; > + case SSIF_IPMI_MULTIPART_WRITE_END: > + /* Multi-part write, 2nd byte received is length */ > + if (ssif_bmc->msg_idx == 1) { > + if (*val > MAX_PAYLOAD_PER_TRANSACTION) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid Multipart Write End len"); > + > + ssif_bmc->request.len += *val; > + ssif_bmc->recv_len = *val; > + > + /* request len should never exceeded 255 bytes */ > + if (ssif_bmc->request.len > 255) > + dev_warn(&ssif_bmc->client->dev, > + "Warn: Invalid request len"); > + > + } else { You check msg_idx above, but I'm not sure that check will cover this operation. > + buf[ssif_bmc->msg_idx - 1 + > + ssif_bmc->request.len - ssif_bmc->recv_len] = *val; > + } > + > + ssif_bmc->msg_idx++; > + > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, > + "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + break; > + } > +} > + > +static bool validate_request(struct ssif_bmc_ctx *ssif_bmc) > +{ > + u8 rpec = 0, cpec = 0; > + bool ret = true; > + u8 addr, index; > + u8 *buf; > + Is there any length validation for using buf below? It looks like you are accessing without checking length, but maybe I missed something. > + buf = (u8 *)&ssif_bmc->request; > + switch (ssif_bmc->smbus_cmd) { > + case SSIF_IPMI_SINGLEPART_WRITE: > + if ((ssif_bmc->msg_idx - 1) == ssif_msg_len(&ssif_bmc->request)) { > + /* PEC is not included */ > + ssif_bmc->pec_support = false; > + ret = true; > + goto exit; > + } > + > + if ((ssif_bmc->msg_idx - 1) != (ssif_msg_len(&ssif_bmc->request) + 1)) { > + dev_err(&ssif_bmc->client->dev, "Error: Unexpected length received %d\n", > + ssif_msg_len(&ssif_bmc->request)); > + ret = false; > + goto exit; > + } > + > + /* PEC is included */ > + ssif_bmc->pec_support = true; > + rpec = buf[ssif_bmc->msg_idx - 2]; > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + cpec = i2c_smbus_pec(cpec, &addr, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->smbus_cmd, 1); > + cpec = i2c_smbus_pec(cpec, buf, ssif_msg_len(&ssif_bmc->request)); > + if (rpec != cpec) { > + dev_err(&ssif_bmc->client->dev, "Bad PEC 0x%02x vs. 0x%02x\n", rpec, cpec); > + ret = false; > + } > + > + break; > + case SSIF_IPMI_MULTIPART_WRITE_START: > + case SSIF_IPMI_MULTIPART_WRITE_MIDDLE: > + case SSIF_IPMI_MULTIPART_WRITE_END: > + index = ssif_bmc->request.len - ssif_bmc->recv_len; > + if ((ssif_bmc->msg_idx - 1 + index) == ssif_msg_len(&ssif_bmc->request)) { > + /* PEC is not included */ > + ssif_bmc->pec_support = false; > + ret = true; > + goto exit; > + } > + > + if ((ssif_bmc->msg_idx - 1 + index) != (ssif_msg_len(&ssif_bmc->request) + 1)) { > + dev_err(&ssif_bmc->client->dev, "Error: Unexpected length received %d\n", > + ssif_msg_len(&ssif_bmc->request)); > + ret = false; > + goto exit; > + } > + > + /* PEC is included */ > + ssif_bmc->pec_support = true; > + rpec = buf[ssif_bmc->msg_idx - 2 + index]; > + addr = GET_8BIT_ADDR(ssif_bmc->client->addr); > + cpec = i2c_smbus_pec(cpec, &addr, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->smbus_cmd, 1); > + cpec = i2c_smbus_pec(cpec, &ssif_bmc->recv_len, 1); Just curious, I'm not sure the client side PEC in the Linux driver has ever been validated. Have you tested both sides? > + /* As SMBus specification does not allow the length > + * (byte count) in the Write-Block protocol to be zero. > + * Therefore, it is illegal to have the last Middle > + * transaction in the sequence carry 32-byte and have > + * a length of ‘0’ in the End transaction. > + * But some users may try to use this way and we should > + * prevent ssif_bmc driver broken in this case. > + */ > + if (ssif_bmc->recv_len != 0) > + cpec = i2c_smbus_pec(cpec, buf + 1 + index, ssif_bmc->recv_len); > + > + if (rpec != cpec) { > + dev_err(&ssif_bmc->client->dev, "Bad PEC 0x%02x vs. 0x%02x\n", rpec, cpec); > + ret = false; > + } > + > + break; > + default: > + /* Do not expect to go to this case */ > + dev_err(&ssif_bmc->client->dev, "%s: Unexpected SMBus command 0x%x\n", > + __func__, ssif_bmc->smbus_cmd); > + ret = false; > + break; > + } > + > +exit: > + return ret; > +} > + Just a nit, more a general coding style comment. It's almost always a bad idea to put a negative (unsupported) into a check function. You often end up with something like: if (!unsupported_smbus_cmd(c)).... which looks a little strange. Double negatives can make it hard to follow and lead to mistakes. This one isn't too bad, but sometimes it can be. It's better to do: if (supported_smbus_cmd(c)).... or if (!supported_smbus_cmd(c)).... > +static bool unsupported_smbus_cmd(u8 cmd) > +{ > + if (cmd == SSIF_IPMI_SINGLEPART_READ || > + cmd == SSIF_IPMI_SINGLEPART_WRITE || > + cmd == SSIF_IPMI_MULTIPART_WRITE_START || > + cmd == SSIF_IPMI_MULTIPART_WRITE_MIDDLE || > + cmd == SSIF_IPMI_MULTIPART_WRITE_END || > + cmd == SSIF_IPMI_MULTIPART_READ_START || > + cmd == SSIF_IPMI_MULTIPART_READ_MIDDLE) > + return false; > + > + return true; > +} > + > +static void process_smbus_cmd(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + /* SMBUS command can vary (single or multi-part) */ > + ssif_bmc->smbus_cmd = *val; > + ssif_bmc->msg_idx++; > + > + if (unsupported_smbus_cmd(*val)) > + dev_warn(&ssif_bmc->client->dev, "Warn: Unknown SMBus command"); > + > + if (*val == SSIF_IPMI_SINGLEPART_WRITE || > + *val == SSIF_IPMI_MULTIPART_WRITE_START) { > + /* > + * The response maybe not come in-time, causing host SSIF driver > + * to timeout and resend a new request. In such case check for > + * pending response and clear it > + */ > + if (ssif_bmc->response_in_progress) > + complete_response(ssif_bmc); > + } > +} > + > +static void on_read_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_RES_SENDING) { > + ssif_bmc->state = SSIF_BAD_SMBUS; > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected READ REQUESTED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_RES_SENDING; > + } > + > + ssif_bmc->msg_idx = 0; > + if (ssif_bmc->is_singlepart_read) > + *val = ssif_bmc->response.len; > + else > + set_multipart_response_buffer(ssif_bmc, val); > +} > + > +static void on_read_processed_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_SMBUS_CMD) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected READ PROCESSED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } > + > + handle_read_processed(ssif_bmc, val); > +} > + > +static void on_write_requested_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + ssif_bmc->msg_idx = 0; > + > + if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_START; > + > + } else if (ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_REQ_RECVING || > + ssif_bmc->state == SSIF_RES_SENDING) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected WRITE REQUEST in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } > +} > + > +static void on_write_received_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || ssif_bmc->state == SSIF_RES_SENDING) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected WRITE RECEIVED in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + ssif_bmc->state = SSIF_BAD_SMBUS; > + } else if (ssif_bmc->state == SSIF_START) { > + ssif_bmc->state = SSIF_SMBUS_CMD; > + } else if (ssif_bmc->state == SSIF_SMBUS_CMD) { > + ssif_bmc->state = SSIF_REQ_RECVING; > + } > + > + /* This is response sending state */ > + if (ssif_bmc->state == SSIF_REQ_RECVING) > + handle_write_received(ssif_bmc, val); > + else if (ssif_bmc->state == SSIF_SMBUS_CMD) > + process_smbus_cmd(ssif_bmc, val); > +} > + > +static void on_stop_event(struct ssif_bmc_ctx *ssif_bmc, u8 *val) > +{ > + if (ssif_bmc->state == SSIF_READY || > + ssif_bmc->state == SSIF_START || > + ssif_bmc->state == SSIF_SMBUS_CMD) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s unexpected SLAVE STOP in state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_BAD_SMBUS) { > + dev_warn(&ssif_bmc->client->dev, > + "Warn: %s received SLAVE STOP from bad state=%s\n", > + __func__, state_to_string(ssif_bmc->state)); > + > + } else if (ssif_bmc->state == SSIF_REQ_RECVING) { > + /* A BMC that receives an invalid request drop the data for the write > + * transaction and any further transactions (read or write) until > + * the next valid read or write Start transaction is received > + */ > + if (!validate_request(ssif_bmc)) > + dev_err(&ssif_bmc->client->dev, "Error: invalid pec\n"); > + else if (ssif_bmc->smbus_cmd == SSIF_IPMI_SINGLEPART_WRITE || > + ssif_bmc->smbus_cmd == SSIF_IPMI_MULTIPART_WRITE_END) > + handle_request(ssif_bmc); > + } > + > + ssif_bmc->state = SSIF_READY; > + /* Reset message index */ > + ssif_bmc->msg_idx = 0; > +} > + > +/* > + * Callback function to handle I2C slave events > + */ > +static int ssif_bmc_cb(struct i2c_client *client, enum i2c_slave_event event, u8 *val) > +{ > + unsigned long flags; > + struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); > + int ret = 0; > + > + spin_lock_irqsave(&ssif_bmc->lock, flags); > + > + switch (event) { > + case I2C_SLAVE_READ_REQUESTED: > + on_read_requested_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_WRITE_REQUESTED: > + on_write_requested_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_READ_PROCESSED: > + on_read_processed_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_WRITE_RECEIVED: > + on_write_received_event(ssif_bmc, val); > + break; > + > + case I2C_SLAVE_STOP: > + on_stop_event(ssif_bmc, val); > + break; > + > + default: > + dev_warn(&ssif_bmc->client->dev, "Warn: Unknown i2c slave event\n"); > + break; > + } > + > + if (ssif_bmc->busy) > + ret = -EBUSY; > + > + spin_unlock_irqrestore(&ssif_bmc->lock, flags); > + > + return ret; > +} > + > +static int ssif_bmc_probe(struct i2c_client *client, const struct i2c_device_id *id) > +{ > + struct ssif_bmc_ctx *ssif_bmc; > + int ret; > + > + ssif_bmc = devm_kzalloc(&client->dev, sizeof(*ssif_bmc), GFP_KERNEL); > + if (!ssif_bmc) > + return -ENOMEM; > + > + spin_lock_init(&ssif_bmc->lock); > + > + init_waitqueue_head(&ssif_bmc->wait_queue); > + ssif_bmc->request_available = false; > + ssif_bmc->response_in_progress = false; > + ssif_bmc->busy = false; > + ssif_bmc->response_timer_inited = false; > + > + /* Register misc device interface */ > + ssif_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; > + ssif_bmc->miscdev.name = DEVICE_NAME; > + ssif_bmc->miscdev.fops = &ssif_bmc_fops; > + ssif_bmc->miscdev.parent = &client->dev; > + ret = misc_register(&ssif_bmc->miscdev); > + if (ret) > + goto out; > + > + ssif_bmc->client = client; > + ssif_bmc->client->flags |= I2C_CLIENT_SLAVE; > + > + /* Register I2C slave */ > + i2c_set_clientdata(client, ssif_bmc); > + ret = i2c_slave_register(client, ssif_bmc_cb); > + if (ret) { > + misc_deregister(&ssif_bmc->miscdev); > + goto out; > + } > + > + return 0; > +out: > + devm_kfree(&client->dev, ssif_bmc); > + return ret; > +} > + > +static int ssif_bmc_remove(struct i2c_client *client) > +{ > + struct ssif_bmc_ctx *ssif_bmc = i2c_get_clientdata(client); > + > + i2c_slave_unregister(client); > + misc_deregister(&ssif_bmc->miscdev); > + > + return 0; > +} > + > +static const struct of_device_id ssif_bmc_match[] = { > + { .compatible = "ampere,ssif-bmc" }, > + { }, > +}; > + > +static const struct i2c_device_id ssif_bmc_id[] = { > + { DEVICE_NAME, 0 }, > + { }, > +}; > + > +MODULE_DEVICE_TABLE(i2c, ssif_bmc_id); > + > +static struct i2c_driver ssif_bmc_driver = { > + .driver = { > + .name = DEVICE_NAME, > + .of_match_table = ssif_bmc_match, > + }, > + .probe = ssif_bmc_probe, > + .remove = ssif_bmc_remove, > + .id_table = ssif_bmc_id, > +}; > + > +module_i2c_driver(ssif_bmc_driver); > + > +MODULE_AUTHOR("Quan Nguyen "); > +MODULE_AUTHOR("Chuong Tran "); > +MODULE_DESCRIPTION("Linux device driver of the BMC IPMI SSIF interface."); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/char/ipmi/ssif_bmc.h b/drivers/char/ipmi/ssif_bmc.h > new file mode 100644 > index 000000000000..9a26f3c252cc > --- /dev/null > +++ b/drivers/char/ipmi/ssif_bmc.h > @@ -0,0 +1,102 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * The driver for BMC side of SSIF interface > + * > + * Copyright (c) 2022, Ampere Computing LLC > + * > + */ > +#ifndef __SSIF_BMC_H__ > +#define __SSIF_BMC_H__ > + > +#define DEVICE_NAME "ipmi-ssif-host" > + > +#define GET_8BIT_ADDR(addr_7bit) (((addr_7bit) << 1) & 0xff) > + > +/* A standard SMBus Transaction is limited to 32 data bytes */ > +#define MAX_PAYLOAD_PER_TRANSACTION 32 > + > +#define MAX_IPMI_DATA_PER_START_TRANSACTION 30 > +#define MAX_IPMI_DATA_PER_MIDDLE_TRANSACTION 31 > + > +#define SSIF_IPMI_SINGLEPART_WRITE 0x2 > +#define SSIF_IPMI_SINGLEPART_READ 0x3 > +#define SSIF_IPMI_MULTIPART_WRITE_START 0x6 > +#define SSIF_IPMI_MULTIPART_WRITE_MIDDLE 0x7 > +#define SSIF_IPMI_MULTIPART_WRITE_END 0x8 > +#define SSIF_IPMI_MULTIPART_READ_START 0x3 > +#define SSIF_IPMI_MULTIPART_READ_MIDDLE 0x9 > + > +#define MSG_PAYLOAD_LEN_MAX 252 > +/* > + * IPMI 2.0 Spec, section 12.7 SSIF Timing, > + * Request-to-Response Time is T6max(250ms) - T1max(20ms) - 3ms = 227ms > + * Recover ssif_bmc from busy state if it takes upto 500ms > + */ > +#define RESPONSE_TIMEOUT 500 /* ms */ > + > +struct ssif_msg { > + u8 len; > + u8 netfn_lun; > + u8 cmd; > + u8 payload[MSG_PAYLOAD_LEN_MAX]; > +} __packed; > + > +static inline u32 ssif_msg_len(struct ssif_msg *ssif_msg) > +{ > + return ssif_msg->len + 1; > +} > + > +/* > + * SSIF internal states: > + * SSIF_READY 0x00 : Ready state > + * SSIF_START 0x01 : Start smbus transaction > + * SSIF_SMBUS_CMD 0x02 : Received SMBus command > + * SSIF_REQ_RECVING 0x03 : Receiving request > + * SSIF_RES_SENDING 0x04 : Sending response > + * SSIF_BAD_SMBUS 0x05 : Bad SMbus transaction > + */ > +enum ssif_state { > + SSIF_READY, > + SSIF_START, > + SSIF_SMBUS_CMD, > + SSIF_REQ_RECVING, > + SSIF_RES_SENDING, > + SSIF_BAD_SMBUS, > + SSIF_STATE_MAX > +}; > + > +struct ssif_bmc_ctx { > + struct i2c_client *client; > + struct miscdevice miscdev; > + size_t msg_idx; > + bool pec_support; > + /* ssif bmc spinlock */ > + spinlock_t lock; > + wait_queue_head_t wait_queue; > + u8 running; > + enum ssif_state state; > + u8 smbus_cmd; > + /* Timeout waiting for response */ > + struct timer_list response_timer; > + bool response_timer_inited; > + /* Flag to identify a Multi-part Read Transaction */ > + bool is_singlepart_read; > + u8 nbytes_processed; > + u8 remain_len; > + u8 recv_len; > + /* Block Number of a Multi-part Read Transaction */ > + u8 block_num; > + bool request_available; > + bool response_in_progress; > + bool busy; > + /* Response buffer for Multi-part Read Transaction */ > + u8 response_buf[MAX_PAYLOAD_PER_TRANSACTION]; > + struct ssif_msg response; > + struct ssif_msg request; > +}; > + > +static inline struct ssif_bmc_ctx *to_ssif_bmc(struct file *file) > +{ > + return container_of(file->private_data, struct ssif_bmc_ctx, miscdev); > +} > +#endif /* __SSIF_BMC_H__ */ > -- > 2.35.1 > > From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3088EC433F5 for ; Fri, 11 Mar 2022 01:21:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:Reply-To:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:In-Reply-To:MIME-Version: References:Message-ID:Subject:Cc:To:From:Date:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=OFsCBZnho4McQJCnTz/Zy1Ndr0/2aorzuBuGtl3WiY8=; b=tWzPKSBq2Ur16t 7HMhnALAE/C4OgrXkUo0UW83lzrbbhIQwPi6xPYnIUoyMiM2tRsLn7rwNku2BJgzEIR20sxRnkdaV ZFSr2rX5E4egSf6clYC99hg/tHKRxs9DSjnX6LfIlbtwAd5qDphyhVluPtntXyMGpBid+5Goanaa5 DIV2+1CaJ/Pn4e+FIcRP8Un9yZO7m2kq8aITySz6lrieIKFDHAlK8Ps5Fq6yiC9BaMNB4UgRIe/1t ABu/GhwFUoDWXpcAKT/gzuhXYFtYlV9drCwL571aqzNCdIQW6QWr1EG1GIXyBEJHLwT+EtWqPbxAJ rqkg9mvFpJ2WO87w1hKA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nSTwg-00EUmP-4W; Fri, 11 Mar 2022 01:19:54 +0000 Received: from mail-qv1-xf30.google.com ([2607:f8b0:4864:20::f30]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nSTwa-00EUlo-5R for linux-arm-kernel@lists.infradead.org; Fri, 11 Mar 2022 01:19:51 +0000 Received: by mail-qv1-xf30.google.com with SMTP id hu12so5933980qvb.6 for ; Thu, 10 Mar 2022 17:19:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=sender:date:from:to:cc:subject:message-id:reply-to:references :mime-version:content-disposition:content-transfer-encoding :in-reply-to; bh=BWLuHkhuLRrMWa8N+fItg7v8ydmq3TO/fXGntCYkO6Y=; b=SuZFZlMlkQQ4S0kNlTh30UByunM4Zhvp+V3yvzH/qoDsqzjS5ejE4svpd4iljAoCpe 05CWTKkCU4/1y1Oer0zWzRR+rzc9qsKnfi/+O+5APKfE5w4C6685gA6L5M+eQr8VcMRU WoxEtvQGfQVnstvr0PVvn/XMJfnxpvQ7ppOqx/ijctdDbEWfeeu8sLFlCWe8zd8SiiR8 bcyxFgqVt448XrBh1iTQACZZIpaZ+TQLBirITlFNPLl+RpX+lct2fy7UvwuNop1xCfvd IbXt454ECUwlSL6Hm9jm0BUcxvNpK46BayRtG2HvJGrt98Qjx/K64P+ZwN3DovFmbFzR labg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:sender:date:from:to:cc:subject:message-id :reply-to:references:mime-version:content-disposition :content-transfer-encoding:in-reply-to; bh=BWLuHkhuLRrMWa8N+fItg7v8ydmq3TO/fXGntCYkO6Y=; b=s3YcsnhQx5p4mAC5NpDvKbKvuWcS0iG0vt5TvSbzJ8TMUXd3XRydksH/HLt9vY6neq rUv1Ve/dHDeJZfQAgDHH0NvcG17QxBJmH4Hfmj8T4kBb1Ly6n2bOa1FxmGON/NaxIZvb p4gctohBRni3DKygRl/W6rD0P1JZq22iLUWlZ088/O9nXzeZqFSnKV9Ci0v0QCXkXRfi Yj4ExUQ6JmRVAn1FKbLlsuhW6LSTa0VCsKrkelmH+w2he4MZ6nkXiwL/JPbj8MlxRb1r TWmv0QAH0PrkPDbtAuKTD+XgBLpwvplxdwBRCOOVtTEPZT1rmqpNUw/a14ycsRoHXqWo L8ig== X-Gm-Message-State: AOAM533k89mc32BUbbwoOmBCDB10EKm90COG7hubyMs2IaTuqV7FZv+F Cqf/2AGBJCICgkOQFPAlEQ== X-Google-Smtp-Source: ABdhPJwL3ZWJehYxMf6yO6S5Io+T49q/ukI/FjtuleC1YSlSekImxG5KL9P6qWMS6cqySdCIpaq6Ig== X-Received: by 2002:a05:6214:76b:b0:435:d12f:628f with SMTP id f11-20020a056214076b00b00435d12f628fmr5403209qvz.53.1646961585262; Thu, 10 Mar 2022 17:19:45 -0800 (PST) Received: from serve.minyard.net (serve.minyard.net. [2001:470:b8f6:1b::1]) by smtp.gmail.com with ESMTPSA id z6-20020ae9c106000000b0067d3b9ef387sm2727890qki.28.2022.03.10.17.19.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Mar 2022 17:19:44 -0800 (PST) Received: from minyard.net (unknown [IPv6:2001:470:b8f6:1b:dc9d:d155:a010:ac11]) by serve.minyard.net (Postfix) with ESMTPSA id 2CC7E1800BC; Fri, 11 Mar 2022 01:19:43 +0000 (UTC) Date: Thu, 10 Mar 2022 19:19:42 -0600 From: Corey Minyard To: Quan Nguyen Cc: Rob Herring , Krzysztof Kozlowski , Joel Stanley , Andrew Jeffery , Brendan Higgins , Benjamin Herrenschmidt , Wolfram Sang , openipmi-developer@lists.sourceforge.net, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, openbmc@lists.ozlabs.org, Open Source Submission , Phong Vo , "Thang Q . Nguyen" Subject: Re: [PATCH v6 1/4] ipmi: ssif_bmc: Add SSIF BMC driver Message-ID: <20220311011942.GX3457@minyard.net> References: <20220310114119.13736-1-quan@os.amperecomputing.com> <20220310114119.13736-2-quan@os.amperecomputing.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20220310114119.13736-2-quan@os.amperecomputing.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220310_171948_301001_DEEBDF0F X-CRM114-Status: GOOD ( 41.74 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: minyard@acm.org Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org T24gVGh1LCBNYXIgMTAsIDIwMjIgYXQgMDY6NDE6MTZQTSArMDcwMCwgUXVhbiBOZ3V5ZW4gd3Jv dGU6Cj4gVGhlIFNNQnVzIHN5c3RlbSBpbnRlcmZhY2UgKFNTSUYpIElQTUkgQk1DIGRyaXZlciBj YW4gYmUgdXNlZCB0byBwZXJmb3JtCj4gaW4tYmFuZCBJUE1JIGNvbW11bmljYXRpb24gd2l0aCB0 aGVpciBob3N0IGluIG1hbmFnZW1lbnQgKEJNQykgc2lkZS4KClRoYW5rcyBmb3IgdGhlIHVwZGF0 ZSwgYW5kIHJlbW92aW5nIHNsYXZlX2VuYWJsZSgpLgoKSSBzcGVudCBzb21lIHF1YWxpdHkgdGlt ZSB3aXRoIHRoaXMsIGFuZCBpdCBsb29rcyBsaWtlIHRoZXJlIGFyZSBwbGFjZXMKd2hlcmUgeW91 IGRvbid0IGNoZWNrIGxlbmd0aHMgYmVmb3JlIGluZGV4aW5nIGFycmF5cy4gIEkndmUgbWFya2Vk CnNldmVyYWwgb2YgdGhlc2UsIGJ1dCB5b3UgbmVlZCB0byBjaGVjayBpdCBhbGwgdGhyb3VnaCB0 aGlzLgoKSSBoYXZlIGEgZmV3IG90aGVyIGNvbW1lbnRzLCB0b28uCgotY29yZXkKCj4gCj4gU2ln bmVkLW9mZi1ieTogUXVhbiBOZ3V5ZW4gPHF1YW5Ab3MuYW1wZXJlY29tcHV0aW5nLmNvbT4KPiAt LS0KPiB2NjoKPiAgICsgRHJvcCB0aGUgdXNlIG9mIHNsYXZlX2VuYWJsZSgpICAgICAgICAgICAg ICAgICAgICAgICAgICAgICBbV29sZnJhbV0KPiAgICsgTWFrZSBpMmMtYXNwZWVkIHRvIGlzc3Vl IFJ4Q21kTGFzdCBjb21tYW5kIG9uIGFsbAo+ICAgSTJDX1NMQVZFX1dSSVRFX1JFUVVFU1RFRCBl dmVudCB0byBhc3NlcnQgTkFLIHdoZW4gc2xhdmUgYnVzeSAgIFtRdWFuXQo+ICAgKyBNYWtlIGky YyBzbGF2ZSB0byByZXR1cm4gLUVCVVNZIHdoZW4gaXQncyBidXN5ICAgICAgICAgICAgICAgIFtR dWFuXQo+ICAgKyBEcm9wIHRoZSBhYm9ydGluZyBmZWF0dXJlIGFzIHJldHVybiBDb21wbGV0aW9u IENvZGUgMHhGRiBtYXkgc3RvcAo+ICAgaG9zdCB0byByZXRyeSBhbmQgbWFrZSBpcG1pX3NzaWYu c28gZmFpbHMgdG8gbG9hZCAgICAgICAgICAgICAgIFtRdWFuXQo+ICAgKyBBZGQgdGltZXIgdG8g cmVjb3ZlciBzbGF2ZSBmcm9tIGJ1c3kgc3RhdGUgd2hlbiBubyByZXNwb25zZSAgIFtRdWFuXQo+ ICAgKyBDbGVhbiByZXF1ZXN0L3Jlc3BvbnNlIGJ1ZmZlciBhcHByb3ByaWF0ZWx5ICAgICAgICAg ICAgICAgICAgIFtRdWFuXQo+ICAgKyBBZGQgc29tZSBtaW5vciBjaGFuZ2Ugb24gZXJyb3IgYW5k IHdhcm5pbmcgbWVzc2FnZXMgICAgICAgICAgIFtRdWFuXQo+IAo+IHY1Ogo+ICAgKyBOb25lCj4g Cj4gdjQ6Cj4gICArIFNlbmQgcmVzcG9uc2Ugd2l0aCBDb21wbGV0aW9uIGNvZGUgMHhGRiB3aGVu IGFib3J0aW5nICAgICAgICAgW1F1YW5dCj4gICArIEFkZGVkIGJvdW5kaW5nIGNoZWNrIG9uIFNN QnVzIHdyaXRlcyBhbmQgdGhlIHdob2xlIHJlcXVlc3QgICAgIFtEYW5dCj4gICArIE1vdmVkIGJ1 ZmZlciB0byBlbmQgb2Ygc3RydWN0IHNzaWZfYm1jX2N0eCB0byBhdm9pZCBjb250ZXh0Cj4gICAg IGNvcnJ1cHRpb24gaWYgc29tZWhvdyBidWZmZXIgaXMgd3JpdHRlbiBwYXN0IHRoZSBlbmQgICAg ICAgICAgIFtEYW5dCj4gICArIFJldHVybiAtRUlOVkFMIGlmIHVzZXJzcGFjZSBidWZmZXIgdG9v IHNtYWxsLCBkb250Cj4gICAgIHNpbGVuY2UgdHJ1bmNhdGUgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICBbQ29yZXksIEpvZWxdCj4gICArIE5vdCBuZWNlc3NhcnkgdG8gY2hl Y2sgTk9OQkxPQ0sgaW4gbG9jayAgICAgICAgICAgICAgICAgICAgICBbQ29yZXldCj4gICArIEVu Zm9yY2Ugb25lIHVzZXIgYXQgYSB0aW1lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgW0pvZWxdCj4gICArIFJlamVjdCB3cml0ZSB3aXRoIGludmFsaWQgcmVzcG9uc2UgbGVuZ3Ro IGZyb20gdXNlcnNwYWNlICAgICBbQ29yZXldCj4gICArIEFkZCBzdGF0ZSBtYWNoaW5lcyBmb3Ig YmV0dGVyIHNzaWYgYm1jIHN0YXRlIGhhbmRsaW5nICAgICAgICAgW1F1YW5dCj4gICArIERyb3Ag c3NpZl9ibWNfYXNwZWVkLmMgYW5kIG1ha2Ugc3NpZl9ibWMuYyBpcyBnZW5lcmljCj4gICAgIFNT SUYgQk1DIGRyaXZlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgW1F1YW5dCj4gICArIENoYW5nZSBjb21wYXRpYmxlIHN0cmluZyAiYXNwZWVkLGFzdDI1MDAt c3NpZi1ibWMiIHRvCj4gICAgICJhbXBlcmUsc3NpZi1ibWMiICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgW1F1YW5dCj4gICArIEFib3J0IGN1cnJlbnQgcmVxdWVz dCB3aXRoIGludmFsaWQgU01CdXMgd3JpdGUgb3IKPiAgICAgaW52YWxpZCBjb21tYW5kICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbUXVhbl0KPiAgICsgQWJv cnQgYWxsIHJlcXVlc3QgaWYgdGhlcmUgaXMgcGVuZGluZyByZXNwb25zZSAgICAgICAgICAgICAg ICBbUXVhbl0KPiAgICsgQ2hhbmdlZCB2YWxpZGF0ZV9wZWMoKSB0byB2YWxpZGF0ZV9yZXF1ZXN0 KCkgICAgICAgICAgICAgICAgICBbUXVhbl0KPiAgICsgQWRkIHVuc3VwcG9ydGVkX3NtYnVzX2Nt ZCgpIHRvIGhhbmRsZSB1bmtub3duIFNNQnVzIGNvbW1hbmQgICBbUXVhbl0KPiAgICsgUHJpbnQg aW50ZXJuYWwgc3RhdGUgc3RyaW5nIGZvciBlYXNlIGludmVzdGlnYXRpbmcgaXNzdWUgICAgICBb UXVhbl0KPiAgICsgTW92ZSB0byBSRUFEWSBzdGF0ZSBvbiBTTEFWRV9TVE9QIGV2ZW50ICAgICAg ICAgICAgICAgICAgICAgICBbUXVhbl0KPiAgICsgQ2hhbmdlIGluaXRpbGl6ZV90cmFuc2Zlcigp IHRvIHByb2Nlc3Nfc21idXNfY21kKCkgICAgICAgICAgICBbUXVhbl0KPiAgICsgSW50cm9kdWNl IGZ1bmN0aW9ucyBmb3IgZWFjaCBzbGF2ZSBldmVudCAgICAgICAgICAgICAgICAgICAgICBbUXVh bl0KPiAKPiB2MzoKPiAgICsgUmVtb3ZlZCByZWR1bmRhbnQgbGljZW5zZSBpbmZvICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICBbSm9lbF0KPiAgICsgU3dpdGNoZWQgdG8gdXNlIHRyYWRp dGlvbmFsIGlmLWVsc2UgICAgICAgICAgICAgICAgICAgICAgICAgICBbSm9lbF0KPiAgICsgUmVt b3ZlZCB1bnVzZWQgc3NpZl9ibWNfaW9jdGwoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICBbSm9lbF0KPiAgICsgTWFkZSBoYW5kbGVfcmVxdWVzdCgpL2NvbXBsZXRlX3Jlc3BvbnNlKCkg dG8gcmV0dXJuIHZvaWQgICAgICBbSm9lbF0KPiAgICsgUmVmYWN0b3JlZCBzZW5kX3NzaWZfYm1j X3Jlc3BvbnNlKCkgYW5kCj4gICByZWNlaXZlX3NzaWZfYm1jX3JlcXVlc3QoKSAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICBbQ29yZXldCj4gICArIFJlbW92ZWQgbXV0ZXggICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbQ29yZXldCj4gICAr IFVzZSBzcGluX2xvY2svdW5sb2NrX2lycXNhdmUvcmVzdG9yZSBpbiBjYWxsYmFjayAgICAgICAg ICAgICBbQ29yZXldCj4gICArIFJlbW92ZWQgdGhlIHVubmVjZXNzYXJ5IG1lbXNldCAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICBbQ29yZXldCj4gICArIFN3aXRjaCB0byB1c2UgZGV2X2Vy cigpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbQ29yZXldCj4gCj4gdjI6 Cj4gICArIEZpeGVkIGNvbXBpbGluZyBlcnJvciB3aXRoIENPTVBJTEVfVEVTVCBmb3IgYXJjCj4g Cj4gIGRyaXZlcnMvY2hhci9pcG1pL0tjb25maWcgICAgfCAgMTEgKwo+ICBkcml2ZXJzL2NoYXIv aXBtaS9NYWtlZmlsZSAgIHwgICAxICsKPiAgZHJpdmVycy9jaGFyL2lwbWkvc3NpZl9ibWMuYyB8 IDc5MSArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKwo+ICBkcml2ZXJzL2NoYXIv aXBtaS9zc2lmX2JtYy5oIHwgMTAyICsrKysrCj4gIDQgZmlsZXMgY2hhbmdlZCwgOTA1IGluc2Vy dGlvbnMoKykKPiAgY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvY2hhci9pcG1pL3NzaWZfYm1j LmMKPiAgY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvY2hhci9pcG1pL3NzaWZfYm1jLmgKPiAK PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9jaGFyL2lwbWkvS2NvbmZpZyBiL2RyaXZlcnMvY2hhci9p cG1pL0tjb25maWcKPiBpbmRleCBiMDYxZTZiNTEzZWQuLjE4YTg5MDkzZDY0ZSAxMDA2NDQKPiAt LS0gYS9kcml2ZXJzL2NoYXIvaXBtaS9LY29uZmlnCj4gKysrIGIvZHJpdmVycy9jaGFyL2lwbWkv S2NvbmZpZwo+IEBAIC0xNjksNiArMTY5LDE3IEBAIGNvbmZpZyBBU1BFRURfQlRfSVBNSV9CTUMK PiAgCSAgZm91bmQgb24gQXNwZWVkIFNPQ3MgKEFTVDI0MDAgYW5kIEFTVDI1MDApLiBUaGUgZHJp dmVyCj4gIAkgIGltcGxlbWVudHMgdGhlIEJNQyBzaWRlIG9mIHRoZSBCVCBpbnRlcmZhY2UuCj4g IAo+ICtjb25maWcgU1NJRl9JUE1JX0JNQwo+ICsJdHJpc3RhdGUgIlNTSUYgSVBNSSBCTUMgZHJp dmVyIgo+ICsJc2VsZWN0IEkyQwo+ICsJc2VsZWN0IEkyQ19TTEFWRQo+ICsJaGVscAo+ICsJICBU aGlzIGVuYWJsZXMgdGhlIElQTUkgU01CdXMgc3lzdGVtIGludGVyZmFjZSAoU1NJRikgYXQgdGhl Cj4gKwkgIG1hbmFnZW1lbnQgKEJNQykgc2lkZS4KPiArCj4gKwkgIFRoZSBkcml2ZXIgaW1wbGVt ZW50cyB0aGUgQk1DIHNpZGUgb2YgdGhlIFNNQnVzIHN5c3RlbQo+ICsJICBpbnRlcmZhY2UgKFNT SUYpLgo+ICsKPiAgY29uZmlnIElQTUJfREVWSUNFX0lOVEVSRkFDRQo+ICAJdHJpc3RhdGUgJ0lQ TUIgSW50ZXJmYWNlIGhhbmRsZXInCj4gIAlkZXBlbmRzIG9uIEkyQwo+IGRpZmYgLS1naXQgYS9k cml2ZXJzL2NoYXIvaXBtaS9NYWtlZmlsZSBiL2RyaXZlcnMvY2hhci9pcG1pL01ha2VmaWxlCj4g aW5kZXggN2NlNzkwZWZhZDkyLi5jYjYxMzhiOGRlZDkgMTAwNjQ0Cj4gLS0tIGEvZHJpdmVycy9j aGFyL2lwbWkvTWFrZWZpbGUKPiArKysgYi9kcml2ZXJzL2NoYXIvaXBtaS9NYWtlZmlsZQo+IEBA IC0zMCwzICszMCw0IEBAIG9iai0kKENPTkZJR19BU1BFRURfQlRfSVBNSV9CTUMpICs9IGJ0LWJt Yy5vCj4gIG9iai0kKENPTkZJR19BU1BFRURfS0NTX0lQTUlfQk1DKSArPSBrY3NfYm1jX2FzcGVl ZC5vCj4gIG9iai0kKENPTkZJR19OUENNN1hYX0tDU19JUE1JX0JNQykgKz0ga2NzX2JtY19ucGNt N3h4Lm8KPiAgb2JqLSQoQ09ORklHX0lQTUJfREVWSUNFX0lOVEVSRkFDRSkgKz0gaXBtYl9kZXZf aW50Lm8KPiArb2JqLSQoQ09ORklHX1NTSUZfSVBNSV9CTUMpICs9IHNzaWZfYm1jLm8KPiBkaWZm IC0tZ2l0IGEvZHJpdmVycy9jaGFyL2lwbWkvc3NpZl9ibWMuYyBiL2RyaXZlcnMvY2hhci9pcG1p L3NzaWZfYm1jLmMKPiBuZXcgZmlsZSBtb2RlIDEwMDY0NAo+IGluZGV4IDAwMDAwMDAwMDAwMC4u NjJkYjk3NzczNjU0Cj4gLS0tIC9kZXYvbnVsbAo+ICsrKyBiL2RyaXZlcnMvY2hhci9pcG1pL3Nz aWZfYm1jLmMKPiBAQCAtMCwwICsxLDc5MSBAQAo+ICsvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmll cjogR1BMLTIuMCsKPiArLyoKPiArICogVGhlIGRyaXZlciBmb3IgQk1DIHNpZGUgb2YgU1NJRiBp bnRlcmZhY2UKPiArICoKPiArICogQ29weXJpZ2h0IChjKSAyMDIyLCBBbXBlcmUgQ29tcHV0aW5n IExMQwo+ICsgKgo+ICsgKi8KPiArCj4gKyNpbmNsdWRlIDxsaW51eC9pMmMuaD4KPiArI2luY2x1 ZGUgPGxpbnV4L21pc2NkZXZpY2UuaD4KPiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPgo+ICsj aW5jbHVkZSA8bGludXgvb2YuaD4KPiArI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5o Pgo+ICsjaW5jbHVkZSA8bGludXgvcG9sbC5oPgo+ICsjaW5jbHVkZSA8bGludXgvc2NoZWQuaD4K PiArI2luY2x1ZGUgPGxpbnV4L211dGV4Lmg+Cj4gKyNpbmNsdWRlIDxsaW51eC9zcGlubG9jay5o Pgo+ICsjaW5jbHVkZSA8bGludXgvdGltZXIuaD4KPiArI2luY2x1ZGUgPGxpbnV4L2ppZmZpZXMu aD4KPiArCj4gKyNpbmNsdWRlICJzc2lmX2JtYy5oIgo+ICsKPiArc3RhdGljIGNvbnN0IGNoYXIg KnN0YXRlX3RvX3N0cmluZyhlbnVtIHNzaWZfc3RhdGUgc3RhdGUpCj4gK3sKPiArCXN3aXRjaCAo c3RhdGUpIHsKPiArCWNhc2UgU1NJRl9SRUFEWToKPiArCQlyZXR1cm4gIlNTSUZfUkVBRFkiOwo+ ICsJY2FzZSBTU0lGX1NUQVJUOgo+ICsJCXJldHVybiAiU1NJRl9TVEFSVCI7Cj4gKwljYXNlIFNT SUZfU01CVVNfQ01EOgo+ICsJCXJldHVybiAiU1NJRl9TTUJVU19DTUQiOwo+ICsJY2FzZSBTU0lG X1JFUV9SRUNWSU5HOgo+ICsJCXJldHVybiAiU1NJRl9SRVFfUkVDVklORyI7Cj4gKwljYXNlIFNT SUZfUkVTX1NFTkRJTkc6Cj4gKwkJcmV0dXJuICJTU0lGX1JFU19TRU5ESU5HIjsKPiArCWNhc2Ug U1NJRl9CQURfU01CVVM6Cj4gKwkJcmV0dXJuICJTU0lGX0JBRF9TTUJVUyI7Cj4gKwlkZWZhdWx0 Ogo+ICsJCXJldHVybiAiU1NJRl9TVEFURV9VTktOT1dOIjsKPiArCX0KPiArfQo+ICsKPiArLyog SGFuZGxlIFNTSUYgbWVzc2FnZSB0aGF0IHdpbGwgYmUgc2VudCB0byB1c2VyICovCj4gK3N0YXRp YyBzc2l6ZV90IHNzaWZfYm1jX3JlYWQoc3RydWN0IGZpbGUgKmZpbGUsIGNoYXIgX191c2VyICpi dWYsIHNpemVfdCBjb3VudCwgbG9mZl90ICpwcG9zKQo+ICt7Cj4gKwlzdHJ1Y3Qgc3NpZl9ibWNf Y3R4ICpzc2lmX2JtYyA9IHRvX3NzaWZfYm1jKGZpbGUpOwo+ICsJc3RydWN0IHNzaWZfbXNnIG1z ZzsKPiArCXVuc2lnbmVkIGxvbmcgZmxhZ3M7Cj4gKwlzc2l6ZV90IHJldDsKPiArCj4gKwlzcGlu X2xvY2tfaXJxc2F2ZSgmc3NpZl9ibWMtPmxvY2ssIGZsYWdzKTsKPiArCXdoaWxlICghc3NpZl9i bWMtPnJlcXVlc3RfYXZhaWxhYmxlKSB7Cj4gKwkJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmc3Np Zl9ibWMtPmxvY2ssIGZsYWdzKTsKPiArCQlpZiAoZmlsZS0+Zl9mbGFncyAmIE9fTk9OQkxPQ0sp Cj4gKwkJCXJldHVybiAtRUFHQUlOOwo+ICsJCXJldCA9IHdhaXRfZXZlbnRfaW50ZXJydXB0aWJs ZShzc2lmX2JtYy0+d2FpdF9xdWV1ZSwKPiArCQkJCQkgICAgICAgc3NpZl9ibWMtPnJlcXVlc3Rf YXZhaWxhYmxlKTsKPiArCQlpZiAocmV0KQo+ICsJCQlyZXR1cm4gcmV0Owo+ICsJCXNwaW5fbG9j a19pcnFzYXZlKCZzc2lmX2JtYy0+bG9jaywgZmxhZ3MpOwo+ICsJfQo+ICsKPiArCWlmIChjb3Vu dCA8IG1pbl90KHNzaXplX3QsIHNzaWZfbXNnX2xlbigmc3NpZl9ibWMtPnJlcXVlc3QpLCBzaXpl b2Yoc3RydWN0IHNzaWZfbXNnKSkpIHsKPiArCQlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZzc2lm X2JtYy0+bG9jaywgZmxhZ3MpOwo+ICsJCXJldCA9IC1FSU5WQUw7Cj4gKwl9IGVsc2Ugewo+ICsJ CWNvdW50ID0gbWluX3Qoc3NpemVfdCwgc3NpZl9tc2dfbGVuKCZzc2lmX2JtYy0+cmVxdWVzdCks IHNpemVvZihzdHJ1Y3Qgc3NpZl9tc2cpKTsKPiArCQltZW1jcHkoJm1zZywgJnNzaWZfYm1jLT5y ZXF1ZXN0LCBjb3VudCk7Cj4gKwkJc3NpZl9ibWMtPnJlcXVlc3RfYXZhaWxhYmxlID0gZmFsc2U7 Cj4gKwkJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmc3NpZl9ibWMtPmxvY2ssIGZsYWdzKTsKPiAr Cj4gKwkJcmV0ID0gY29weV90b191c2VyKGJ1ZiwgJm1zZywgY291bnQpOwo+ICsJfQo+ICsKPiAr CXJldHVybiAocmV0IDwgMCkgPyByZXQgOiBjb3VudDsKPiArfQo+ICsKPiArLyogSGFuZGxlIFNT SUYgbWVzc2FnZSB0aGF0IGlzIHdyaXR0ZW4gYnkgdXNlciAqLwo+ICtzdGF0aWMgc3NpemVfdCBz c2lmX2JtY193cml0ZShzdHJ1Y3QgZmlsZSAqZmlsZSwgY29uc3QgY2hhciBfX3VzZXIgKmJ1Ziwg c2l6ZV90IGNvdW50LAo+ICsJCQkgICAgICBsb2ZmX3QgKnBwb3MpCj4gK3sKPiArCXN0cnVjdCBz c2lmX2JtY19jdHggKnNzaWZfYm1jID0gdG9fc3NpZl9ibWMoZmlsZSk7Cj4gKwlzdHJ1Y3Qgc3Np Zl9tc2cgbXNnOwo+ICsJdW5zaWduZWQgbG9uZyBmbGFnczsKPiArCXNzaXplX3QgcmV0Owo+ICsK PiArCWlmIChjb3VudCA+IHNpemVvZihzdHJ1Y3Qgc3NpZl9tc2cpKQo+ICsJCXJldHVybiAtRUlO VkFMOwo+ICsKPiArCXJldCA9IGNvcHlfZnJvbV91c2VyKCZtc2csIGJ1ZiwgY291bnQpOwo+ICsJ aWYgKHJldCkKPiArCQlyZXR1cm4gcmV0Owo+ICsKPiArCWlmICghbXNnLmxlbiB8fCBjb3VudCA8 IHNzaWZfbXNnX2xlbigmbXNnKSkKPiArCQlyZXR1cm4gLUVJTlZBTDsKPiArCj4gKwlzcGluX2xv Y2tfaXJxc2F2ZSgmc3NpZl9ibWMtPmxvY2ssIGZsYWdzKTsKPiArCXdoaWxlIChzc2lmX2JtYy0+ cmVzcG9uc2VfaW5fcHJvZ3Jlc3MpIHsKPiArCQlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZzc2lm X2JtYy0+bG9jaywgZmxhZ3MpOwo+ICsJCWlmIChmaWxlLT5mX2ZsYWdzICYgT19OT05CTE9DSykK PiArCQkJcmV0dXJuIC1FQUdBSU47Cj4gKwkJcmV0ID0gd2FpdF9ldmVudF9pbnRlcnJ1cHRpYmxl KHNzaWZfYm1jLT53YWl0X3F1ZXVlLAo+ICsJCQkJCSAgICAgICAhc3NpZl9ibWMtPnJlc3BvbnNl X2luX3Byb2dyZXNzKTsKPiArCQlpZiAocmV0KQo+ICsJCQlyZXR1cm4gcmV0Owo+ICsJCXNwaW5f bG9ja19pcnFzYXZlKCZzc2lmX2JtYy0+bG9jaywgZmxhZ3MpOwo+ICsJfQo+ICsKPiArCW1lbWNw eSgmc3NpZl9ibWMtPnJlc3BvbnNlLCAmbXNnLCBjb3VudCk7Cj4gKwlzc2lmX2JtYy0+aXNfc2lu Z2xlcGFydF9yZWFkID0gKHNzaWZfbXNnX2xlbigmbXNnKSA8PSBNQVhfUEFZTE9BRF9QRVJfVFJB TlNBQ1RJT04gKyAxKTsKPiArCj4gKwlzc2lmX2JtYy0+cmVzcG9uc2VfaW5fcHJvZ3Jlc3MgPSB0 cnVlOwo+ICsKPiArCS8qIHNzaWZfYm1jIG5vdCBidXN5ICovCj4gKwlzc2lmX2JtYy0+YnVzeSA9 IGZhbHNlOwo+ICsKPiArCWlmIChzc2lmX2JtYy0+cmVzcG9uc2VfdGltZXJfaW5pdGVkKSB7Cj4g KwkJZGVsX3RpbWVyKCZzc2lmX2JtYy0+cmVzcG9uc2VfdGltZXIpOwo+ICsJCXNzaWZfYm1jLT5y ZXNwb25zZV90aW1lcl9pbml0ZWQgPSBmYWxzZTsKPiArCX0KPiArCS8qIENsZWFuIG9sZCByZXF1 ZXN0IGJ1ZmZlciAqLwo+ICsJbWVtc2V0KCZzc2lmX2JtYy0+cmVxdWVzdCwgMCwgc2l6ZW9mKHN0 cnVjdCBzc2lmX21zZykpOwo+ICsKPiArCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJnNzaWZfYm1j LT5sb2NrLCBmbGFncyk7Cj4gKwo+ICsJcmV0dXJuIGNvdW50Owo+ICt9Cj4gKwo+ICtzdGF0aWMg aW50IHNzaWZfYm1jX29wZW4oc3RydWN0IGlub2RlICppbm9kZSwgc3RydWN0IGZpbGUgKmZpbGUp Cj4gK3sKPiArCXN0cnVjdCBzc2lmX2JtY19jdHggKnNzaWZfYm1jID0gdG9fc3NpZl9ibWMoZmls ZSk7Cj4gKwlpbnQgcmV0ID0gMDsKPiArCj4gKwlzcGluX2xvY2tfaXJxKCZzc2lmX2JtYy0+bG9j ayk7Cj4gKwlpZiAoIXNzaWZfYm1jLT5ydW5uaW5nKQo+ICsJCXNzaWZfYm1jLT5ydW5uaW5nID0g MTsKPiArCWVsc2UKPiArCQlyZXQgPSAtRUJVU1k7Cj4gKwlzcGluX3VubG9ja19pcnEoJnNzaWZf Ym1jLT5sb2NrKTsKPiArCj4gKwlyZXR1cm4gcmV0Owo+ICt9Cj4gKwo+ICtzdGF0aWMgdW5zaWdu ZWQgaW50IHNzaWZfYm1jX3BvbGwoc3RydWN0IGZpbGUgKmZpbGUsIHBvbGxfdGFibGUgKndhaXQp Cj4gK3sKPiArCXN0cnVjdCBzc2lmX2JtY19jdHggKnNzaWZfYm1jID0gdG9fc3NpZl9ibWMoZmls ZSk7Cj4gKwl1bnNpZ25lZCBpbnQgbWFzayA9IDA7Cj4gKwo+ICsJcG9sbF93YWl0KGZpbGUsICZz c2lmX2JtYy0+d2FpdF9xdWV1ZSwgd2FpdCk7Cj4gKwo+ICsJc3Bpbl9sb2NrX2lycSgmc3NpZl9i bWMtPmxvY2spOwo+ICsJLyogVGhlIHJlcXVlc3QgaXMgYXZhaWxhYmxlLCB1c2Vyc3BhY2UgYXBw bGljYXRpb24gY2FuIGdldCB0aGUgcmVxdWVzdCAqLwo+ICsJaWYgKHNzaWZfYm1jLT5yZXF1ZXN0 X2F2YWlsYWJsZSkKPiArCQltYXNrIHw9IFBPTExJTjsKPiArCj4gKwlzcGluX3VubG9ja19pcnEo JnNzaWZfYm1jLT5sb2NrKTsKPiArCj4gKwlyZXR1cm4gbWFzazsKPiArfQo+ICsKPiArc3RhdGlj IGludCBzc2lmX2JtY19yZWxlYXNlKHN0cnVjdCBpbm9kZSAqaW5vZGUsIHN0cnVjdCBmaWxlICpm aWxlKQo+ICt7Cj4gKwlzdHJ1Y3Qgc3NpZl9ibWNfY3R4ICpzc2lmX2JtYyA9IHRvX3NzaWZfYm1j KGZpbGUpOwo+ICsKPiArCXNwaW5fbG9ja19pcnEoJnNzaWZfYm1jLT5sb2NrKTsKPiArCXNzaWZf Ym1jLT5ydW5uaW5nID0gMDsKPiArCXNwaW5fdW5sb2NrX2lycSgmc3NpZl9ibWMtPmxvY2spOwo+ ICsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICsvKgo+ICsgKiBTeXN0ZW0gY2FsbHMgdG8gZGV2 aWNlIGludGVyZmFjZSBmb3IgdXNlciBhcHBzCj4gKyAqLwo+ICtzdGF0aWMgY29uc3Qgc3RydWN0 IGZpbGVfb3BlcmF0aW9ucyBzc2lmX2JtY19mb3BzID0gewo+ICsJLm93bmVyCQk9IFRISVNfTU9E VUxFLAo+ICsJLm9wZW4JCT0gc3NpZl9ibWNfb3BlbiwKPiArCS5yZWFkCQk9IHNzaWZfYm1jX3Jl YWQsCj4gKwkud3JpdGUJCT0gc3NpZl9ibWNfd3JpdGUsCj4gKwkucmVsZWFzZQk9IHNzaWZfYm1j X3JlbGVhc2UsCj4gKwkucG9sbAkJPSBzc2lmX2JtY19wb2xsLAo+ICt9Owo+ICsKPiArLyogQ2Fs bGVkIHdpdGggc3NpZl9ibWMtPmxvY2sgaGVsZC4gKi8KPiArc3RhdGljIHZvaWQgY29tcGxldGVf cmVzcG9uc2Uoc3RydWN0IHNzaWZfYm1jX2N0eCAqc3NpZl9ibWMpCj4gK3sKPiArCS8qIEludmFs aWRhdGUgcmVzcG9uc2UgaW4gYnVmZmVyIHRvIGRlbm90ZSBpdCBoYXZpbmcgYmVlbiBzZW50LiAq Lwo+ICsJc3NpZl9ibWMtPnJlc3BvbnNlLmxlbiA9IDA7Cj4gKwlzc2lmX2JtYy0+cmVzcG9uc2Vf aW5fcHJvZ3Jlc3MgPSBmYWxzZTsKPiArCXNzaWZfYm1jLT5uYnl0ZXNfcHJvY2Vzc2VkID0gMDsK PiArCXNzaWZfYm1jLT5yZW1haW5fbGVuID0gMDsKPiArCXNzaWZfYm1jLT5idXN5ID0gZmFsc2U7 Cj4gKwltZW1zZXQoJnNzaWZfYm1jLT5yZXNwb25zZV9idWYsIDAsIE1BWF9QQVlMT0FEX1BFUl9U UkFOU0FDVElPTik7Cj4gKwl3YWtlX3VwX2FsbCgmc3NpZl9ibWMtPndhaXRfcXVldWUpOwo+ICt9 Cj4gKwo+ICtzdGF0aWMgdm9pZCByZXNwb25zZV90aW1lb3V0KHN0cnVjdCB0aW1lcl9saXN0ICp0 KQo+ICt7Cj4gKwlzdHJ1Y3Qgc3NpZl9ibWNfY3R4ICpzc2lmX2JtYyA9IGZyb21fdGltZXIoc3Np Zl9ibWMsIHQsIHJlc3BvbnNlX3RpbWVyKTsKPiArCXVuc2lnbmVkIGxvbmcgZmxhZ3M7Cj4gKwoK SXMgdGhlcmUgYSBwb3NzaWJsZSByYWNlIGhlcmU/ICBUaGUgdGltZW91dCBjYW4gaGFwcGVuIGF0 IHRoZSBzYW1lIHRpbWUKYXMgYSByZWNlaXZlZCBtZXNzYWdlLCB3aWxsIHNvbWV0aGluZyBiYWQg aGFwcGVuIGlmIHRoYXQncyB0aGUgY2FzZT8KCj4gKwlzcGluX2xvY2tfaXJxc2F2ZSgmc3NpZl9i bWMtPmxvY2ssIGZsYWdzKTsKPiArCj4gKwkvKiBSZWNvdmVyIHNzaWZfYm1jIGZyb20gYnVzeSAq Lwo+ICsJc3NpZl9ibWMtPmJ1c3kgPSBmYWxzZTsKPiArCWRlbF90aW1lcigmc3NpZl9ibWMtPnJl c3BvbnNlX3RpbWVyKTsKCllvdSBkb24ndCBuZWVkIHRvIGRlbGV0ZSB0aGUgdGltZXIsIGl0J3Mg aW4gdGhlIHRpbWVvdXQuCgo+ICsJc3NpZl9ibWMtPnJlc3BvbnNlX3RpbWVyX2luaXRlZCA9IGZh bHNlOwo+ICsKPiArCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJnNzaWZfYm1jLT5sb2NrLCBmbGFn cyk7Cj4gK30KPiArCj4gKy8qIENhbGxlZCB3aXRoIHNzaWZfYm1jLT5sb2NrIGhlbGQuICovCj4g K3N0YXRpYyB2b2lkIGhhbmRsZV9yZXF1ZXN0KHN0cnVjdCBzc2lmX2JtY19jdHggKnNzaWZfYm1j KQo+ICt7Cj4gKwkvKiBzZXQgc3NpZl9ibWMgdG8gYnVzeSB3YWl0aW5nIGZvciByZXNwb25zZSAq Lwo+ICsJc3NpZl9ibWMtPmJ1c3kgPSB0cnVlOwo+ICsKPiArCS8qIFJlcXVlc3QgbWVzc2FnZSBp cyBhdmFpbGFibGUgdG8gcHJvY2VzcyAqLwo+ICsJc3NpZl9ibWMtPnJlcXVlc3RfYXZhaWxhYmxl ID0gdHJ1ZTsKPiArCj4gKwkvKiBDbGVhbiBvbGQgcmVzcG9uc2UgYnVmZmVyICovCj4gKwltZW1z ZXQoJnNzaWZfYm1jLT5yZXNwb25zZSwgMCwgc2l6ZW9mKHN0cnVjdCBzc2lmX21zZykpOwo+ICsK PiArCS8qIFRoaXMgaXMgdGhlIG5ldyBSRUFEIHJlcXVlc3QuKi8KPiArCXdha2VfdXBfYWxsKCZz c2lmX2JtYy0+d2FpdF9xdWV1ZSk7Cj4gKwo+ICsJLyogQXJtZWQgdGltZXIgdG8gcmVjb3ZlciBz bGF2ZSBmcm9tIGJ1c3kgc3RhdGUgaW4gY2FzZSBvZiBubyByZXNwb25zZSAqLwo+ICsJaWYgKCFz c2lmX2JtYy0+cmVzcG9uc2VfdGltZXJfaW5pdGVkKSB7Cj4gKwkJdGltZXJfc2V0dXAoJnNzaWZf Ym1jLT5yZXNwb25zZV90aW1lciwgcmVzcG9uc2VfdGltZW91dCwgMCk7Cj4gKwkJc3NpZl9ibWMt PnJlc3BvbnNlX3RpbWVyX2luaXRlZCA9IHRydWU7Cj4gKwl9Cj4gKwltb2RfdGltZXIoJnNzaWZf Ym1jLT5yZXNwb25zZV90aW1lciwgamlmZmllcyArIG1zZWNzX3RvX2ppZmZpZXMoUkVTUE9OU0Vf VElNRU9VVCkpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9pZCBzZXRfbXVsdGlwYXJ0X3Jlc3BvbnNl X2J1ZmZlcihzdHJ1Y3Qgc3NpZl9ibWNfY3R4ICpzc2lmX2JtYywgdTggKnZhbCkKPiArewo+ICsJ dTggcmVzcG9uc2VfbGVuID0gMDsKPiArCWludCBpZHggPSAwOwo+ICsJdTggZGF0YV9sZW47Cj4g Kwo+ICsJZGF0YV9sZW4gPSBzc2lmX2JtYy0+cmVzcG9uc2UubGVuOwo+ICsJc3dpdGNoIChzc2lm X2JtYy0+c21idXNfY21kKSB7Cj4gKwljYXNlIFNTSUZfSVBNSV9NVUxUSVBBUlRfUkVBRF9TVEFS VDoKPiArCQkvKgo+ICsJCSAqIFJlYWQgU3RhcnQgbGVuZ3RoIGlzIDMyIGJ5dGVzLgo+ICsJCSAq IFJlYWQgU3RhcnQgdHJhbnNmZXIgZmlyc3QgMzAgYnl0ZXMgb2YgSVBNSSByZXNwb25zZQo+ICsJ CSAqIGFuZCAyIHNwZWNpYWwgY29kZSAweDAwLCAweDAxLgo+ICsJCSAqLwo+ICsJCSp2YWwgPSBN QVhfUEFZTE9BRF9QRVJfVFJBTlNBQ1RJT047Cj4gKwkJc3NpZl9ibWMtPnJlbWFpbl9sZW4gPSBk YXRhX2xlbiAtIE1BWF9JUE1JX0RBVEFfUEVSX1NUQVJUX1RSQU5TQUNUSU9OOwo+ICsJCXNzaWZf Ym1jLT5ibG9ja19udW0gPSAwOwoKRG8geW91IG5lZWQgdG8gdmFsaWRhdGUgdGhlIGRhdGEgbGVu Z3RoIGJlZm9yZSB1c2luZyB0aGlzPwpUaGlzIGFwcGxpZXMgZm9yIGxvdHMgb2YgcGxhY2VzIHRo cm91Z2ggaGVyZS4KCj4gKwo+ICsJCXNzaWZfYm1jLT5yZXNwb25zZV9idWZbaWR4KytdID0gMHgw MDsgLyogU3RhcnQgRmxhZyAqLwo+ICsJCXNzaWZfYm1jLT5yZXNwb25zZV9idWZbaWR4KytdID0g MHgwMTsgLyogU3RhcnQgRmxhZyAqLwo+ICsJCXNzaWZfYm1jLT5yZXNwb25zZV9idWZbaWR4Kytd ID0gc3NpZl9ibWMtPnJlc3BvbnNlLm5ldGZuX2x1bjsKPiArCQlzc2lmX2JtYy0+cmVzcG9uc2Vf YnVmW2lkeCsrXSA9IHNzaWZfYm1jLT5yZXNwb25zZS5jbWQ7Cj4gKwkJc3NpZl9ibWMtPnJlc3Bv bnNlX2J1ZltpZHgrK10gPSBzc2lmX2JtYy0+cmVzcG9uc2UucGF5bG9hZFswXTsKPiArCj4gKwkJ cmVzcG9uc2VfbGVuID0gTUFYX1BBWUxPQURfUEVSX1RSQU5TQUNUSU9OIC0gaWR4Owo+ICsKPiAr CQltZW1jcHkoJnNzaWZfYm1jLT5yZXNwb25zZV9idWZbaWR4XSwgJnNzaWZfYm1jLT5yZXNwb25z ZS5wYXlsb2FkWzFdLAo+ICsJCSAgICAgICByZXNwb25zZV9sZW4pOwo+ICsJCWJyZWFrOwo+ICsK PiArCWNhc2UgU1NJRl9JUE1JX01VTFRJUEFSVF9SRUFEX01JRERMRToKPiArCQkvKgo+ICsJCSAq IElQTUkgUkVBRCBNaWRkbGUgb3IgUkVBRCBFbmQgbWVzc2FnZXMgY2FuIGNhcnJ5IHVwIHRvIDMx IGJ5dGVzCj4gKwkJICogSVBNSSBkYXRhIHBsdXMgYmxvY2sgbnVtYmVyIGJ5dGUuCj4gKwkJICov Cj4gKwkJaWYgKHNzaWZfYm1jLT5yZW1haW5fbGVuIDwgTUFYX0lQTUlfREFUQV9QRVJfTUlERExF X1RSQU5TQUNUSU9OKSB7Cj4gKwkJCS8qCj4gKwkJCSAqIFRoaXMgaXMgUkVBRCBFbmQgbWVzc2Fn ZQo+ICsJCQkgKiAgUmV0dXJuIGxlbmd0aCBpcyB0aGUgcmVtYWluaW5nIHJlc3BvbnNlIGRhdGEg bGVuZ3RoCj4gKwkJCSAqICBwbHVzIGJsb2NrIG51bWJlcgo+ICsJCQkgKiAgQmxvY2sgbnVtYmVy IDB4RkYgaXMgdG8gaW5kaWNhdGUgdGhpcyBpcyBsYXN0IG1lc3NhZ2UKPiArCQkJICoKPiArCQkJ ICovCj4gKwkJCSp2YWwgPSBzc2lmX2JtYy0+cmVtYWluX2xlbiArIDE7Cj4gKwkJCXNzaWZfYm1j LT5ibG9ja19udW0gPSAweEZGOwo+ICsJCQlzc2lmX2JtYy0+cmVzcG9uc2VfYnVmW2lkeCsrXSA9 IHNzaWZfYm1jLT5ibG9ja19udW07Cj4gKwkJCXJlc3BvbnNlX2xlbiA9IHNzaWZfYm1jLT5yZW1h aW5fbGVuOwo+ICsJCQkvKiBDbGVhbiB0aGUgYnVmZmVyICovCj4gKwkJCW1lbXNldCgmc3NpZl9i bWMtPnJlc3BvbnNlX2J1ZltpZHhdLCAwLCBNQVhfUEFZTE9BRF9QRVJfVFJBTlNBQ1RJT04gLSBp ZHgpOwo+ICsJCX0gZWxzZSB7Cj4gKwkJCS8qCj4gKwkJCSAqIFRoaXMgaXMgUkVBRCBNaWRkbGUg bWVzc2FnZQo+ICsJCQkgKiAgUmVzcG9uc2UgbGVuZ3RoIGlzIHRoZSBtYXhpbXVtIFNNQlVTIHRy YW5zZmVyIGxlbmd0aAo+ICsJCQkgKiAgQmxvY2sgbnVtYmVyIGJ5dGUgaXMgaW5jcmVtZW50ZWQK PiArCQkJICogUmV0dXJuIGxlbmd0aCBpcyBtYXhpbXVtIFNNQlVTIHRyYW5zZmVyIGxlbmd0aAo+ ICsJCQkgKi8KPiArCQkJKnZhbCA9IE1BWF9QQVlMT0FEX1BFUl9UUkFOU0FDVElPTjsKPiArCQkJ c3NpZl9ibWMtPnJlbWFpbl9sZW4gLT0gTUFYX0lQTUlfREFUQV9QRVJfTUlERExFX1RSQU5TQUNU SU9OOwo+ICsJCQlyZXNwb25zZV9sZW4gPSBNQVhfSVBNSV9EQVRBX1BFUl9NSURETEVfVFJBTlNB Q1RJT047Cj4gKwkJCXNzaWZfYm1jLT5yZXNwb25zZV9idWZbaWR4KytdID0gc3NpZl9ibWMtPmJs b2NrX251bTsKPiArCQkJc3NpZl9ibWMtPmJsb2NrX251bSsrOwo+ICsJCX0KPiArCj4gKwkJbWVt Y3B5KCZzc2lmX2JtYy0+cmVzcG9uc2VfYnVmW2lkeF0sCj4gKwkJICAgICAgIHNzaWZfYm1jLT5y ZXNwb25zZS5wYXlsb2FkICsgMSArIHNzaWZfYm1jLT5uYnl0ZXNfcHJvY2Vzc2VkLAo+ICsJCSAg ICAgICByZXNwb25zZV9sZW4pOwo+ICsJCWJyZWFrOwo+ICsKPiArCWRlZmF1bHQ6Cj4gKwkJLyog RG8gbm90IGV4cGVjdCB0byBnbyB0byB0aGlzIGNhc2UgKi8KPiArCQlkZXZfZXJyKCZzc2lmX2Jt Yy0+Y2xpZW50LT5kZXYsCj4gKwkJCSIlczogVW5leHBlY3RlZCBTTUJ1cyBjb21tYW5kIDB4JXhc biIsCj4gKwkJCV9fZnVuY19fLCBzc2lmX2JtYy0+c21idXNfY21kKTsKPiArCQlicmVhazsKPiAr CX0KPiArCj4gKwlzc2lmX2JtYy0+bmJ5dGVzX3Byb2Nlc3NlZCArPSByZXNwb25zZV9sZW47Cj4g K30KPiArCj4gKy8qIFByb2Nlc3MgdGhlIElQTUkgcmVzcG9uc2UgdGhhdCB3aWxsIGJlIHJlYWQg YnkgbWFzdGVyICovCj4gK3N0YXRpYyB2b2lkIGhhbmRsZV9yZWFkX3Byb2Nlc3NlZChzdHJ1Y3Qg c3NpZl9ibWNfY3R4ICpzc2lmX2JtYywgdTggKnZhbCkKPiArewo+ICsJdTggKmJ1ZjsKPiArCXU4 IHBlY19sZW4sIGFkZHIsIGxlbjsKPiArCXU4IHBlYyA9IDA7Cj4gKwo+ICsJcGVjX2xlbiA9IHNz aWZfYm1jLT5wZWNfc3VwcG9ydCA/IDEgOiAwOwo+ICsJLyogUEVDIC0gU3RhcnQgUmVhZCBBZGRy ZXNzICovCj4gKwlhZGRyID0gR0VUXzhCSVRfQUREUihzc2lmX2JtYy0+Y2xpZW50LT5hZGRyKTsK PiArCXBlYyA9IGkyY19zbWJ1c19wZWMocGVjLCAmYWRkciwgMSk7Cj4gKwkvKiBQRUMgLSBTU0lG IENvbW1hbmQgKi8KPiArCXBlYyA9IGkyY19zbWJ1c19wZWMocGVjLCAmc3NpZl9ibWMtPnNtYnVz X2NtZCwgMSk7Cj4gKwkvKiBQRUMgLSBSZXN0YXJ0IFdyaXRlIEFkZHJlc3MgKi8KPiArCWFkZHIg PSBhZGRyIHwgMHgwMTsKPiArCXBlYyA9IGkyY19zbWJ1c19wZWMocGVjLCAmYWRkciwgMSk7Cj4g Kwo+ICsJaWYgKHNzaWZfYm1jLT5pc19zaW5nbGVwYXJ0X3JlYWQpIHsKPiArCQkvKiBTaW5nbGUt cGFydCBSZWFkIHByb2Nlc3NpbmcgKi8KPiArCQlidWYgPSAodTggKikmc3NpZl9ibWMtPnJlc3Bv bnNlOwo+ICsKPiArCQlpZiAoc3NpZl9ibWMtPnJlc3BvbnNlLmxlbiAmJiBzc2lmX2JtYy0+bXNn X2lkeCA8IHNzaWZfYm1jLT5yZXNwb25zZS5sZW4pIHsKPiArCQkJc3NpZl9ibWMtPm1zZ19pZHgr KzsKPiArCQkJKnZhbCA9IGJ1Zltzc2lmX2JtYy0+bXNnX2lkeF07Cj4gKwkJfSBlbHNlIGlmIChz c2lmX2JtYy0+cmVzcG9uc2UubGVuICYmIHNzaWZfYm1jLT5tc2dfaWR4ID09IHNzaWZfYm1jLT5y ZXNwb25zZS5sZW4pIHsKPiArCQkJc3NpZl9ibWMtPm1zZ19pZHgrKzsKPiArCQkJKnZhbCA9IGky Y19zbWJ1c19wZWMocGVjLCBidWYsIHNzaWZfbXNnX2xlbigmc3NpZl9ibWMtPnJlc3BvbnNlKSk7 Cj4gKwkJfSBlbHNlIHsKCkkgdGhvdWdodCBmb3IgYSBzZWNvbmQgdGhhdCB0aGlzIHdhcyB3cm9u ZywgYnV0IEkgdGhpbmsgaXQncyBjb3JyZWN0LgpTdXBwbHkgemVybyBpZiB5b3UgZG9uJ3QgaGF2 ZSBkYXRhLgoKPiArCQkJKnZhbCA9IDA7Cj4gKwkJfQo+ICsJCS8qIEludmFsaWRhdGUgcmVzcG9u c2UgYnVmZmVyIHRvIGRlbm90ZSBpdCBpcyBzZW50ICovCj4gKwkJaWYgKHNzaWZfYm1jLT5tc2df aWR4ICsgMSA+PSAoc3NpZl9tc2dfbGVuKCZzc2lmX2JtYy0+cmVzcG9uc2UpICsgcGVjX2xlbikp Cj4gKwkJCWNvbXBsZXRlX3Jlc3BvbnNlKHNzaWZfYm1jKTsKPiArCX0gZWxzZSB7Cj4gKwkJLyog TXVsdGktcGFydCBSZWFkIHByb2Nlc3NpbmcgKi8KCllvdSBkb24ndCBjaGVjayB0aGUgbGVuZ3Ro IGhlcmUgbGlrZSB5b3UgZGlkIGFib3ZlLiAgSSB0aGluayB0aGF0J3MKcmVxdWlyZWQuCgo+ICsJ CXN3aXRjaCAoc3NpZl9ibWMtPnNtYnVzX2NtZCkgewo+ICsJCWNhc2UgU1NJRl9JUE1JX01VTFRJ UEFSVF9SRUFEX1NUQVJUOgo+ICsJCWNhc2UgU1NJRl9JUE1JX01VTFRJUEFSVF9SRUFEX01JRERM RToKPiArCQkJYnVmID0gKHU4ICopJnNzaWZfYm1jLT5yZXNwb25zZV9idWY7Cj4gKwkJCSp2YWwg PSBidWZbc3NpZl9ibWMtPm1zZ19pZHhdOwo+ICsJCQlzc2lmX2JtYy0+bXNnX2lkeCsrOwo+ICsJ CQlicmVhazsKPiArCQlkZWZhdWx0Ogo+ICsJCQkvKiBEbyBub3QgZXhwZWN0IHRvIGdvIHRvIHRo aXMgY2FzZSAqLwo+ICsJCQlkZXZfZXJyKCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsCj4gKwkJCQki JXM6IFVuZXhwZWN0ZWQgU01CdXMgY29tbWFuZCAweCV4XG4iLAo+ICsJCQkJX19mdW5jX18sIHNz aWZfYm1jLT5zbWJ1c19jbWQpOwo+ICsJCQlicmVhazsKPiArCQl9Cj4gKwo+ICsJCWxlbiA9IChz c2lmX2JtYy0+YmxvY2tfbnVtID09IDB4RkYpID8KPiArCQkgICAgICAgc3NpZl9ibWMtPnJlbWFp bl9sZW4gKyAxIDogTUFYX1BBWUxPQURfUEVSX1RSQU5TQUNUSU9OOwo+ICsJCWlmIChzc2lmX2Jt Yy0+bXNnX2lkeCA9PSAobGVuICsgMSkpIHsKPiArCQkJcGVjID0gaTJjX3NtYnVzX3BlYyhwZWMs ICZsZW4sIDEpOwo+ICsJCQkqdmFsID0gaTJjX3NtYnVzX3BlYyhwZWMsIHNzaWZfYm1jLT5yZXNw b25zZV9idWYsIGxlbik7Cj4gKwkJfQo+ICsJCS8qIEludmFsaWRhdGUgcmVzcG9uc2UgYnVmZmVy IHRvIGRlbm90ZSBsYXN0IHJlc3BvbnNlIGlzIHNlbnQgKi8KPiArCQlpZiAoc3NpZl9ibWMtPmJs b2NrX251bSA9PSAweEZGICYmCj4gKwkJICAgIHNzaWZfYm1jLT5tc2dfaWR4ID4gKHNzaWZfYm1j LT5yZW1haW5fbGVuICsgcGVjX2xlbikpIHsKPiArCQkJY29tcGxldGVfcmVzcG9uc2Uoc3NpZl9i bWMpOwo+ICsJCX0KPiArCX0KPiArfQo+ICsKPiArc3RhdGljIHZvaWQgaGFuZGxlX3dyaXRlX3Jl Y2VpdmVkKHN0cnVjdCBzc2lmX2JtY19jdHggKnNzaWZfYm1jLCB1OCAqdmFsKQo+ICt7Cj4gKwl1 OCAqYnVmID0gKHU4ICopJnNzaWZfYm1jLT5yZXF1ZXN0Owo+ICsKPiArCWlmIChzc2lmX2JtYy0+ bXNnX2lkeCA+PSBzaXplb2Yoc3RydWN0IHNzaWZfbXNnKSkKPiArCQlyZXR1cm47Cj4gKwo+ICsJ c3dpdGNoIChzc2lmX2JtYy0+c21idXNfY21kKSB7Cj4gKwljYXNlIFNTSUZfSVBNSV9TSU5HTEVQ QVJUX1dSSVRFOgo+ICsJCWJ1Zltzc2lmX2JtYy0+bXNnX2lkeCAtIDFdID0gKnZhbDsKPiArCQlz c2lmX2JtYy0+bXNnX2lkeCsrOwo+ICsKPiArCQlicmVhazsKPiArCWNhc2UgU1NJRl9JUE1JX01V TFRJUEFSVF9XUklURV9TVEFSVDoKPiArCQlpZiAoc3NpZl9ibWMtPm1zZ19pZHggPT0gMSkKPiAr CQkJc3NpZl9ibWMtPnJlcXVlc3QubGVuID0gMDsKPiArCj4gKwkJZmFsbHRocm91Z2g7Cj4gKwlj YXNlIFNTSUZfSVBNSV9NVUxUSVBBUlRfV1JJVEVfTUlERExFOgo+ICsJCS8qIFRoZSBsZW4gc2hv dWxkIGFsd2F5cyBiZSAzMiAqLwo+ICsJCWlmIChzc2lmX2JtYy0+bXNnX2lkeCA9PSAxICYmICp2 YWwgIT0gTUFYX1BBWUxPQURfUEVSX1RSQU5TQUNUSU9OKQo+ICsJCQlkZXZfd2Fybigmc3NpZl9i bWMtPmNsaWVudC0+ZGV2LAo+ICsJCQkJICJXYXJuOiBJbnZhbGlkIE11bHRpcGFydCBXcml0ZSBs ZW4iKTsKPiArCj4gKwkJZmFsbHRocm91Z2g7Cj4gKwljYXNlIFNTSUZfSVBNSV9NVUxUSVBBUlRf V1JJVEVfRU5EOgo+ICsJCS8qIE11bHRpLXBhcnQgd3JpdGUsIDJuZCBieXRlIHJlY2VpdmVkIGlz IGxlbmd0aCAqLwo+ICsJCWlmIChzc2lmX2JtYy0+bXNnX2lkeCA9PSAxKSB7Cj4gKwkJCWlmICgq dmFsID4gTUFYX1BBWUxPQURfUEVSX1RSQU5TQUNUSU9OKQo+ICsJCQkJZGV2X3dhcm4oJnNzaWZf Ym1jLT5jbGllbnQtPmRldiwKPiArCQkJCQkgIldhcm46IEludmFsaWQgTXVsdGlwYXJ0IFdyaXRl IEVuZCBsZW4iKTsKPiArCj4gKwkJCXNzaWZfYm1jLT5yZXF1ZXN0LmxlbiArPSAqdmFsOwo+ICsJ CQlzc2lmX2JtYy0+cmVjdl9sZW4gPSAqdmFsOwo+ICsKPiArCQkJLyogcmVxdWVzdCBsZW4gc2hv dWxkIG5ldmVyIGV4Y2VlZGVkIDI1NSBieXRlcyAqLwo+ICsJCQlpZiAoc3NpZl9ibWMtPnJlcXVl c3QubGVuID4gMjU1KQo+ICsJCQkJZGV2X3dhcm4oJnNzaWZfYm1jLT5jbGllbnQtPmRldiwKPiAr CQkJCQkgIldhcm46IEludmFsaWQgcmVxdWVzdCBsZW4iKTsKPiArCj4gKwkJfSBlbHNlIHsKCllv dSBjaGVjayBtc2dfaWR4IGFib3ZlLCBidXQgSSdtIG5vdCBzdXJlIHRoYXQgY2hlY2sgd2lsbCBj b3ZlciB0aGlzCm9wZXJhdGlvbi4KCj4gKwkJCWJ1Zltzc2lmX2JtYy0+bXNnX2lkeCAtIDEgKwo+ ICsJCQkgICAgc3NpZl9ibWMtPnJlcXVlc3QubGVuIC0gc3NpZl9ibWMtPnJlY3ZfbGVuXQk9ICp2 YWw7Cj4gKwkJfQo+ICsKPiArCQlzc2lmX2JtYy0+bXNnX2lkeCsrOwo+ICsKPiArCQlicmVhazsK PiArCWRlZmF1bHQ6Cj4gKwkJLyogRG8gbm90IGV4cGVjdCB0byBnbyB0byB0aGlzIGNhc2UgKi8K PiArCQlkZXZfZXJyKCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsCj4gKwkJCSIlczogVW5leHBlY3Rl ZCBTTUJ1cyBjb21tYW5kIDB4JXhcbiIsCj4gKwkJCV9fZnVuY19fLCBzc2lmX2JtYy0+c21idXNf Y21kKTsKPiArCQlicmVhazsKPiArCX0KPiArfQo+ICsKPiArc3RhdGljIGJvb2wgdmFsaWRhdGVf cmVxdWVzdChzdHJ1Y3Qgc3NpZl9ibWNfY3R4ICpzc2lmX2JtYykKPiArewo+ICsJdTggcnBlYyA9 IDAsIGNwZWMgPSAwOwo+ICsJYm9vbCByZXQgPSB0cnVlOwo+ICsJdTggYWRkciwgaW5kZXg7Cj4g Kwl1OCAqYnVmOwo+ICsKCklzIHRoZXJlIGFueSBsZW5ndGggdmFsaWRhdGlvbiBmb3IgdXNpbmcg YnVmIGJlbG93PyAgSXQgbG9va3MgbGlrZSB5b3UKYXJlIGFjY2Vzc2luZyB3aXRob3V0IGNoZWNr aW5nIGxlbmd0aCwgYnV0IG1heWJlIEkgbWlzc2VkIHNvbWV0aGluZy4KCj4gKwlidWYgPSAodTgg Kikmc3NpZl9ibWMtPnJlcXVlc3Q7Cj4gKwlzd2l0Y2ggKHNzaWZfYm1jLT5zbWJ1c19jbWQpIHsK PiArCWNhc2UgU1NJRl9JUE1JX1NJTkdMRVBBUlRfV1JJVEU6Cj4gKwkJaWYgKChzc2lmX2JtYy0+ bXNnX2lkeCAtIDEpID09IHNzaWZfbXNnX2xlbigmc3NpZl9ibWMtPnJlcXVlc3QpKSB7Cj4gKwkJ CS8qIFBFQyBpcyBub3QgaW5jbHVkZWQgKi8KPiArCQkJc3NpZl9ibWMtPnBlY19zdXBwb3J0ID0g ZmFsc2U7Cj4gKwkJCXJldCA9IHRydWU7Cj4gKwkJCWdvdG8gZXhpdDsKPiArCQl9Cj4gKwo+ICsJ CWlmICgoc3NpZl9ibWMtPm1zZ19pZHggLSAxKSAhPSAoc3NpZl9tc2dfbGVuKCZzc2lmX2JtYy0+ cmVxdWVzdCkgKyAxKSkgewo+ICsJCQlkZXZfZXJyKCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsICJF cnJvcjogVW5leHBlY3RlZCBsZW5ndGggcmVjZWl2ZWQgJWRcbiIsCj4gKwkJCQlzc2lmX21zZ19s ZW4oJnNzaWZfYm1jLT5yZXF1ZXN0KSk7Cj4gKwkJCXJldCA9IGZhbHNlOwo+ICsJCQlnb3RvIGV4 aXQ7Cj4gKwkJfQo+ICsKPiArCQkvKiBQRUMgaXMgaW5jbHVkZWQgKi8KPiArCQlzc2lmX2JtYy0+ cGVjX3N1cHBvcnQgPSB0cnVlOwo+ICsJCXJwZWMgPSBidWZbc3NpZl9ibWMtPm1zZ19pZHggLSAy XTsKPiArCQlhZGRyID0gR0VUXzhCSVRfQUREUihzc2lmX2JtYy0+Y2xpZW50LT5hZGRyKTsKPiAr CQljcGVjID0gaTJjX3NtYnVzX3BlYyhjcGVjLCAmYWRkciwgMSk7Cj4gKwkJY3BlYyA9IGkyY19z bWJ1c19wZWMoY3BlYywgJnNzaWZfYm1jLT5zbWJ1c19jbWQsIDEpOwo+ICsJCWNwZWMgPSBpMmNf c21idXNfcGVjKGNwZWMsIGJ1Ziwgc3NpZl9tc2dfbGVuKCZzc2lmX2JtYy0+cmVxdWVzdCkpOwo+ ICsJCWlmIChycGVjICE9IGNwZWMpIHsKPiArCQkJZGV2X2Vycigmc3NpZl9ibWMtPmNsaWVudC0+ ZGV2LCAiQmFkIFBFQyAweCUwMnggdnMuIDB4JTAyeFxuIiwgcnBlYywgY3BlYyk7Cj4gKwkJCXJl dCA9IGZhbHNlOwo+ICsJCX0KPiArCj4gKwkJYnJlYWs7Cj4gKwljYXNlIFNTSUZfSVBNSV9NVUxU SVBBUlRfV1JJVEVfU1RBUlQ6Cj4gKwljYXNlIFNTSUZfSVBNSV9NVUxUSVBBUlRfV1JJVEVfTUlE RExFOgo+ICsJY2FzZSBTU0lGX0lQTUlfTVVMVElQQVJUX1dSSVRFX0VORDoKPiArCQlpbmRleCA9 IHNzaWZfYm1jLT5yZXF1ZXN0LmxlbiAtIHNzaWZfYm1jLT5yZWN2X2xlbjsKPiArCQlpZiAoKHNz aWZfYm1jLT5tc2dfaWR4IC0gMSArIGluZGV4KSA9PSBzc2lmX21zZ19sZW4oJnNzaWZfYm1jLT5y ZXF1ZXN0KSkgewo+ICsJCQkvKiBQRUMgaXMgbm90IGluY2x1ZGVkICovCj4gKwkJCXNzaWZfYm1j LT5wZWNfc3VwcG9ydCA9IGZhbHNlOwo+ICsJCQlyZXQgPSB0cnVlOwo+ICsJCQlnb3RvIGV4aXQ7 Cj4gKwkJfQo+ICsKPiArCQlpZiAoKHNzaWZfYm1jLT5tc2dfaWR4IC0gMSArIGluZGV4KSAhPSAo c3NpZl9tc2dfbGVuKCZzc2lmX2JtYy0+cmVxdWVzdCkgKyAxKSkgewo+ICsJCQlkZXZfZXJyKCZz c2lmX2JtYy0+Y2xpZW50LT5kZXYsICJFcnJvcjogVW5leHBlY3RlZCBsZW5ndGggcmVjZWl2ZWQg JWRcbiIsCj4gKwkJCQlzc2lmX21zZ19sZW4oJnNzaWZfYm1jLT5yZXF1ZXN0KSk7Cj4gKwkJCXJl dCA9IGZhbHNlOwo+ICsJCQlnb3RvIGV4aXQ7Cj4gKwkJfQo+ICsKPiArCQkvKiBQRUMgaXMgaW5j bHVkZWQgKi8KPiArCQlzc2lmX2JtYy0+cGVjX3N1cHBvcnQgPSB0cnVlOwo+ICsJCXJwZWMgPSBi dWZbc3NpZl9ibWMtPm1zZ19pZHggLSAyICsgaW5kZXhdOwo+ICsJCWFkZHIgPSBHRVRfOEJJVF9B RERSKHNzaWZfYm1jLT5jbGllbnQtPmFkZHIpOwo+ICsJCWNwZWMgPSBpMmNfc21idXNfcGVjKGNw ZWMsICZhZGRyLCAxKTsKPiArCQljcGVjID0gaTJjX3NtYnVzX3BlYyhjcGVjLCAmc3NpZl9ibWMt PnNtYnVzX2NtZCwgMSk7Cj4gKwkJY3BlYyA9IGkyY19zbWJ1c19wZWMoY3BlYywgJnNzaWZfYm1j LT5yZWN2X2xlbiwgMSk7CgpKdXN0IGN1cmlvdXMsIEknbSBub3Qgc3VyZSB0aGUgY2xpZW50IHNp ZGUgUEVDIGluIHRoZSBMaW51eCBkcml2ZXIgaGFzCmV2ZXIgYmVlbiB2YWxpZGF0ZWQuICBIYXZl IHlvdSB0ZXN0ZWQgYm90aCBzaWRlcz8KCj4gKwkJLyogQXMgU01CdXMgc3BlY2lmaWNhdGlvbiBk b2VzIG5vdCBhbGxvdyB0aGUgbGVuZ3RoCj4gKwkJICogKGJ5dGUgY291bnQpIGluIHRoZSBXcml0 ZS1CbG9jayBwcm90b2NvbCB0byBiZSB6ZXJvLgo+ICsJCSAqIFRoZXJlZm9yZSwgaXQgaXMgaWxs ZWdhbCB0byBoYXZlIHRoZSBsYXN0IE1pZGRsZQo+ICsJCSAqIHRyYW5zYWN0aW9uIGluIHRoZSBz ZXF1ZW5jZSBjYXJyeSAzMi1ieXRlIGFuZCBoYXZlCj4gKwkJICogYSBsZW5ndGggb2Yg4oCYMOKA mSBpbiB0aGUgRW5kIHRyYW5zYWN0aW9uLgo+ICsJCSAqIEJ1dCBzb21lIHVzZXJzIG1heSB0cnkg dG8gdXNlIHRoaXMgd2F5IGFuZCB3ZSBzaG91bGQKPiArCQkgKiBwcmV2ZW50IHNzaWZfYm1jIGRy aXZlciBicm9rZW4gaW4gdGhpcyBjYXNlLgo+ICsJCSAqLwo+ICsJCWlmIChzc2lmX2JtYy0+cmVj dl9sZW4gIT0gMCkKPiArCQkJY3BlYyA9IGkyY19zbWJ1c19wZWMoY3BlYywgYnVmICsgMSArIGlu ZGV4LCBzc2lmX2JtYy0+cmVjdl9sZW4pOwo+ICsKPiArCQlpZiAocnBlYyAhPSBjcGVjKSB7Cj4g KwkJCWRldl9lcnIoJnNzaWZfYm1jLT5jbGllbnQtPmRldiwgIkJhZCBQRUMgMHglMDJ4IHZzLiAw eCUwMnhcbiIsIHJwZWMsIGNwZWMpOwo+ICsJCQlyZXQgPSBmYWxzZTsKPiArCQl9Cj4gKwo+ICsJ CWJyZWFrOwo+ICsJZGVmYXVsdDoKPiArCQkvKiBEbyBub3QgZXhwZWN0IHRvIGdvIHRvIHRoaXMg Y2FzZSAqLwo+ICsJCWRldl9lcnIoJnNzaWZfYm1jLT5jbGllbnQtPmRldiwgIiVzOiBVbmV4cGVj dGVkIFNNQnVzIGNvbW1hbmQgMHgleFxuIiwKPiArCQkJX19mdW5jX18sIHNzaWZfYm1jLT5zbWJ1 c19jbWQpOwo+ICsJCXJldCA9IGZhbHNlOwo+ICsJCWJyZWFrOwo+ICsJfQo+ICsKPiArZXhpdDoK PiArCXJldHVybiByZXQ7Cj4gK30KPiArCgpKdXN0IGEgbml0LCBtb3JlIGEgZ2VuZXJhbCBjb2Rp bmcgc3R5bGUgY29tbWVudC4gIEl0J3MgYWxtb3N0IGFsd2F5cyBhCmJhZCBpZGVhIHRvIHB1dCBh IG5lZ2F0aXZlICh1bnN1cHBvcnRlZCkgaW50byBhIGNoZWNrIGZ1bmN0aW9uLiAgWW91Cm9mdGVu IGVuZCB1cCB3aXRoIHNvbWV0aGluZyBsaWtlOgoKCWlmICghdW5zdXBwb3J0ZWRfc21idXNfY21k KGMpKS4uLi4KCndoaWNoIGxvb2tzIGEgbGl0dGxlIHN0cmFuZ2UuICBEb3VibGUgbmVnYXRpdmVz IGNhbiBtYWtlIGl0IGhhcmQgdG8KZm9sbG93IGFuZCBsZWFkIHRvIG1pc3Rha2VzLiAgVGhpcyBv bmUgaXNuJ3QgdG9vIGJhZCwgYnV0IHNvbWV0aW1lcyBpdApjYW4gYmUuICBJdCdzIGJldHRlciB0 byBkbzoKCglpZiAoc3VwcG9ydGVkX3NtYnVzX2NtZChjKSkuLi4uCm9yCglpZiAoIXN1cHBvcnRl ZF9zbWJ1c19jbWQoYykpLi4uLgoKCj4gK3N0YXRpYyBib29sIHVuc3VwcG9ydGVkX3NtYnVzX2Nt ZCh1OCBjbWQpCj4gK3sKPiArCWlmIChjbWQgPT0gU1NJRl9JUE1JX1NJTkdMRVBBUlRfUkVBRCB8 fAo+ICsJICAgIGNtZCA9PSBTU0lGX0lQTUlfU0lOR0xFUEFSVF9XUklURSB8fAo+ICsJICAgIGNt ZCA9PSBTU0lGX0lQTUlfTVVMVElQQVJUX1dSSVRFX1NUQVJUIHx8Cj4gKwkgICAgY21kID09IFNT SUZfSVBNSV9NVUxUSVBBUlRfV1JJVEVfTUlERExFIHx8Cj4gKwkgICAgY21kID09IFNTSUZfSVBN SV9NVUxUSVBBUlRfV1JJVEVfRU5EIHx8Cj4gKwkgICAgY21kID09IFNTSUZfSVBNSV9NVUxUSVBB UlRfUkVBRF9TVEFSVCB8fAo+ICsJICAgIGNtZCA9PSBTU0lGX0lQTUlfTVVMVElQQVJUX1JFQURf TUlERExFKQo+ICsJCXJldHVybiBmYWxzZTsKPiArCj4gKwlyZXR1cm4gdHJ1ZTsKPiArfQo+ICsK PiArc3RhdGljIHZvaWQgcHJvY2Vzc19zbWJ1c19jbWQoc3RydWN0IHNzaWZfYm1jX2N0eCAqc3Np Zl9ibWMsIHU4ICp2YWwpCj4gK3sKPiArCS8qIFNNQlVTIGNvbW1hbmQgY2FuIHZhcnkgKHNpbmds ZSBvciBtdWx0aS1wYXJ0KSAqLwo+ICsJc3NpZl9ibWMtPnNtYnVzX2NtZCA9ICp2YWw7Cj4gKwlz c2lmX2JtYy0+bXNnX2lkeCsrOwo+ICsKPiArCWlmICh1bnN1cHBvcnRlZF9zbWJ1c19jbWQoKnZh bCkpCj4gKwkJZGV2X3dhcm4oJnNzaWZfYm1jLT5jbGllbnQtPmRldiwgIldhcm46IFVua25vd24g U01CdXMgY29tbWFuZCIpOwo+ICsKPiArCWlmICgqdmFsID09IFNTSUZfSVBNSV9TSU5HTEVQQVJU X1dSSVRFIHx8Cj4gKwkgICAgKnZhbCA9PSBTU0lGX0lQTUlfTVVMVElQQVJUX1dSSVRFX1NUQVJU KSB7Cj4gKwkJLyoKPiArCQkgKiBUaGUgcmVzcG9uc2UgbWF5YmUgbm90IGNvbWUgaW4tdGltZSwg Y2F1c2luZyBob3N0IFNTSUYgZHJpdmVyCj4gKwkJICogdG8gdGltZW91dCBhbmQgcmVzZW5kIGEg bmV3IHJlcXVlc3QuIEluIHN1Y2ggY2FzZSBjaGVjayBmb3IKPiArCQkgKiBwZW5kaW5nIHJlc3Bv bnNlIGFuZCBjbGVhciBpdAo+ICsJCSAqLwo+ICsJCWlmIChzc2lmX2JtYy0+cmVzcG9uc2VfaW5f cHJvZ3Jlc3MpCj4gKwkJCWNvbXBsZXRlX3Jlc3BvbnNlKHNzaWZfYm1jKTsKPiArCX0KPiArfQo+ ICsKPiArc3RhdGljIHZvaWQgb25fcmVhZF9yZXF1ZXN0ZWRfZXZlbnQoc3RydWN0IHNzaWZfYm1j X2N0eCAqc3NpZl9ibWMsIHU4ICp2YWwpCj4gK3sKPiArCWlmIChzc2lmX2JtYy0+c3RhdGUgPT0g U1NJRl9SRUFEWSB8fAo+ICsJICAgIHNzaWZfYm1jLT5zdGF0ZSA9PSBTU0lGX1NUQVJUIHx8Cj4g KwkgICAgc3NpZl9ibWMtPnN0YXRlID09IFNTSUZfUkVRX1JFQ1ZJTkcgfHwKPiArCSAgICBzc2lm X2JtYy0+c3RhdGUgPT0gU1NJRl9SRVNfU0VORElORykgewo+ICsJCXNzaWZfYm1jLT5zdGF0ZSA9 IFNTSUZfQkFEX1NNQlVTOwo+ICsJCWRldl93YXJuKCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsCj4g KwkJCSAiV2FybjogJXMgdW5leHBlY3RlZCBSRUFEIFJFUVVFU1RFRCBpbiBzdGF0ZT0lc1xuIiwK PiArCQkJIF9fZnVuY19fLCBzdGF0ZV90b19zdHJpbmcoc3NpZl9ibWMtPnN0YXRlKSk7Cj4gKwo+ ICsJfSBlbHNlIGlmIChzc2lmX2JtYy0+c3RhdGUgPT0gU1NJRl9TTUJVU19DTUQpIHsKPiArCQlz c2lmX2JtYy0+c3RhdGUgPSBTU0lGX1JFU19TRU5ESU5HOwo+ICsJfQo+ICsKPiArCXNzaWZfYm1j LT5tc2dfaWR4ID0gMDsKPiArCWlmIChzc2lmX2JtYy0+aXNfc2luZ2xlcGFydF9yZWFkKQo+ICsJ CSp2YWwgPSBzc2lmX2JtYy0+cmVzcG9uc2UubGVuOwo+ICsJZWxzZQo+ICsJCXNldF9tdWx0aXBh cnRfcmVzcG9uc2VfYnVmZmVyKHNzaWZfYm1jLCB2YWwpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9p ZCBvbl9yZWFkX3Byb2Nlc3NlZF9ldmVudChzdHJ1Y3Qgc3NpZl9ibWNfY3R4ICpzc2lmX2JtYywg dTggKnZhbCkKPiArewo+ICsJaWYgKHNzaWZfYm1jLT5zdGF0ZSA9PSBTU0lGX1JFQURZIHx8Cj4g KwkgICAgc3NpZl9ibWMtPnN0YXRlID09IFNTSUZfU1RBUlQgfHwKPiArCSAgICBzc2lmX2JtYy0+ c3RhdGUgPT0gU1NJRl9SRVFfUkVDVklORyB8fAo+ICsJICAgIHNzaWZfYm1jLT5zdGF0ZSA9PSBT U0lGX1NNQlVTX0NNRCkgewo+ICsJCWRldl93YXJuKCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsCj4g KwkJCSAiV2FybjogJXMgdW5leHBlY3RlZCBSRUFEIFBST0NFU1NFRCBpbiBzdGF0ZT0lc1xuIiwK PiArCQkJIF9fZnVuY19fLCBzdGF0ZV90b19zdHJpbmcoc3NpZl9ibWMtPnN0YXRlKSk7Cj4gKwkJ c3NpZl9ibWMtPnN0YXRlID0gU1NJRl9CQURfU01CVVM7Cj4gKwl9Cj4gKwo+ICsJaGFuZGxlX3Jl YWRfcHJvY2Vzc2VkKHNzaWZfYm1jLCB2YWwpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9pZCBvbl93 cml0ZV9yZXF1ZXN0ZWRfZXZlbnQoc3RydWN0IHNzaWZfYm1jX2N0eCAqc3NpZl9ibWMsIHU4ICp2 YWwpCj4gK3sKPiArCXNzaWZfYm1jLT5tc2dfaWR4ID0gMDsKPiArCj4gKwlpZiAoc3NpZl9ibWMt PnN0YXRlID09IFNTSUZfUkVBRFkgfHwgc3NpZl9ibWMtPnN0YXRlID09IFNTSUZfU01CVVNfQ01E KSB7Cj4gKwkJc3NpZl9ibWMtPnN0YXRlID0gU1NJRl9TVEFSVDsKPiArCj4gKwl9IGVsc2UgaWYg KHNzaWZfYm1jLT5zdGF0ZSA9PSBTU0lGX1NUQVJUIHx8Cj4gKwkJICAgc3NpZl9ibWMtPnN0YXRl ID09IFNTSUZfUkVRX1JFQ1ZJTkcgfHwKPiArCQkgICBzc2lmX2JtYy0+c3RhdGUgPT0gU1NJRl9S RVNfU0VORElORykgewo+ICsJCWRldl93YXJuKCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsCj4gKwkJ CSAiV2FybjogJXMgdW5leHBlY3RlZCBXUklURSBSRVFVRVNUIGluIHN0YXRlPSVzXG4iLAo+ICsJ CQkgX19mdW5jX18sIHN0YXRlX3RvX3N0cmluZyhzc2lmX2JtYy0+c3RhdGUpKTsKPiArCQlzc2lm X2JtYy0+c3RhdGUgPSBTU0lGX0JBRF9TTUJVUzsKPiArCX0KPiArfQo+ICsKPiArc3RhdGljIHZv aWQgb25fd3JpdGVfcmVjZWl2ZWRfZXZlbnQoc3RydWN0IHNzaWZfYm1jX2N0eCAqc3NpZl9ibWMs IHU4ICp2YWwpCj4gK3sKPiArCWlmIChzc2lmX2JtYy0+c3RhdGUgPT0gU1NJRl9SRUFEWSB8fCBz c2lmX2JtYy0+c3RhdGUgPT0gU1NJRl9SRVNfU0VORElORykgewo+ICsJCWRldl93YXJuKCZzc2lm X2JtYy0+Y2xpZW50LT5kZXYsCj4gKwkJCSAiV2FybjogJXMgdW5leHBlY3RlZCBXUklURSBSRUNF SVZFRCBpbiBzdGF0ZT0lc1xuIiwKPiArCQkJIF9fZnVuY19fLCBzdGF0ZV90b19zdHJpbmcoc3Np Zl9ibWMtPnN0YXRlKSk7Cj4gKwkJc3NpZl9ibWMtPnN0YXRlID0gU1NJRl9CQURfU01CVVM7Cj4g Kwl9IGVsc2UgaWYgKHNzaWZfYm1jLT5zdGF0ZSA9PSBTU0lGX1NUQVJUKSB7Cj4gKwkJc3NpZl9i bWMtPnN0YXRlID0gU1NJRl9TTUJVU19DTUQ7Cj4gKwl9IGVsc2UgaWYgKHNzaWZfYm1jLT5zdGF0 ZSA9PSBTU0lGX1NNQlVTX0NNRCkgewo+ICsJCXNzaWZfYm1jLT5zdGF0ZSA9IFNTSUZfUkVRX1JF Q1ZJTkc7Cj4gKwl9Cj4gKwo+ICsJLyogVGhpcyBpcyByZXNwb25zZSBzZW5kaW5nIHN0YXRlICov Cj4gKwlpZiAoc3NpZl9ibWMtPnN0YXRlID09IFNTSUZfUkVRX1JFQ1ZJTkcpCj4gKwkJaGFuZGxl X3dyaXRlX3JlY2VpdmVkKHNzaWZfYm1jLCB2YWwpOwo+ICsJZWxzZSBpZiAoc3NpZl9ibWMtPnN0 YXRlID09IFNTSUZfU01CVVNfQ01EKQo+ICsJCXByb2Nlc3Nfc21idXNfY21kKHNzaWZfYm1jLCB2 YWwpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9pZCBvbl9zdG9wX2V2ZW50KHN0cnVjdCBzc2lmX2Jt Y19jdHggKnNzaWZfYm1jLCB1OCAqdmFsKQo+ICt7Cj4gKwlpZiAoc3NpZl9ibWMtPnN0YXRlID09 IFNTSUZfUkVBRFkgfHwKPiArCSAgICBzc2lmX2JtYy0+c3RhdGUgPT0gU1NJRl9TVEFSVCB8fAo+ ICsJICAgIHNzaWZfYm1jLT5zdGF0ZSA9PSBTU0lGX1NNQlVTX0NNRCkgewo+ICsJCWRldl93YXJu KCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsCj4gKwkJCSAiV2FybjogJXMgdW5leHBlY3RlZCBTTEFW RSBTVE9QIGluIHN0YXRlPSVzXG4iLAo+ICsJCQkgX19mdW5jX18sIHN0YXRlX3RvX3N0cmluZyhz c2lmX2JtYy0+c3RhdGUpKTsKPiArCj4gKwl9IGVsc2UgaWYgKHNzaWZfYm1jLT5zdGF0ZSA9PSBT U0lGX0JBRF9TTUJVUykgewo+ICsJCWRldl93YXJuKCZzc2lmX2JtYy0+Y2xpZW50LT5kZXYsCj4g KwkJCSAiV2FybjogJXMgcmVjZWl2ZWQgU0xBVkUgU1RPUCBmcm9tIGJhZCBzdGF0ZT0lc1xuIiwK PiArCQkJIF9fZnVuY19fLCBzdGF0ZV90b19zdHJpbmcoc3NpZl9ibWMtPnN0YXRlKSk7Cj4gKwo+ ICsJfSBlbHNlIGlmIChzc2lmX2JtYy0+c3RhdGUgPT0gU1NJRl9SRVFfUkVDVklORykgewo+ICsJ CS8qIEEgQk1DIHRoYXQgcmVjZWl2ZXMgYW4gaW52YWxpZCByZXF1ZXN0IGRyb3AgdGhlIGRhdGEg Zm9yIHRoZSB3cml0ZQo+ICsJCSAqIHRyYW5zYWN0aW9uIGFuZCBhbnkgZnVydGhlciB0cmFuc2Fj dGlvbnMgKHJlYWQgb3Igd3JpdGUpIHVudGlsCj4gKwkJICogdGhlIG5leHQgdmFsaWQgcmVhZCBv ciB3cml0ZSBTdGFydCB0cmFuc2FjdGlvbiBpcyByZWNlaXZlZAo+ICsJCSAqLwo+ICsJCWlmICgh dmFsaWRhdGVfcmVxdWVzdChzc2lmX2JtYykpCj4gKwkJCWRldl9lcnIoJnNzaWZfYm1jLT5jbGll bnQtPmRldiwgIkVycm9yOiBpbnZhbGlkIHBlY1xuIik7Cj4gKwkJZWxzZSBpZiAoc3NpZl9ibWMt PnNtYnVzX2NtZCA9PSBTU0lGX0lQTUlfU0lOR0xFUEFSVF9XUklURSB8fAo+ICsJCQkgc3NpZl9i bWMtPnNtYnVzX2NtZCA9PSBTU0lGX0lQTUlfTVVMVElQQVJUX1dSSVRFX0VORCkKPiArCQkJaGFu ZGxlX3JlcXVlc3Qoc3NpZl9ibWMpOwo+ICsJfQo+ICsKPiArCXNzaWZfYm1jLT5zdGF0ZSA9IFNT SUZfUkVBRFk7Cj4gKwkvKiBSZXNldCBtZXNzYWdlIGluZGV4ICovCj4gKwlzc2lmX2JtYy0+bXNn X2lkeCA9IDA7Cj4gK30KPiArCj4gKy8qCj4gKyAqIENhbGxiYWNrIGZ1bmN0aW9uIHRvIGhhbmRs ZSBJMkMgc2xhdmUgZXZlbnRzCj4gKyAqLwo+ICtzdGF0aWMgaW50IHNzaWZfYm1jX2NiKHN0cnVj dCBpMmNfY2xpZW50ICpjbGllbnQsIGVudW0gaTJjX3NsYXZlX2V2ZW50IGV2ZW50LCB1OCAqdmFs KQo+ICt7Cj4gKwl1bnNpZ25lZCBsb25nIGZsYWdzOwo+ICsJc3RydWN0IHNzaWZfYm1jX2N0eCAq c3NpZl9ibWMgPSBpMmNfZ2V0X2NsaWVudGRhdGEoY2xpZW50KTsKPiArCWludCByZXQgPSAwOwo+ ICsKPiArCXNwaW5fbG9ja19pcnFzYXZlKCZzc2lmX2JtYy0+bG9jaywgZmxhZ3MpOwo+ICsKPiAr CXN3aXRjaCAoZXZlbnQpIHsKPiArCWNhc2UgSTJDX1NMQVZFX1JFQURfUkVRVUVTVEVEOgo+ICsJ CW9uX3JlYWRfcmVxdWVzdGVkX2V2ZW50KHNzaWZfYm1jLCB2YWwpOwo+ICsJCWJyZWFrOwo+ICsK PiArCWNhc2UgSTJDX1NMQVZFX1dSSVRFX1JFUVVFU1RFRDoKPiArCQlvbl93cml0ZV9yZXF1ZXN0 ZWRfZXZlbnQoc3NpZl9ibWMsIHZhbCk7Cj4gKwkJYnJlYWs7Cj4gKwo+ICsJY2FzZSBJMkNfU0xB VkVfUkVBRF9QUk9DRVNTRUQ6Cj4gKwkJb25fcmVhZF9wcm9jZXNzZWRfZXZlbnQoc3NpZl9ibWMs IHZhbCk7Cj4gKwkJYnJlYWs7Cj4gKwo+ICsJY2FzZSBJMkNfU0xBVkVfV1JJVEVfUkVDRUlWRUQ6 Cj4gKwkJb25fd3JpdGVfcmVjZWl2ZWRfZXZlbnQoc3NpZl9ibWMsIHZhbCk7Cj4gKwkJYnJlYWs7 Cj4gKwo+ICsJY2FzZSBJMkNfU0xBVkVfU1RPUDoKPiArCQlvbl9zdG9wX2V2ZW50KHNzaWZfYm1j LCB2YWwpOwo+ICsJCWJyZWFrOwo+ICsKPiArCWRlZmF1bHQ6Cj4gKwkJZGV2X3dhcm4oJnNzaWZf Ym1jLT5jbGllbnQtPmRldiwgIldhcm46IFVua25vd24gaTJjIHNsYXZlIGV2ZW50XG4iKTsKPiAr CQlicmVhazsKPiArCX0KPiArCj4gKwlpZiAoc3NpZl9ibWMtPmJ1c3kpCj4gKwkJcmV0ID0gLUVC VVNZOwo+ICsKPiArCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJnNzaWZfYm1jLT5sb2NrLCBmbGFn cyk7Cj4gKwo+ICsJcmV0dXJuIHJldDsKPiArfQo+ICsKPiArc3RhdGljIGludCBzc2lmX2JtY19w cm9iZShzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50LCBjb25zdCBzdHJ1Y3QgaTJjX2RldmljZV9p ZCAqaWQpCj4gK3sKPiArCXN0cnVjdCBzc2lmX2JtY19jdHggKnNzaWZfYm1jOwo+ICsJaW50IHJl dDsKPiArCj4gKwlzc2lmX2JtYyA9IGRldm1fa3phbGxvYygmY2xpZW50LT5kZXYsIHNpemVvZigq c3NpZl9ibWMpLCBHRlBfS0VSTkVMKTsKPiArCWlmICghc3NpZl9ibWMpCj4gKwkJcmV0dXJuIC1F Tk9NRU07Cj4gKwo+ICsJc3Bpbl9sb2NrX2luaXQoJnNzaWZfYm1jLT5sb2NrKTsKPiArCj4gKwlp bml0X3dhaXRxdWV1ZV9oZWFkKCZzc2lmX2JtYy0+d2FpdF9xdWV1ZSk7Cj4gKwlzc2lmX2JtYy0+ cmVxdWVzdF9hdmFpbGFibGUgPSBmYWxzZTsKPiArCXNzaWZfYm1jLT5yZXNwb25zZV9pbl9wcm9n cmVzcyA9IGZhbHNlOwo+ICsJc3NpZl9ibWMtPmJ1c3kgPSBmYWxzZTsKPiArCXNzaWZfYm1jLT5y ZXNwb25zZV90aW1lcl9pbml0ZWQgPSBmYWxzZTsKPiArCj4gKwkvKiBSZWdpc3RlciBtaXNjIGRl dmljZSBpbnRlcmZhY2UgKi8KPiArCXNzaWZfYm1jLT5taXNjZGV2Lm1pbm9yID0gTUlTQ19EWU5B TUlDX01JTk9SOwo+ICsJc3NpZl9ibWMtPm1pc2NkZXYubmFtZSA9IERFVklDRV9OQU1FOwo+ICsJ c3NpZl9ibWMtPm1pc2NkZXYuZm9wcyA9ICZzc2lmX2JtY19mb3BzOwo+ICsJc3NpZl9ibWMtPm1p c2NkZXYucGFyZW50ID0gJmNsaWVudC0+ZGV2Owo+ICsJcmV0ID0gbWlzY19yZWdpc3Rlcigmc3Np Zl9ibWMtPm1pc2NkZXYpOwo+ICsJaWYgKHJldCkKPiArCQlnb3RvIG91dDsKPiArCj4gKwlzc2lm X2JtYy0+Y2xpZW50ID0gY2xpZW50Owo+ICsJc3NpZl9ibWMtPmNsaWVudC0+ZmxhZ3MgfD0gSTJD X0NMSUVOVF9TTEFWRTsKPiArCj4gKwkvKiBSZWdpc3RlciBJMkMgc2xhdmUgKi8KPiArCWkyY19z ZXRfY2xpZW50ZGF0YShjbGllbnQsIHNzaWZfYm1jKTsKPiArCXJldCA9IGkyY19zbGF2ZV9yZWdp c3RlcihjbGllbnQsIHNzaWZfYm1jX2NiKTsKPiArCWlmIChyZXQpIHsKPiArCQltaXNjX2RlcmVn aXN0ZXIoJnNzaWZfYm1jLT5taXNjZGV2KTsKPiArCQlnb3RvIG91dDsKPiArCX0KPiArCj4gKwly ZXR1cm4gMDsKPiArb3V0Ogo+ICsJZGV2bV9rZnJlZSgmY2xpZW50LT5kZXYsIHNzaWZfYm1jKTsK PiArCXJldHVybiByZXQ7Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQgc3NpZl9ibWNfcmVtb3ZlKHN0 cnVjdCBpMmNfY2xpZW50ICpjbGllbnQpCj4gK3sKPiArCXN0cnVjdCBzc2lmX2JtY19jdHggKnNz aWZfYm1jID0gaTJjX2dldF9jbGllbnRkYXRhKGNsaWVudCk7Cj4gKwo+ICsJaTJjX3NsYXZlX3Vu cmVnaXN0ZXIoY2xpZW50KTsKPiArCW1pc2NfZGVyZWdpc3Rlcigmc3NpZl9ibWMtPm1pc2NkZXYp Owo+ICsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IG9mX2Rl dmljZV9pZCBzc2lmX2JtY19tYXRjaFtdID0gewo+ICsJeyAuY29tcGF0aWJsZSA9ICJhbXBlcmUs c3NpZi1ibWMiIH0sCj4gKwl7IH0sCj4gK307Cj4gKwo+ICtzdGF0aWMgY29uc3Qgc3RydWN0IGky Y19kZXZpY2VfaWQgc3NpZl9ibWNfaWRbXSA9IHsKPiArCXsgREVWSUNFX05BTUUsIDAgfSwKPiAr CXsgfSwKPiArfTsKPiArCj4gK01PRFVMRV9ERVZJQ0VfVEFCTEUoaTJjLCBzc2lmX2JtY19pZCk7 Cj4gKwo+ICtzdGF0aWMgc3RydWN0IGkyY19kcml2ZXIgc3NpZl9ibWNfZHJpdmVyID0gewo+ICsJ LmRyaXZlciAgICAgICAgID0gewo+ICsJCS5uYW1lICAgICAgICAgICA9IERFVklDRV9OQU1FLAo+ ICsJCS5vZl9tYXRjaF90YWJsZSA9IHNzaWZfYm1jX21hdGNoLAo+ICsJfSwKPiArCS5wcm9iZSAg ICAgICAgICA9IHNzaWZfYm1jX3Byb2JlLAo+ICsJLnJlbW92ZSAgICAgICAgID0gc3NpZl9ibWNf cmVtb3ZlLAo+ICsJLmlkX3RhYmxlICAgICAgID0gc3NpZl9ibWNfaWQsCj4gK307Cj4gKwo+ICtt b2R1bGVfaTJjX2RyaXZlcihzc2lmX2JtY19kcml2ZXIpOwo+ICsKPiArTU9EVUxFX0FVVEhPUigi UXVhbiBOZ3V5ZW4gPHF1YW5Ab3MuYW1wZXJlY29tcHV0aW5nLmNvbT4iKTsKPiArTU9EVUxFX0FV VEhPUigiQ2h1b25nIFRyYW4gPGNodW9uZ0Bvcy5hbXBlcmVjb21wdXRpbmcuY29tPiIpOwo+ICtN T0RVTEVfREVTQ1JJUFRJT04oIkxpbnV4IGRldmljZSBkcml2ZXIgb2YgdGhlIEJNQyBJUE1JIFNT SUYgaW50ZXJmYWNlLiIpOwo+ICtNT0RVTEVfTElDRU5TRSgiR1BMIik7Cj4gZGlmZiAtLWdpdCBh L2RyaXZlcnMvY2hhci9pcG1pL3NzaWZfYm1jLmggYi9kcml2ZXJzL2NoYXIvaXBtaS9zc2lmX2Jt Yy5oCj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQKPiBpbmRleCAwMDAwMDAwMDAwMDAuLjlhMjZmM2My NTJjYwo+IC0tLSAvZGV2L251bGwKPiArKysgYi9kcml2ZXJzL2NoYXIvaXBtaS9zc2lmX2JtYy5o Cj4gQEAgLTAsMCArMSwxMDIgQEAKPiArLyogU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0y LjArICovCj4gKy8qCj4gKyAqIFRoZSBkcml2ZXIgZm9yIEJNQyBzaWRlIG9mIFNTSUYgaW50ZXJm YWNlCj4gKyAqCj4gKyAqIENvcHlyaWdodCAoYykgMjAyMiwgQW1wZXJlIENvbXB1dGluZyBMTEMK PiArICoKPiArICovCj4gKyNpZm5kZWYgX19TU0lGX0JNQ19IX18KPiArI2RlZmluZSBfX1NTSUZf Qk1DX0hfXwo+ICsKPiArI2RlZmluZSBERVZJQ0VfTkFNRQkJCQkiaXBtaS1zc2lmLWhvc3QiCj4g Kwo+ICsjZGVmaW5lIEdFVF84QklUX0FERFIoYWRkcl83Yml0KQkJKCgoYWRkcl83Yml0KSA8PCAx KSAmIDB4ZmYpCj4gKwo+ICsvKiBBIHN0YW5kYXJkIFNNQnVzIFRyYW5zYWN0aW9uIGlzIGxpbWl0 ZWQgdG8gMzIgZGF0YSBieXRlcyAqLwo+ICsjZGVmaW5lIE1BWF9QQVlMT0FEX1BFUl9UUkFOU0FD VElPTgkJMzIKPiArCj4gKyNkZWZpbmUgTUFYX0lQTUlfREFUQV9QRVJfU1RBUlRfVFJBTlNBQ1RJ T04JMzAKPiArI2RlZmluZSBNQVhfSVBNSV9EQVRBX1BFUl9NSURETEVfVFJBTlNBQ1RJT04JMzEK PiArCj4gKyNkZWZpbmUgU1NJRl9JUE1JX1NJTkdMRVBBUlRfV1JJVEUJCTB4Mgo+ICsjZGVmaW5l IFNTSUZfSVBNSV9TSU5HTEVQQVJUX1JFQUQJCTB4Mwo+ICsjZGVmaW5lIFNTSUZfSVBNSV9NVUxU SVBBUlRfV1JJVEVfU1RBUlQJCTB4Ngo+ICsjZGVmaW5lIFNTSUZfSVBNSV9NVUxUSVBBUlRfV1JJ VEVfTUlERExFCTB4Nwo+ICsjZGVmaW5lIFNTSUZfSVBNSV9NVUxUSVBBUlRfV1JJVEVfRU5ECQkw eDgKPiArI2RlZmluZSBTU0lGX0lQTUlfTVVMVElQQVJUX1JFQURfU1RBUlQJCTB4Mwo+ICsjZGVm aW5lIFNTSUZfSVBNSV9NVUxUSVBBUlRfUkVBRF9NSURETEUJCTB4OQo+ICsKPiArI2RlZmluZSBN U0dfUEFZTE9BRF9MRU5fTUFYCQkJMjUyCj4gKy8qCj4gKyAqIElQTUkgMi4wIFNwZWMsIHNlY3Rp b24gMTIuNyBTU0lGIFRpbWluZywKPiArICogUmVxdWVzdC10by1SZXNwb25zZSBUaW1lIGlzIFQ2 bWF4KDI1MG1zKSAtIFQxbWF4KDIwbXMpIC0gM21zID0gMjI3bXMKPiArICogUmVjb3ZlciBzc2lm X2JtYyBmcm9tIGJ1c3kgc3RhdGUgaWYgaXQgdGFrZXMgdXB0byA1MDBtcwo+ICsgKi8KPiArI2Rl ZmluZSBSRVNQT05TRV9USU1FT1VUCQkJNTAwIC8qIG1zICovCj4gKwo+ICtzdHJ1Y3Qgc3NpZl9t c2cgewo+ICsJdTggbGVuOwo+ICsJdTggbmV0Zm5fbHVuOwo+ICsJdTggY21kOwo+ICsJdTggcGF5 bG9hZFtNU0dfUEFZTE9BRF9MRU5fTUFYXTsKPiArfSBfX3BhY2tlZDsKPiArCj4gK3N0YXRpYyBp bmxpbmUgdTMyIHNzaWZfbXNnX2xlbihzdHJ1Y3Qgc3NpZl9tc2cgKnNzaWZfbXNnKQo+ICt7Cj4g KwlyZXR1cm4gc3NpZl9tc2ctPmxlbiArIDE7Cj4gK30KPiArCj4gKy8qCj4gKyAqIFNTSUYgaW50 ZXJuYWwgc3RhdGVzOgo+ICsgKiAgIFNTSUZfUkVBRFkgICAgICAgICAweDAwIDogUmVhZHkgc3Rh dGUKPiArICogICBTU0lGX1NUQVJUICAgICAgICAgMHgwMSA6IFN0YXJ0IHNtYnVzIHRyYW5zYWN0 aW9uCj4gKyAqICAgU1NJRl9TTUJVU19DTUQgICAgIDB4MDIgOiBSZWNlaXZlZCBTTUJ1cyBjb21t YW5kCj4gKyAqICAgU1NJRl9SRVFfUkVDVklORyAgIDB4MDMgOiBSZWNlaXZpbmcgcmVxdWVzdAo+ ICsgKiAgIFNTSUZfUkVTX1NFTkRJTkcgICAweDA0IDogU2VuZGluZyByZXNwb25zZQo+ICsgKiAg IFNTSUZfQkFEX1NNQlVTICAgICAweDA1IDogQmFkIFNNYnVzIHRyYW5zYWN0aW9uCj4gKyAqLwo+ ICtlbnVtIHNzaWZfc3RhdGUgewo+ICsJU1NJRl9SRUFEWSwKPiArCVNTSUZfU1RBUlQsCj4gKwlT U0lGX1NNQlVTX0NNRCwKPiArCVNTSUZfUkVRX1JFQ1ZJTkcsCj4gKwlTU0lGX1JFU19TRU5ESU5H LAo+ICsJU1NJRl9CQURfU01CVVMsCj4gKwlTU0lGX1NUQVRFX01BWAo+ICt9Owo+ICsKPiArc3Ry dWN0IHNzaWZfYm1jX2N0eCB7Cj4gKwlzdHJ1Y3QgaTJjX2NsaWVudAkqY2xpZW50Owo+ICsJc3Ry dWN0IG1pc2NkZXZpY2UJbWlzY2RldjsKPiArCXNpemVfdAkJCW1zZ19pZHg7Cj4gKwlib29sCQkJ cGVjX3N1cHBvcnQ7Cj4gKwkvKiBzc2lmIGJtYyBzcGlubG9jayAqLwo+ICsJc3BpbmxvY2tfdAkJ bG9jazsKPiArCXdhaXRfcXVldWVfaGVhZF90CXdhaXRfcXVldWU7Cj4gKwl1OAkJCXJ1bm5pbmc7 Cj4gKwllbnVtIHNzaWZfc3RhdGUJCXN0YXRlOwo+ICsJdTgJCQlzbWJ1c19jbWQ7Cj4gKwkvKiBU aW1lb3V0IHdhaXRpbmcgZm9yIHJlc3BvbnNlICovCj4gKwlzdHJ1Y3QgdGltZXJfbGlzdAlyZXNw b25zZV90aW1lcjsKPiArCWJvb2wgICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlX3RpbWVyX2lu aXRlZDsKPiArCS8qIEZsYWcgdG8gaWRlbnRpZnkgYSBNdWx0aS1wYXJ0IFJlYWQgVHJhbnNhY3Rp b24gKi8KPiArCWJvb2wJCQlpc19zaW5nbGVwYXJ0X3JlYWQ7Cj4gKwl1OAkJCW5ieXRlc19wcm9j ZXNzZWQ7Cj4gKwl1OAkJCXJlbWFpbl9sZW47Cj4gKwl1OAkJCXJlY3ZfbGVuOwo+ICsJLyogQmxv Y2sgTnVtYmVyIG9mIGEgTXVsdGktcGFydCBSZWFkIFRyYW5zYWN0aW9uICovCj4gKwl1OAkJCWJs b2NrX251bTsKPiArCWJvb2wJCQlyZXF1ZXN0X2F2YWlsYWJsZTsKPiArCWJvb2wJCQlyZXNwb25z ZV9pbl9wcm9ncmVzczsKPiArCWJvb2wJCQlidXN5Owo+ICsJLyogUmVzcG9uc2UgYnVmZmVyIGZv ciBNdWx0aS1wYXJ0IFJlYWQgVHJhbnNhY3Rpb24gKi8KPiArCXU4CQkJcmVzcG9uc2VfYnVmW01B WF9QQVlMT0FEX1BFUl9UUkFOU0FDVElPTl07Cj4gKwlzdHJ1Y3Qgc3NpZl9tc2cJCXJlc3BvbnNl Owo+ICsJc3RydWN0IHNzaWZfbXNnCQlyZXF1ZXN0Owo+ICt9Owo+ICsKPiArc3RhdGljIGlubGlu ZSBzdHJ1Y3Qgc3NpZl9ibWNfY3R4ICp0b19zc2lmX2JtYyhzdHJ1Y3QgZmlsZSAqZmlsZSkKPiAr ewo+ICsJcmV0dXJuIGNvbnRhaW5lcl9vZihmaWxlLT5wcml2YXRlX2RhdGEsIHN0cnVjdCBzc2lm X2JtY19jdHgsIG1pc2NkZXYpOwo+ICt9Cj4gKyNlbmRpZiAvKiBfX1NTSUZfQk1DX0hfXyAqLwo+ IC0tIAo+IDIuMzUuMQo+IAo+IAoKX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX18KbGludXgtYXJtLWtlcm5lbCBtYWlsaW5nIGxpc3QKbGludXgtYXJtLWtlcm5l bEBsaXN0cy5pbmZyYWRlYWQub3JnCmh0dHA6Ly9saXN0cy5pbmZyYWRlYWQub3JnL21haWxtYW4v bGlzdGluZm8vbGludXgtYXJtLWtlcm5lbAo=