All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Demharter <stefan.demharter at gmx.net>
To: devel@acpica.org
Subject: [Devel] Advice for in-kernel acpi patching
Date: Wed, 30 Oct 2013 22:31:40 +0100	[thread overview]
Message-ID: <52717ABC.6010204@gmx.net> (raw)

[-- 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;
}

             reply	other threads:[~2013-10-30 21:31 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-10-30 21:31 Stefan Demharter [this message]
  -- strict thread matches above, loose matches on Subject: below --
2013-10-31  2:41 [Devel] Advice for in-kernel acpi patching Zheng, Lv
2013-10-31 12:31 Zhang, Rui

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=52717ABC.6010204@gmx.net \
    --to=devel@acpica.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.