From b3a75be74808170515cae50fffa537dd7b8bd932 Mon Sep 17 00:00:00 2001 From: Arun Murthy Date: Wed, 8 Feb 2012 17:45:15 +0530 Subject: u8500-shrm: secure the access of GOP register Before accessing the GOP registers checks are present to verify the modem reset status and the availability of hostaccess_port. There are chances of preemtion happening between the check and access to GOP during which if modem has reset, then we will end up with a system freeze. Hence secure that the check and access to GOP happends at a time without preemption. ST-Ericsson Linux next: NA ST-Ericsson ID: 413508 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Arun Murthy Change-Id: I481e925b3a457ad3c8c82a5b7986dba6b2a4457b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48276 Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/modem/shrm/shrm_protocol.c | 174 ++++++++++++++++++++++++++++++------- 1 file changed, 143 insertions(+), 31 deletions(-) diff --git a/drivers/modem/shrm/shrm_protocol.c b/drivers/modem/shrm/shrm_protocol.c index 151ccf4c5a0..e3daeda4348 100644 --- a/drivers/modem/shrm/shrm_protocol.c +++ b/drivers/modem/shrm/shrm_protocol.c @@ -83,6 +83,8 @@ enum shrm_nl { SHRM_NL_STATUS_MOD_OFFLINE, }; +static int check_modem_in_reset(void); + void shm_print_dbg_info_work(struct kthread_work *work) { abx500_dump_all_banks(); @@ -96,6 +98,11 @@ void shm_mod_reset_req_work(struct kthread_work *work) /* update the boot_state */ spin_lock_irqsave(&boot_lock, flags); + if (boot_state != BOOT_DONE) { + dev_info(shm_dev->dev, "Modem in reset state\n"); + spin_unlock_irqrestore(&boot_lock, flags); + return; + } boot_state = BOOT_UNKNOWN; wmb(); spin_unlock_irqrestore(&boot_lock, flags); @@ -515,18 +522,30 @@ void shm_ca_sleep_req_work(struct kthread_work *work) bt_state = boot_state; spin_unlock_irqrestore(&boot_lock, flags); + local_irq_save(flags); + preempt_disable(); if (bt_state != BOOT_DONE) { dev_err(shm_dev->dev, "%s:Modem state reset or unknown\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } shrm_common_rx_state = SHRM_IDLE; shrm_audio_rx_state = SHRM_IDLE; - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shm_dev->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return; + } writel((1<intr_base + GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); hrtimer_start(&timer, ktime_set(0, 10*NSEC_PER_MSEC), HRTIMER_MODE_REL); @@ -536,6 +555,7 @@ void shm_ca_sleep_req_work(struct kthread_work *work) void shm_ca_wake_req_work(struct kthread_work *work) { + unsigned long flags; struct shrm_dev *shrm = container_of(work, struct shrm_dev, shm_ca_wake_req); @@ -547,22 +567,30 @@ void shm_ca_wake_req_work(struct kthread_work *work) modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); + local_irq_save(flags); + preempt_disable(); /* send ca_wake_ack_interrupt to CMU */ - if (!get_host_accessport_val()) { - dev_crit(shrm->dev, "get_host_accessport failed\n"); - dev_info(shrm->dev, "Initiating a modem reset\n"); - prcmu_modem_reset(); - } - if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + } + /* send ca_wake_ack_interrupt to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); } #ifdef CONFIG_U8500_SHRM_MODEM_SILENT_RESET static int shrm_modem_reset_sequence(void) @@ -717,19 +745,22 @@ static void send_ac_msg_pend_notify_0_work(struct kthread_work *work) modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); + spin_lock_irqsave(&start_timer_lock, flags); if (!get_host_accessport_val()) { - dev_crit(shrm->dev, "get_host_accessport failed\n"); - dev_info(shrm->dev, "Initiating a modem reset\n"); - prcmu_modem_reset(); + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + spin_unlock_irqrestore(&start_timer_lock, flags); + return; } if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + spin_unlock_irqrestore(&start_timer_lock, flags); return; } - spin_lock_irqsave(&start_timer_lock, flags); /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -762,19 +793,22 @@ static void send_ac_msg_pend_notify_1_work(struct kthread_work *work) modem_request(shrm->modem); mutex_unlock(&ac_state_mutex); + spin_lock_irqsave(&start_timer_lock, flags); if (!get_host_accessport_val()) { - dev_crit(shrm->dev, "get_host_accessport failed\n"); - dev_info(shrm->dev, "Initiating a modem reset\n"); - prcmu_modem_reset(); + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + spin_unlock_irqrestore(&start_timer_lock, flags); + return; } if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + spin_unlock_irqrestore(&start_timer_lock, flags); return; } - spin_lock_irqsave(&start_timer_lock, flags); /* Trigger AcMsgPendingNotification to CMU */ writel((1<intr_base + GOP_SET_REGISTER_BASE); @@ -1043,7 +1077,7 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_0); spin_unlock_irqrestore(&start_timer_lock, flags); - if(atomic_read(&fifo_full)) { + if (atomic_read(&fifo_full)) { atomic_set(&fifo_full, 0); hrtimer_cancel(&fifo_full_timer); } @@ -1057,17 +1091,29 @@ irqreturn_t ac_read_notif_0_irq_handler(int irq, void *ctrlr) shm_ac_read_0_tasklet.data = (unsigned long)shrm; tasklet_schedule(&shm_ac_read_0_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1 << GOP_COMMON_AC_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1083,7 +1129,7 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) spin_lock_irqsave(&start_timer_lock, flags); hrtimer_cancel(&mod_stuck_timer_1); spin_unlock_irqrestore(&start_timer_lock, flags); - if(atomic_read(&fifo_full)) { + if (atomic_read(&fifo_full)) { atomic_set(&fifo_full, 0); hrtimer_cancel(&fifo_full_timer); } @@ -1097,17 +1143,29 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) shm_ac_read_1_tasklet.data = (unsigned long)shrm; tasklet_schedule(&shm_ac_read_1_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1 << GOP_AUDIO_AC_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1115,6 +1173,7 @@ irqreturn_t ac_read_notif_1_irq_handler(int irq, void *ctrlr) irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr) { + unsigned long flags; struct shrm_dev *shrm = ctrlr; dev_dbg(shrm->dev, "%s IN\n", __func__); @@ -1127,17 +1186,29 @@ irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr) tasklet_schedule(&shm_ca_0_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1 << GOP_COMMON_CA_MSG_PENDING_NOTIFICATION_BIT), shrm->intr_base + GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1145,6 +1216,7 @@ irqreturn_t ca_msg_pending_notif_0_irq_handler(int irq, void *ctrlr) irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr) { + unsigned long flags; struct shrm_dev *shrm = ctrlr; dev_dbg(shrm->dev, "%s IN\n", __func__); @@ -1157,17 +1229,29 @@ irqreturn_t ca_msg_pending_notif_1_irq_handler(int irq, void *ctrlr) tasklet_schedule(&shm_ca_1_tasklet); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return IRQ_HANDLED; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return IRQ_HANDLED; + } /* Clear the interrupt */ writel((1<intr_base+GOP_CLEAR_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); dev_dbg(shrm->dev, "%s OUT\n", __func__); return IRQ_HANDLED; @@ -1233,7 +1317,7 @@ int shm_write_msg(struct shrm_dev *shrm, u8 l2_header, if (ret < 0) { dev_err(shrm->dev, "write message to fifo failed\n"); if (ret == -EAGAIN) { - if(!atomic_read(&fifo_full)) { + if (!atomic_read(&fifo_full)) { /* Start a timer so as to handle this gently */ atomic_set(&fifo_full, 1); hrtimer_start(&fifo_full_timer, ktime_set( @@ -1268,22 +1352,36 @@ out: void ca_msg_read_notification_0(struct shrm_dev *shrm) { + unsigned long flags; dev_dbg(shrm->dev, "%s IN\n", __func__); if (get_ca_msg_0_read_notif_send() == 0) { update_ca_common_shared_rptr(shrm); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", + __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return; + } /* Trigger CaMsgReadNotification to CMU */ writel((1 << GOP_COMMON_CA_READ_NOTIFICATION_BIT), shrm->intr_base + GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); set_ca_msg_0_read_notif_send(1); shrm_common_rx_state = SHRM_PTR_BUSY; } @@ -1293,22 +1391,36 @@ void ca_msg_read_notification_0(struct shrm_dev *shrm) void ca_msg_read_notification_1(struct shrm_dev *shrm) { + unsigned long flags; dev_dbg(shrm->dev, "%s IN\n", __func__); if (get_ca_msg_1_read_notif_send() == 0) { update_ca_audio_shared_rptr(shrm); + local_irq_save(flags); + preempt_disable(); if (check_modem_in_reset()) { dev_err(shrm->dev, "%s:Modem state reset or unknown.\n", __func__); + preempt_enable(); + local_irq_restore(flags); return; } - if (!get_host_accessport_val()) - BUG(); + if (!get_host_accessport_val()) { + dev_err(shrm->dev, "%s: host_accessport is low\n", + __func__); + queue_kthread_work(&shm_dev->shm_mod_stuck_kw, + &shm_dev->shm_mod_reset_req); + preempt_enable(); + local_irq_restore(flags); + return; + } /* Trigger CaMsgReadNotification to CMU */ writel((1<intr_base+GOP_SET_REGISTER_BASE); + preempt_enable(); + local_irq_restore(flags); set_ca_msg_1_read_notif_send(1); shrm_audio_rx_state = SHRM_PTR_BUSY; } -- cgit v1.2.3