forked from mirrors/linux
		
	ACPICA: Save current masks of enabled GPEs after enable register writes
There is a race condition between acpi_hw_disable_all_gpes() or acpi_enable_all_wakeup_gpes() and acpi_ev_asynch_enable_gpe() such that if the latter wins the race, it may mistakenly enable a GPE disabled by the former. This may lead to premature system wakeups during system suspend and potentially to more serious consequences. The source of the problem is how acpi_hw_low_set_gpe() works when passed ACPI_GPE_CONDITIONAL_ENABLE as the second argument. In that case, the GPE will be enabled if the corresponding bit is set in the enable_for_run mask of the GPE enable register containing that bit. However, acpi_hw_disable_all_gpes() and acpi_enable_all_wakeup_gpes() don't modify the enable_for_run masks of GPE registers when writing to them. In consequence, if acpi_ev_asynch_enable_gpe(), which eventually calls acpi_hw_low_set_gpe() with the second argument equal to ACPI_GPE_CONDITIONAL_ENABLE, is executed in parallel with one of these functions, it may reverse changes made by them. To fix the problem, introduce a new enable_mask field in struct acpi_gpe_register_info in which to store the current mask of enabled GPEs and modify acpi_hw_low_set_gpe() to take this mask into account instead of enable_for_run when its second argument is equal to ACPI_GPE_CONDITIONAL_ENABLE. Also modify the low-level routines called by acpi_hw_disable_all_gpes(), acpi_enable_all_wakeup_gpes() and acpi_enable_all_runtime_gpes() to update the enable_mask masks of GPE registers after all (successful) writes to those registers. Acked-by: Lv Zheng <lv.zheng@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
		
							parent
							
								
									e5874591d6
								
							
						
					
					
						commit
						c50f13c672
					
				
					 4 changed files with 51 additions and 13 deletions
				
			
		|  | @ -454,6 +454,7 @@ struct acpi_gpe_register_info { | ||||||
| 	u16 base_gpe_number;	/* Base GPE number for this register */ | 	u16 base_gpe_number;	/* Base GPE number for this register */ | ||||||
| 	u8 enable_for_wake;	/* GPEs to keep enabled when sleeping */ | 	u8 enable_for_wake;	/* GPEs to keep enabled when sleeping */ | ||||||
| 	u8 enable_for_run;	/* GPEs to keep enabled when running */ | 	u8 enable_for_run;	/* GPEs to keep enabled when running */ | ||||||
|  | 	u8 enable_mask;		/* Current mask of enabled GPEs */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
|  | @ -134,7 +134,7 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) | ||||||
| 
 | 
 | ||||||
| 	/* Enable the requested GPE */ | 	/* Enable the requested GPE */ | ||||||
| 
 | 
 | ||||||
| 	status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); | 	status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE_SAVE); | ||||||
| 	return_ACPI_STATUS(status); | 	return_ACPI_STATUS(status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -213,7 +213,7 @@ acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) | ||||||
| 		if (ACPI_SUCCESS(status)) { | 		if (ACPI_SUCCESS(status)) { | ||||||
| 			status = | 			status = | ||||||
| 			    acpi_hw_low_set_gpe(gpe_event_info, | 			    acpi_hw_low_set_gpe(gpe_event_info, | ||||||
| 						ACPI_GPE_DISABLE); | 						ACPI_GPE_DISABLE_SAVE); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (ACPI_FAILURE(status)) { | 		if (ACPI_FAILURE(status)) { | ||||||
|  | @ -655,7 +655,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info * gpe_event_info) | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Enable this GPE, conditionally. This means that the GPE will | 	 * Enable this GPE, conditionally. This means that the GPE will | ||||||
| 	 * only be physically enabled if the enable_for_run bit is set | 	 * only be physically enabled if the enable_mask bit is set | ||||||
| 	 * in the event_info. | 	 * in the event_info. | ||||||
| 	 */ | 	 */ | ||||||
| 	(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); | 	(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); | ||||||
|  |  | ||||||
|  | @ -115,12 +115,12 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) | ||||||
| 	/* Set or clear just the bit that corresponds to this GPE */ | 	/* Set or clear just the bit that corresponds to this GPE */ | ||||||
| 
 | 
 | ||||||
| 	register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); | 	register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); | ||||||
| 	switch (action) { | 	switch (action & ~ACPI_GPE_SAVE_MASK) { | ||||||
| 	case ACPI_GPE_CONDITIONAL_ENABLE: | 	case ACPI_GPE_CONDITIONAL_ENABLE: | ||||||
| 
 | 
 | ||||||
| 		/* Only enable if the enable_for_run bit is set */ | 		/* Only enable if the corresponding enable_mask bit is set */ | ||||||
| 
 | 
 | ||||||
| 		if (!(register_bit & gpe_register_info->enable_for_run)) { | 		if (!(register_bit & gpe_register_info->enable_mask)) { | ||||||
| 			return (AE_BAD_PARAMETER); | 			return (AE_BAD_PARAMETER); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -145,6 +145,9 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) | ||||||
| 	/* Write the updated enable mask */ | 	/* Write the updated enable mask */ | ||||||
| 
 | 
 | ||||||
| 	status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); | 	status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); | ||||||
|  | 	if (ACPI_SUCCESS(status) && (action & ACPI_GPE_SAVE_MASK)) { | ||||||
|  | 		gpe_register_info->enable_mask = enable_mask; | ||||||
|  | 	} | ||||||
| 	return (status); | 	return (status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -260,6 +263,32 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, | ||||||
| 	return (AE_OK); | 	return (AE_OK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /******************************************************************************
 | ||||||
|  |  * | ||||||
|  |  * FUNCTION:    acpi_hw_gpe_enable_write | ||||||
|  |  * | ||||||
|  |  * PARAMETERS:  enable_mask         - Bit mask to write to the GPE register | ||||||
|  |  *              gpe_register_info   - Gpe Register info | ||||||
|  |  * | ||||||
|  |  * RETURN:      Status | ||||||
|  |  * | ||||||
|  |  * DESCRIPTION: Write the enable mask byte to the given GPE register. | ||||||
|  |  * | ||||||
|  |  ******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | static acpi_status | ||||||
|  | acpi_hw_gpe_enable_write(u8 enable_mask, | ||||||
|  | 			 struct acpi_gpe_register_info *gpe_register_info) | ||||||
|  | { | ||||||
|  | 	acpi_status status; | ||||||
|  | 
 | ||||||
|  | 	status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); | ||||||
|  | 	if (ACPI_SUCCESS(status)) { | ||||||
|  | 		gpe_register_info->enable_mask = enable_mask; | ||||||
|  | 	} | ||||||
|  | 	return (status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /******************************************************************************
 | /******************************************************************************
 | ||||||
|  * |  * | ||||||
|  * FUNCTION:    acpi_hw_disable_gpe_block |  * FUNCTION:    acpi_hw_disable_gpe_block | ||||||
|  | @ -287,8 +316,8 @@ acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | ||||||
| 		/* Disable all GPEs in this register */ | 		/* Disable all GPEs in this register */ | ||||||
| 
 | 
 | ||||||
| 		status = | 		status = | ||||||
| 		    acpi_hw_write(0x00, | 		    acpi_hw_gpe_enable_write(0x00, | ||||||
| 				  &gpe_block->register_info[i].enable_address); | 					     &gpe_block->register_info[i]); | ||||||
| 		if (ACPI_FAILURE(status)) { | 		if (ACPI_FAILURE(status)) { | ||||||
| 			return (status); | 			return (status); | ||||||
| 		} | 		} | ||||||
|  | @ -355,21 +384,23 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | ||||||
| { | { | ||||||
| 	u32 i; | 	u32 i; | ||||||
| 	acpi_status status; | 	acpi_status status; | ||||||
|  | 	struct acpi_gpe_register_info *gpe_register_info; | ||||||
| 
 | 
 | ||||||
| 	/* NOTE: assumes that all GPEs are currently disabled */ | 	/* NOTE: assumes that all GPEs are currently disabled */ | ||||||
| 
 | 
 | ||||||
| 	/* Examine each GPE Register within the block */ | 	/* Examine each GPE Register within the block */ | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < gpe_block->register_count; i++) { | 	for (i = 0; i < gpe_block->register_count; i++) { | ||||||
| 		if (!gpe_block->register_info[i].enable_for_run) { | 		gpe_register_info = &gpe_block->register_info[i]; | ||||||
|  | 		if (!gpe_register_info->enable_for_run) { | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/* Enable all "runtime" GPEs in this register */ | 		/* Enable all "runtime" GPEs in this register */ | ||||||
| 
 | 
 | ||||||
| 		status = | 		status = | ||||||
| 		    acpi_hw_write(gpe_block->register_info[i].enable_for_run, | 		    acpi_hw_gpe_enable_write(gpe_register_info->enable_for_run, | ||||||
| 				  &gpe_block->register_info[i].enable_address); | 					     gpe_register_info); | ||||||
| 		if (ACPI_FAILURE(status)) { | 		if (ACPI_FAILURE(status)) { | ||||||
| 			return (status); | 			return (status); | ||||||
| 		} | 		} | ||||||
|  | @ -399,10 +430,12 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | ||||||
| { | { | ||||||
| 	u32 i; | 	u32 i; | ||||||
| 	acpi_status status; | 	acpi_status status; | ||||||
|  | 	struct acpi_gpe_register_info *gpe_register_info; | ||||||
| 
 | 
 | ||||||
| 	/* Examine each GPE Register within the block */ | 	/* Examine each GPE Register within the block */ | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < gpe_block->register_count; i++) { | 	for (i = 0; i < gpe_block->register_count; i++) { | ||||||
|  | 		gpe_register_info = &gpe_block->register_info[i]; | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Enable all "wake" GPEs in this register and disable the | 		 * Enable all "wake" GPEs in this register and disable the | ||||||
|  | @ -410,8 +443,8 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | ||||||
| 		 */ | 		 */ | ||||||
| 
 | 
 | ||||||
| 		status = | 		status = | ||||||
| 		    acpi_hw_write(gpe_block->register_info[i].enable_for_wake, | 		    acpi_hw_gpe_enable_write(gpe_register_info->enable_for_wake, | ||||||
| 				  &gpe_block->register_info[i].enable_address); | 					     gpe_register_info); | ||||||
| 		if (ACPI_FAILURE(status)) { | 		if (ACPI_FAILURE(status)) { | ||||||
| 			return (status); | 			return (status); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -736,6 +736,10 @@ typedef u32 acpi_event_status; | ||||||
| #define ACPI_GPE_ENABLE                 0 | #define ACPI_GPE_ENABLE                 0 | ||||||
| #define ACPI_GPE_DISABLE                1 | #define ACPI_GPE_DISABLE                1 | ||||||
| #define ACPI_GPE_CONDITIONAL_ENABLE     2 | #define ACPI_GPE_CONDITIONAL_ENABLE     2 | ||||||
|  | #define ACPI_GPE_SAVE_MASK              4 | ||||||
|  | 
 | ||||||
|  | #define ACPI_GPE_ENABLE_SAVE            (ACPI_GPE_ENABLE | ACPI_GPE_SAVE_MASK) | ||||||
|  | #define ACPI_GPE_DISABLE_SAVE           (ACPI_GPE_DISABLE | ACPI_GPE_SAVE_MASK) | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * GPE info flags - Per GPE |  * GPE info flags - Per GPE | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Rafael J. Wysocki
						Rafael J. Wysocki