All of lore.kernel.org
 help / color / mirror / Atom feed
* [Devel] Advice for in-kernel acpi patching
@ 2013-10-30 21:31 Stefan Demharter
  0 siblings, 0 replies; 3+ messages in thread
From: Stefan Demharter @ 2013-10-30 21:31 UTC (permalink / raw)
  To: devel

[-- Attachment #1: Type: text/plain, Size: 1235 bytes --]

Hi all,

I have a laptop (acer 3820tg) with muxed intel + radeon graphic cards which has the following problem:

If I use the intel as primary card and switch the radeon card off an on, the mux wronlgy changes to the discrete card.
This is particularily bad if you want to enable dynamic-power-off for the radeon card.

I identifed the problem to be a specific STORE-call in the method PEGP._ON in the table SSDT1.
I also wrote a module which patches the SSDT1 table on the fly. The function which does the real patching is attached.

As of now I think it's a somewhat hacky solution which should be improved.

At first I have the following questions:
- Is there already an easy way to patch acpi methods within the linux kernel?
- Do you agree that solving such stuff within the kernel is the right way to go?

Any other advice would be appreciated, too.

Best Regards,
Stefan

PS: I don't think loading a user-patched SSDT1 table at kernel start is the right way to go because:
- It doesn't work out of the box for new linux installations on the aforementioned laptop.
- If you change the bios setting for the graphic cards from switchable to intel-only or radeon-only the patched SSDT1 table would be wrong.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: fix_ssdt1_fn.c --]
[-- Type: text/x-csrc, Size: 2810 bytes --]

/**
 * Patch the SSDT1 acpi table.
 * I know it's a hack but I currently don't know of a better method...
 *
 * This patch disables the call "Store (Zero, PO52)" in Method PEGP._ON because this switches
 * the mux to the discrete GPU when the GPU is only switched on.
 */
static int acerx_patch_mux_in_ssdt1_of_acer_3820(enum PATCH_MODE mode)
{
	static const u8 replace[] = { AML_STORE_OP, AML_ZERO_OP, 'P', 'O', '5', '2' }; // Store (Zero, PO52)
	static const u8 with[] = { AML_NOOP_OP, AML_NOOP_OP, AML_NOOP_OP, AML_NOOP_OP, AML_NOOP_OP, AML_NOOP_OP }; // NoOp x 6
	static const u8 len = ARRAY_SIZE(replace);
	static const size_t replace_offset = 0xebc; // Position where to patch within the binary table SSDT1

	/*
	 * check that ssdt1 is ssdt1
	 * check
	 * Signature        "SSDT"
	 * Length           0x000010B9 (4281)
	 * Revision         0x01
	 * Checksum         0x0B
	 * OEM ID           "AmdRef"
	 * OEM Table ID     "AmdTabl"
	 * OEM Revision     0x00001000 (4096)
	 * Compiler ID      "INTL"
	 * Compiler Version 0x20060912 (537266450)
	 */
	struct acpi_table_header *ssdt1;
	acpi_size size;
	u8* offset;
	const u8 *rep = mode == PATCH_MODE_REVERSE ? with : replace;
	const u8 *wit = mode == PATCH_MODE_REVERSE ? replace : with;
	u8 checksum;
	acpi_status status;
	size_t expected_size = 4281;

#if 0
	/* Grab the mutex -- currently unsupported in this directory */
	status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
	if (!ACPI_SUCCESS(status)) {
		pr_warn("Couldn't get acpi lock\n");
		return status;
	}
#endif

	status = acpi_get_table_with_size(ACPI_SIG_SSDT, 1, &ssdt1, &size);
	if (!ACPI_SUCCESS(status)) {
		pr_err("Couldn't get ssdt1 table\n");
		goto unlock;
	}
	if (size != expected_size || size < replace_offset + len) {
		pr_info("Non matching ssdt1 tabling - won't patch.\n");
		goto unlock;
	}
	// at first check checksum...
	checksum = acpi_tb_checksum_cloned(ACPI_CAST_PTR(u8, ssdt1), size);
	if (checksum) {
		pr_err("Invalid checksum %d - abort patching%s\n", checksum, mode == PATCH_MODE_REVERSE ? " (reversed)" : "");
		goto unlock;
	}

	offset = ((u8*)ssdt1) + replace_offset;
	if (!memeq(offset, rep, len)) {
		pr_err("Non matching memory bytes %x %x ...\n", (int)*offset, (int)*(offset + 1));
		status = -EINVAL;
		goto unlock;
	}
	pr_info("ACPI table SSDT1 successfully patched%s\n", mode == PATCH_MODE_REVERSE ?
		" (reversed)" : " (removes implicit mux-switch to discrete graphics card when switching it on)");
	memcpy(offset, wit, len);
	checksum = acpi_tb_checksum_cloned(ACPI_CAST_PTR(u8, ssdt1), size);
	ssdt1->checksum -= checksum;
	compiletime_assert(len == ARRAY_SIZE(with), "len(replace) != len(with)");
unlock:
#if 0
	/* Mutex control -- disabled... see above */
	acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock);
#endif
	return status;
}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Devel] Advice for in-kernel acpi patching
@ 2013-10-31  2:41 Zheng, Lv
  0 siblings, 0 replies; 3+ messages in thread
From: Zheng, Lv @ 2013-10-31  2:41 UTC (permalink / raw)
  To: devel

[-- Attachment #1: Type: text/plain, Size: 1774 bytes --]

> From: Devel [mailto:devel-bounces(a)acpica.org] On Behalf Of Stefan Demharter
> Sent: Thursday, October 31, 2013 5:32 AM
> To: devel(a)acpica.org
> Subject: [Devel] Advice for in-kernel acpi patching
> 
> Hi all,
> 
> I have a laptop (acer 3820tg) with muxed intel + radeon graphic cards which has the following problem:
> 
> If I use the intel as primary card and switch the radeon card off an on, the mux wronlgy changes to the discrete card.
> This is particularily bad if you want to enable dynamic-power-off for the radeon card.
> 
> I identifed the problem to be a specific STORE-call in the method PEGP._ON in the table SSDT1.
> I also wrote a module which patches the SSDT1 table on the fly. The function which does the real patching is attached.
> 
> As of now I think it's a somewhat hacky solution which should be improved.
> 
> At first I have the following questions:
> - Is there already an easy way to patch acpi methods within the linux kernel?
> - Do you agree that solving such stuff within the kernel is the right way to go?

There are means in the Linux kernel can be used to achieve such kind of namespace patching, please find the documentation at:
Documentation/acpi/dsdt-override
Documentation/initrd_table_override

I think the latter might be able to fit your requirement.

Thanks
-Lv

> 
> Any other advice would be appreciated, too.
> 
> Best Regards,
> Stefan
> 
> PS: I don't think loading a user-patched SSDT1 table at kernel start is the right way to go because:
> - It doesn't work out of the box for new linux installations on the aforementioned laptop.
> - If you change the bios setting for the graphic cards from switchable to intel-only or radeon-only the patched SSDT1 table would be
> wrong.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Devel] Advice for in-kernel acpi patching
@ 2013-10-31 12:31 Zhang, Rui
  0 siblings, 0 replies; 3+ messages in thread
From: Zhang, Rui @ 2013-10-31 12:31 UTC (permalink / raw)
  To: devel

[-- Attachment #1: Type: text/plain, Size: 2474 bytes --]



> -----Original Message-----
> From: Devel [mailto:devel-bounces(a)acpica.org] On Behalf Of Zheng, Lv
> Sent: Thursday, October 31, 2013 10:41 AM
> To: Stefan Demharter; devel(a)acpica.org
> Subject: Re: [Devel] Advice for in-kernel acpi patching
> 
> > From: Devel [mailto:devel-bounces(a)acpica.org] On Behalf Of Stefan
> > Demharter
> > Sent: Thursday, October 31, 2013 5:32 AM
> > To: devel(a)acpica.org
> > Subject: [Devel] Advice for in-kernel acpi patching
> >
> > Hi all,
> >
> > I have a laptop (acer 3820tg) with muxed intel + radeon graphic cards
> which has the following problem:
> >
> > If I use the intel as primary card and switch the radeon card off an
> on, the mux wronlgy changes to the discrete card.
> > This is particularily bad if you want to enable dynamic-power-off for
> the radeon card.
> >
> > I identifed the problem to be a specific STORE-call in the method
> PEGP._ON in the table SSDT1.
> > I also wrote a module which patches the SSDT1 table on the fly. The
> function which does the real patching is attached.
> >
> > As of now I think it's a somewhat hacky solution which should be
> improved.
> >
> > At first I have the following questions:
> > - Is there already an easy way to patch acpi methods within the linux
> kernel?
> > - Do you agree that solving such stuff within the kernel is the right
> way to go?
> 
> There are means in the Linux kernel can be used to achieve such kind of
> namespace patching, please find the documentation at:
> Documentation/acpi/dsdt-override
> Documentation/initrd_table_override

Plus, Documentation/acpi/method_customizing.txt introduces a way to override a control method at runtime, without rebuilding the kernel, please check if it helps.

Thanks,
rui
> 
> I think the latter might be able to fit your requirement.
> 
> Thanks
> -Lv
> 
> >
> > Any other advice would be appreciated, too.
> >
> > Best Regards,
> > Stefan
> >
> > PS: I don't think loading a user-patched SSDT1 table at kernel start
> is the right way to go because:
> > - It doesn't work out of the box for new linux installations on the
> aforementioned laptop.
> > - If you change the bios setting for the graphic cards from
> switchable
> > to intel-only or radeon-only the patched SSDT1 table would be wrong.
> _______________________________________________
> Devel mailing list
> Devel(a)acpica.org
> https://lists.acpica.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2013-10-31 12:31 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-30 21:31 [Devel] Advice for in-kernel acpi patching Stefan Demharter
  -- strict thread matches above, loose matches on Subject: below --
2013-10-31  2:41 Zheng, Lv
2013-10-31 12:31 Zhang, Rui

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.