From 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 Mon Sep 17 00:00:00 2001 From: Yunhong Jiang Date: Tue, 4 Aug 2015 12:17:53 -0700 Subject: Add the rt linux 4.1.3-rt3 as base Import the rt linux 4.1.3-rt3 as OPNFV kvm base. It's from git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git linux-4.1.y-rt and the base is: commit 0917f823c59692d751951bf5ea699a2d1e2f26a2 Author: Sebastian Andrzej Siewior Date: Sat Jul 25 12:13:34 2015 +0200 Prepare v4.1.3-rt3 Signed-off-by: Sebastian Andrzej Siewior We lose all the git history this way and it's not good. We should apply another opnfv project repo in future. Change-Id: I87543d81c9df70d99c5001fbdf646b202c19f423 Signed-off-by: Yunhong Jiang --- kernel/drivers/acpi/Kconfig | 433 +++++ kernel/drivers/acpi/Makefile | 97 + kernel/drivers/acpi/ac.c | 462 +++++ kernel/drivers/acpi/acpi_apd.c | 150 ++ kernel/drivers/acpi/acpi_cmos_rtc.c | 90 + kernel/drivers/acpi/acpi_extlog.c | 327 ++++ kernel/drivers/acpi/acpi_ipmi.c | 661 +++++++ kernel/drivers/acpi/acpi_lpat.c | 161 ++ kernel/drivers/acpi/acpi_lpss.c | 746 +++++++ kernel/drivers/acpi/acpi_memhotplug.c | 395 ++++ kernel/drivers/acpi/acpi_pad.c | 502 +++++ kernel/drivers/acpi/acpi_platform.c | 118 ++ kernel/drivers/acpi/acpi_pnp.c | 389 ++++ kernel/drivers/acpi/acpi_processor.c | 504 +++++ kernel/drivers/acpi/acpica/Makefile | 185 ++ kernel/drivers/acpi/acpica/acapps.h | 173 ++ kernel/drivers/acpi/acpica/accommon.h | 63 + kernel/drivers/acpi/acpica/acdebug.h | 282 +++ kernel/drivers/acpi/acpica/acdispat.h | 357 ++++ kernel/drivers/acpi/acpica/acevents.h | 253 +++ kernel/drivers/acpi/acpica/acglobal.h | 383 ++++ kernel/drivers/acpi/acpica/achware.h | 150 ++ kernel/drivers/acpi/acpica/acinterp.h | 534 +++++ kernel/drivers/acpi/acpica/aclocal.h | 1168 +++++++++++ kernel/drivers/acpi/acpica/acmacros.h | 419 ++++ kernel/drivers/acpi/acpica/acnamesp.h | 407 ++++ kernel/drivers/acpi/acpica/acobject.h | 463 +++++ kernel/drivers/acpi/acpica/acopcode.h | 329 ++++ kernel/drivers/acpi/acpica/acparser.h | 254 +++ kernel/drivers/acpi/acpica/acpredef.h | 1101 +++++++++++ kernel/drivers/acpi/acpica/acresrc.h | 383 ++++ kernel/drivers/acpi/acpica/acstruct.h | 242 +++ kernel/drivers/acpi/acpica/actables.h | 167 ++ kernel/drivers/acpi/acpica/acutils.h | 792 ++++++++ kernel/drivers/acpi/acpica/amlcode.h | 487 +++++ kernel/drivers/acpi/acpica/amlresrc.h | 486 +++++ kernel/drivers/acpi/acpica/dsargs.c | 405 ++++ kernel/drivers/acpi/acpica/dscontrol.c | 410 ++++ kernel/drivers/acpi/acpica/dsfield.c | 794 ++++++++ kernel/drivers/acpi/acpica/dsinit.c | 253 +++ kernel/drivers/acpi/acpica/dsmethod.c | 831 ++++++++ kernel/drivers/acpi/acpica/dsmthdat.c | 714 +++++++ kernel/drivers/acpi/acpica/dsobject.c | 850 ++++++++ kernel/drivers/acpi/acpica/dsopcode.c | 757 ++++++++ kernel/drivers/acpi/acpica/dsutils.c | 879 +++++++++ kernel/drivers/acpi/acpica/dswexec.c | 759 ++++++++ kernel/drivers/acpi/acpica/dswload.c | 570 ++++++ kernel/drivers/acpi/acpica/dswload2.c | 738 +++++++ kernel/drivers/acpi/acpica/dswscope.c | 214 ++ kernel/drivers/acpi/acpica/dswstate.c | 757 ++++++++ kernel/drivers/acpi/acpica/evevent.c | 297 +++ kernel/drivers/acpi/acpica/evglock.c | 344 ++++ kernel/drivers/acpi/acpica/evgpe.c | 798 ++++++++ kernel/drivers/acpi/acpica/evgpeblk.c | 513 +++++ kernel/drivers/acpi/acpica/evgpeinit.c | 446 +++++ kernel/drivers/acpi/acpica/evgpeutil.c | 359 ++++ kernel/drivers/acpi/acpica/evhandler.c | 536 +++++ kernel/drivers/acpi/acpica/evmisc.c | 299 +++ kernel/drivers/acpi/acpica/evregion.c | 795 ++++++++ kernel/drivers/acpi/acpica/evrgnini.c | 675 +++++++ kernel/drivers/acpi/acpica/evsci.c | 250 +++ kernel/drivers/acpi/acpica/evxface.c | 1105 +++++++++++ kernel/drivers/acpi/acpica/evxfevnt.c | 381 ++++ kernel/drivers/acpi/acpica/evxfgpe.c | 952 +++++++++ kernel/drivers/acpi/acpica/evxfregn.c | 295 +++ kernel/drivers/acpi/acpica/exconfig.c | 634 ++++++ kernel/drivers/acpi/acpica/exconvrt.c | 694 +++++++ kernel/drivers/acpi/acpica/excreate.c | 528 +++++ kernel/drivers/acpi/acpica/exdebug.c | 272 +++ kernel/drivers/acpi/acpica/exdump.c | 1209 ++++++++++++ kernel/drivers/acpi/acpica/exfield.c | 536 +++++ kernel/drivers/acpi/acpica/exfldio.c | 1004 ++++++++++ kernel/drivers/acpi/acpica/exmisc.c | 733 +++++++ kernel/drivers/acpi/acpica/exmutex.c | 502 +++++ kernel/drivers/acpi/acpica/exnames.c | 435 +++++ kernel/drivers/acpi/acpica/exoparg1.c | 1072 ++++++++++ kernel/drivers/acpi/acpica/exoparg2.c | 580 ++++++ kernel/drivers/acpi/acpica/exoparg3.c | 281 +++ kernel/drivers/acpi/acpica/exoparg6.c | 332 ++++ kernel/drivers/acpi/acpica/exprep.c | 630 ++++++ kernel/drivers/acpi/acpica/exregion.c | 537 +++++ kernel/drivers/acpi/acpica/exresnte.c | 279 +++ kernel/drivers/acpi/acpica/exresolv.c | 554 ++++++ kernel/drivers/acpi/acpica/exresop.c | 701 +++++++ kernel/drivers/acpi/acpica/exstore.c | 557 ++++++ kernel/drivers/acpi/acpica/exstoren.c | 294 +++ kernel/drivers/acpi/acpica/exstorob.c | 220 +++ kernel/drivers/acpi/acpica/exsystem.c | 310 +++ kernel/drivers/acpi/acpica/exutils.c | 406 ++++ kernel/drivers/acpi/acpica/hwacpi.c | 181 ++ kernel/drivers/acpi/acpica/hwesleep.c | 243 +++ kernel/drivers/acpi/acpica/hwgpe.c | 535 +++++ kernel/drivers/acpi/acpica/hwpci.c | 421 ++++ kernel/drivers/acpi/acpica/hwregs.c | 668 +++++++ kernel/drivers/acpi/acpica/hwsleep.c | 345 ++++ kernel/drivers/acpi/acpica/hwtimer.c | 202 ++ kernel/drivers/acpi/acpica/hwvalid.c | 328 ++++ kernel/drivers/acpi/acpica/hwxface.c | 587 ++++++ kernel/drivers/acpi/acpica/hwxfsleep.c | 432 +++++ kernel/drivers/acpi/acpica/nsaccess.c | 672 +++++++ kernel/drivers/acpi/acpica/nsalloc.c | 526 +++++ kernel/drivers/acpi/acpica/nsarguments.c | 294 +++ kernel/drivers/acpi/acpica/nsconvert.c | 446 +++++ kernel/drivers/acpi/acpica/nsdump.c | 886 +++++++++ kernel/drivers/acpi/acpica/nsdumpdv.c | 141 ++ kernel/drivers/acpi/acpica/nseval.c | 494 +++++ kernel/drivers/acpi/acpica/nsinit.c | 607 ++++++ kernel/drivers/acpi/acpica/nsload.c | 314 +++ kernel/drivers/acpi/acpica/nsnames.c | 266 +++ kernel/drivers/acpi/acpica/nsobject.c | 464 +++++ kernel/drivers/acpi/acpica/nsparse.c | 202 ++ kernel/drivers/acpi/acpica/nspredef.c | 400 ++++ kernel/drivers/acpi/acpica/nsprepkg.c | 663 +++++++ kernel/drivers/acpi/acpica/nsrepair.c | 595 ++++++ kernel/drivers/acpi/acpica/nsrepair2.c | 979 ++++++++++ kernel/drivers/acpi/acpica/nssearch.c | 399 ++++ kernel/drivers/acpi/acpica/nsutils.c | 730 +++++++ kernel/drivers/acpi/acpica/nswalk.c | 358 ++++ kernel/drivers/acpi/acpica/nsxfeval.c | 996 ++++++++++ kernel/drivers/acpi/acpica/nsxfname.c | 663 +++++++ kernel/drivers/acpi/acpica/nsxfobj.c | 246 +++ kernel/drivers/acpi/acpica/psargs.c | 874 +++++++++ kernel/drivers/acpi/acpica/psloop.c | 626 ++++++ kernel/drivers/acpi/acpica/psobject.c | 651 +++++++ kernel/drivers/acpi/acpica/psopcode.c | 658 +++++++ kernel/drivers/acpi/acpica/psopinfo.c | 270 +++ kernel/drivers/acpi/acpica/psparse.c | 688 +++++++ kernel/drivers/acpi/acpica/psscope.c | 265 +++ kernel/drivers/acpi/acpica/pstree.c | 320 +++ kernel/drivers/acpi/acpica/psutils.c | 236 +++ kernel/drivers/acpi/acpica/pswalk.c | 110 ++ kernel/drivers/acpi/acpica/psxface.c | 390 ++++ kernel/drivers/acpi/acpica/rsaddr.c | 382 ++++ kernel/drivers/acpi/acpica/rscalc.c | 718 +++++++ kernel/drivers/acpi/acpica/rscreate.c | 482 +++++ kernel/drivers/acpi/acpica/rsdump.c | 569 ++++++ kernel/drivers/acpi/acpica/rsdumpinfo.c | 455 +++++ kernel/drivers/acpi/acpica/rsinfo.c | 248 +++ kernel/drivers/acpi/acpica/rsio.c | 290 +++ kernel/drivers/acpi/acpica/rsirq.c | 307 +++ kernel/drivers/acpi/acpica/rslist.c | 259 +++ kernel/drivers/acpi/acpica/rsmemory.c | 234 +++ kernel/drivers/acpi/acpica/rsmisc.c | 825 ++++++++ kernel/drivers/acpi/acpica/rsserial.c | 445 +++++ kernel/drivers/acpi/acpica/rsutils.c | 790 ++++++++ kernel/drivers/acpi/acpica/rsxface.c | 663 +++++++ kernel/drivers/acpi/acpica/tbdata.c | 773 ++++++++ kernel/drivers/acpi/acpica/tbfadt.c | 747 +++++++ kernel/drivers/acpi/acpica/tbfind.c | 140 ++ kernel/drivers/acpi/acpica/tbinstal.c | 490 +++++ kernel/drivers/acpi/acpica/tbprint.c | 244 +++ kernel/drivers/acpi/acpica/tbutils.c | 401 ++++ kernel/drivers/acpi/acpica/tbxface.c | 481 +++++ kernel/drivers/acpi/acpica/tbxfload.c | 418 ++++ kernel/drivers/acpi/acpica/tbxfroot.c | 298 +++ kernel/drivers/acpi/acpica/utaddress.c | 297 +++ kernel/drivers/acpi/acpica/utalloc.c | 342 ++++ kernel/drivers/acpi/acpica/utbuffer.c | 337 ++++ kernel/drivers/acpi/acpica/utcache.c | 313 +++ kernel/drivers/acpi/acpica/utcopy.c | 1011 ++++++++++ kernel/drivers/acpi/acpica/utdebug.c | 589 ++++++ kernel/drivers/acpi/acpica/utdecode.c | 545 ++++++ kernel/drivers/acpi/acpica/utdelete.c | 755 ++++++++ kernel/drivers/acpi/acpica/uterror.c | 289 +++ kernel/drivers/acpi/acpica/uteval.c | 349 ++++ kernel/drivers/acpi/acpica/utexcep.c | 159 ++ kernel/drivers/acpi/acpica/utfileio.c | 331 ++++ kernel/drivers/acpi/acpica/utglobal.c | 223 +++ kernel/drivers/acpi/acpica/uthex.c | 100 + kernel/drivers/acpi/acpica/utids.c | 418 ++++ kernel/drivers/acpi/acpica/utinit.c | 315 +++ kernel/drivers/acpi/acpica/utlock.c | 175 ++ kernel/drivers/acpi/acpica/utmath.c | 324 ++++ kernel/drivers/acpi/acpica/utmisc.c | 411 ++++ kernel/drivers/acpi/acpica/utmutex.c | 360 ++++ kernel/drivers/acpi/acpica/utobject.c | 706 +++++++ kernel/drivers/acpi/acpica/utosi.c | 474 +++++ kernel/drivers/acpi/acpica/utownerid.c | 218 +++ kernel/drivers/acpi/acpica/utpredef.c | 399 ++++ kernel/drivers/acpi/acpica/utprint.c | 667 +++++++ kernel/drivers/acpi/acpica/utresrc.c | 830 ++++++++ kernel/drivers/acpi/acpica/utstate.c | 308 +++ kernel/drivers/acpi/acpica/utstring.c | 648 +++++++ kernel/drivers/acpi/acpica/uttrack.c | 722 +++++++ kernel/drivers/acpi/acpica/utuuid.c | 98 + kernel/drivers/acpi/acpica/utxface.c | 583 ++++++ kernel/drivers/acpi/acpica/utxferror.c | 253 +++ kernel/drivers/acpi/acpica/utxfinit.c | 340 ++++ kernel/drivers/acpi/acpica/utxfmutex.c | 187 ++ kernel/drivers/acpi/apei/Kconfig | 64 + kernel/drivers/acpi/apei/Makefile | 6 + kernel/drivers/acpi/apei/apei-base.c | 805 ++++++++ kernel/drivers/acpi/apei/apei-internal.h | 143 ++ kernel/drivers/acpi/apei/einj.c | 833 ++++++++ kernel/drivers/acpi/apei/erst-dbg.c | 243 +++ kernel/drivers/acpi/apei/erst.c | 1230 ++++++++++++ kernel/drivers/acpi/apei/ghes.c | 1163 +++++++++++ kernel/drivers/acpi/apei/hest.c | 255 +++ kernel/drivers/acpi/battery.c | 1329 +++++++++++++ kernel/drivers/acpi/battery.h | 10 + kernel/drivers/acpi/bgrt.c | 111 ++ kernel/drivers/acpi/blacklist.c | 331 ++++ kernel/drivers/acpi/bus.c | 706 +++++++ kernel/drivers/acpi/button.c | 453 +++++ kernel/drivers/acpi/cm_sbs.c | 105 + kernel/drivers/acpi/container.c | 136 ++ kernel/drivers/acpi/custom_method.c | 100 + kernel/drivers/acpi/debugfs.c | 19 + kernel/drivers/acpi/device_pm.c | 1118 +++++++++++ kernel/drivers/acpi/dock.c | 666 +++++++ kernel/drivers/acpi/ec.c | 1459 ++++++++++++++ kernel/drivers/acpi/ec_sys.c | 162 ++ kernel/drivers/acpi/event.c | 183 ++ kernel/drivers/acpi/fan.c | 420 ++++ kernel/drivers/acpi/glue.c | 358 ++++ kernel/drivers/acpi/gsi.c | 105 + kernel/drivers/acpi/hed.c | 92 + kernel/drivers/acpi/int340x_thermal.c | 54 + kernel/drivers/acpi/internal.h | 197 ++ kernel/drivers/acpi/ioapic.c | 229 +++ kernel/drivers/acpi/numa.c | 336 ++++ kernel/drivers/acpi/nvs.c | 212 ++ kernel/drivers/acpi/osl.c | 1914 ++++++++++++++++++ kernel/drivers/acpi/pci_irq.c | 517 +++++ kernel/drivers/acpi/pci_link.c | 886 +++++++++ kernel/drivers/acpi/pci_root.c | 667 +++++++ kernel/drivers/acpi/pci_slot.c | 219 +++ kernel/drivers/acpi/pmic/intel_pmic.c | 257 +++ kernel/drivers/acpi/pmic/intel_pmic.h | 25 + kernel/drivers/acpi/pmic/intel_pmic_crc.c | 211 ++ kernel/drivers/acpi/pmic/intel_pmic_xpower.c | 268 +++ kernel/drivers/acpi/power.c | 858 ++++++++ kernel/drivers/acpi/proc.c | 154 ++ kernel/drivers/acpi/processor_core.c | 340 ++++ kernel/drivers/acpi/processor_driver.c | 307 +++ kernel/drivers/acpi/processor_idle.c | 1145 +++++++++++ kernel/drivers/acpi/processor_pdc.c | 206 ++ kernel/drivers/acpi/processor_perflib.c | 810 ++++++++ kernel/drivers/acpi/processor_thermal.c | 276 +++ kernel/drivers/acpi/processor_throttling.c | 1264 ++++++++++++ kernel/drivers/acpi/property.c | 551 ++++++ kernel/drivers/acpi/reboot.c | 54 + kernel/drivers/acpi/resource.c | 783 ++++++++ kernel/drivers/acpi/sbs.c | 773 ++++++++ kernel/drivers/acpi/sbshc.c | 338 ++++ kernel/drivers/acpi/sbshc.h | 33 + kernel/drivers/acpi/scan.c | 2688 ++++++++++++++++++++++++++ kernel/drivers/acpi/sleep.c | 859 ++++++++ kernel/drivers/acpi/sleep.h | 8 + kernel/drivers/acpi/sysfs.c | 824 ++++++++ kernel/drivers/acpi/tables.c | 432 +++++ kernel/drivers/acpi/thermal.c | 1285 ++++++++++++ kernel/drivers/acpi/utils.c | 714 +++++++ kernel/drivers/acpi/video.c | 2231 +++++++++++++++++++++ kernel/drivers/acpi/video_detect.c | 310 +++ kernel/drivers/acpi/wakeup.c | 97 + 256 files changed, 125432 insertions(+) create mode 100644 kernel/drivers/acpi/Kconfig create mode 100644 kernel/drivers/acpi/Makefile create mode 100644 kernel/drivers/acpi/ac.c create mode 100644 kernel/drivers/acpi/acpi_apd.c create mode 100644 kernel/drivers/acpi/acpi_cmos_rtc.c create mode 100644 kernel/drivers/acpi/acpi_extlog.c create mode 100644 kernel/drivers/acpi/acpi_ipmi.c create mode 100644 kernel/drivers/acpi/acpi_lpat.c create mode 100644 kernel/drivers/acpi/acpi_lpss.c create mode 100644 kernel/drivers/acpi/acpi_memhotplug.c create mode 100644 kernel/drivers/acpi/acpi_pad.c create mode 100644 kernel/drivers/acpi/acpi_platform.c create mode 100644 kernel/drivers/acpi/acpi_pnp.c create mode 100644 kernel/drivers/acpi/acpi_processor.c create mode 100644 kernel/drivers/acpi/acpica/Makefile create mode 100644 kernel/drivers/acpi/acpica/acapps.h create mode 100644 kernel/drivers/acpi/acpica/accommon.h create mode 100644 kernel/drivers/acpi/acpica/acdebug.h create mode 100644 kernel/drivers/acpi/acpica/acdispat.h create mode 100644 kernel/drivers/acpi/acpica/acevents.h create mode 100644 kernel/drivers/acpi/acpica/acglobal.h create mode 100644 kernel/drivers/acpi/acpica/achware.h create mode 100644 kernel/drivers/acpi/acpica/acinterp.h create mode 100644 kernel/drivers/acpi/acpica/aclocal.h create mode 100644 kernel/drivers/acpi/acpica/acmacros.h create mode 100644 kernel/drivers/acpi/acpica/acnamesp.h create mode 100644 kernel/drivers/acpi/acpica/acobject.h create mode 100644 kernel/drivers/acpi/acpica/acopcode.h create mode 100644 kernel/drivers/acpi/acpica/acparser.h create mode 100644 kernel/drivers/acpi/acpica/acpredef.h create mode 100644 kernel/drivers/acpi/acpica/acresrc.h create mode 100644 kernel/drivers/acpi/acpica/acstruct.h create mode 100644 kernel/drivers/acpi/acpica/actables.h create mode 100644 kernel/drivers/acpi/acpica/acutils.h create mode 100644 kernel/drivers/acpi/acpica/amlcode.h create mode 100644 kernel/drivers/acpi/acpica/amlresrc.h create mode 100644 kernel/drivers/acpi/acpica/dsargs.c create mode 100644 kernel/drivers/acpi/acpica/dscontrol.c create mode 100644 kernel/drivers/acpi/acpica/dsfield.c create mode 100644 kernel/drivers/acpi/acpica/dsinit.c create mode 100644 kernel/drivers/acpi/acpica/dsmethod.c create mode 100644 kernel/drivers/acpi/acpica/dsmthdat.c create mode 100644 kernel/drivers/acpi/acpica/dsobject.c create mode 100644 kernel/drivers/acpi/acpica/dsopcode.c create mode 100644 kernel/drivers/acpi/acpica/dsutils.c create mode 100644 kernel/drivers/acpi/acpica/dswexec.c create mode 100644 kernel/drivers/acpi/acpica/dswload.c create mode 100644 kernel/drivers/acpi/acpica/dswload2.c create mode 100644 kernel/drivers/acpi/acpica/dswscope.c create mode 100644 kernel/drivers/acpi/acpica/dswstate.c create mode 100644 kernel/drivers/acpi/acpica/evevent.c create mode 100644 kernel/drivers/acpi/acpica/evglock.c create mode 100644 kernel/drivers/acpi/acpica/evgpe.c create mode 100644 kernel/drivers/acpi/acpica/evgpeblk.c create mode 100644 kernel/drivers/acpi/acpica/evgpeinit.c create mode 100644 kernel/drivers/acpi/acpica/evgpeutil.c create mode 100644 kernel/drivers/acpi/acpica/evhandler.c create mode 100644 kernel/drivers/acpi/acpica/evmisc.c create mode 100644 kernel/drivers/acpi/acpica/evregion.c create mode 100644 kernel/drivers/acpi/acpica/evrgnini.c create mode 100644 kernel/drivers/acpi/acpica/evsci.c create mode 100644 kernel/drivers/acpi/acpica/evxface.c create mode 100644 kernel/drivers/acpi/acpica/evxfevnt.c create mode 100644 kernel/drivers/acpi/acpica/evxfgpe.c create mode 100644 kernel/drivers/acpi/acpica/evxfregn.c create mode 100644 kernel/drivers/acpi/acpica/exconfig.c create mode 100644 kernel/drivers/acpi/acpica/exconvrt.c create mode 100644 kernel/drivers/acpi/acpica/excreate.c create mode 100644 kernel/drivers/acpi/acpica/exdebug.c create mode 100644 kernel/drivers/acpi/acpica/exdump.c create mode 100644 kernel/drivers/acpi/acpica/exfield.c create mode 100644 kernel/drivers/acpi/acpica/exfldio.c create mode 100644 kernel/drivers/acpi/acpica/exmisc.c create mode 100644 kernel/drivers/acpi/acpica/exmutex.c create mode 100644 kernel/drivers/acpi/acpica/exnames.c create mode 100644 kernel/drivers/acpi/acpica/exoparg1.c create mode 100644 kernel/drivers/acpi/acpica/exoparg2.c create mode 100644 kernel/drivers/acpi/acpica/exoparg3.c create mode 100644 kernel/drivers/acpi/acpica/exoparg6.c create mode 100644 kernel/drivers/acpi/acpica/exprep.c create mode 100644 kernel/drivers/acpi/acpica/exregion.c create mode 100644 kernel/drivers/acpi/acpica/exresnte.c create mode 100644 kernel/drivers/acpi/acpica/exresolv.c create mode 100644 kernel/drivers/acpi/acpica/exresop.c create mode 100644 kernel/drivers/acpi/acpica/exstore.c create mode 100644 kernel/drivers/acpi/acpica/exstoren.c create mode 100644 kernel/drivers/acpi/acpica/exstorob.c create mode 100644 kernel/drivers/acpi/acpica/exsystem.c create mode 100644 kernel/drivers/acpi/acpica/exutils.c create mode 100644 kernel/drivers/acpi/acpica/hwacpi.c create mode 100644 kernel/drivers/acpi/acpica/hwesleep.c create mode 100644 kernel/drivers/acpi/acpica/hwgpe.c create mode 100644 kernel/drivers/acpi/acpica/hwpci.c create mode 100644 kernel/drivers/acpi/acpica/hwregs.c create mode 100644 kernel/drivers/acpi/acpica/hwsleep.c create mode 100644 kernel/drivers/acpi/acpica/hwtimer.c create mode 100644 kernel/drivers/acpi/acpica/hwvalid.c create mode 100644 kernel/drivers/acpi/acpica/hwxface.c create mode 100644 kernel/drivers/acpi/acpica/hwxfsleep.c create mode 100644 kernel/drivers/acpi/acpica/nsaccess.c create mode 100644 kernel/drivers/acpi/acpica/nsalloc.c create mode 100644 kernel/drivers/acpi/acpica/nsarguments.c create mode 100644 kernel/drivers/acpi/acpica/nsconvert.c create mode 100644 kernel/drivers/acpi/acpica/nsdump.c create mode 100644 kernel/drivers/acpi/acpica/nsdumpdv.c create mode 100644 kernel/drivers/acpi/acpica/nseval.c create mode 100644 kernel/drivers/acpi/acpica/nsinit.c create mode 100644 kernel/drivers/acpi/acpica/nsload.c create mode 100644 kernel/drivers/acpi/acpica/nsnames.c create mode 100644 kernel/drivers/acpi/acpica/nsobject.c create mode 100644 kernel/drivers/acpi/acpica/nsparse.c create mode 100644 kernel/drivers/acpi/acpica/nspredef.c create mode 100644 kernel/drivers/acpi/acpica/nsprepkg.c create mode 100644 kernel/drivers/acpi/acpica/nsrepair.c create mode 100644 kernel/drivers/acpi/acpica/nsrepair2.c create mode 100644 kernel/drivers/acpi/acpica/nssearch.c create mode 100644 kernel/drivers/acpi/acpica/nsutils.c create mode 100644 kernel/drivers/acpi/acpica/nswalk.c create mode 100644 kernel/drivers/acpi/acpica/nsxfeval.c create mode 100644 kernel/drivers/acpi/acpica/nsxfname.c create mode 100644 kernel/drivers/acpi/acpica/nsxfobj.c create mode 100644 kernel/drivers/acpi/acpica/psargs.c create mode 100644 kernel/drivers/acpi/acpica/psloop.c create mode 100644 kernel/drivers/acpi/acpica/psobject.c create mode 100644 kernel/drivers/acpi/acpica/psopcode.c create mode 100644 kernel/drivers/acpi/acpica/psopinfo.c create mode 100644 kernel/drivers/acpi/acpica/psparse.c create mode 100644 kernel/drivers/acpi/acpica/psscope.c create mode 100644 kernel/drivers/acpi/acpica/pstree.c create mode 100644 kernel/drivers/acpi/acpica/psutils.c create mode 100644 kernel/drivers/acpi/acpica/pswalk.c create mode 100644 kernel/drivers/acpi/acpica/psxface.c create mode 100644 kernel/drivers/acpi/acpica/rsaddr.c create mode 100644 kernel/drivers/acpi/acpica/rscalc.c create mode 100644 kernel/drivers/acpi/acpica/rscreate.c create mode 100644 kernel/drivers/acpi/acpica/rsdump.c create mode 100644 kernel/drivers/acpi/acpica/rsdumpinfo.c create mode 100644 kernel/drivers/acpi/acpica/rsinfo.c create mode 100644 kernel/drivers/acpi/acpica/rsio.c create mode 100644 kernel/drivers/acpi/acpica/rsirq.c create mode 100644 kernel/drivers/acpi/acpica/rslist.c create mode 100644 kernel/drivers/acpi/acpica/rsmemory.c create mode 100644 kernel/drivers/acpi/acpica/rsmisc.c create mode 100644 kernel/drivers/acpi/acpica/rsserial.c create mode 100644 kernel/drivers/acpi/acpica/rsutils.c create mode 100644 kernel/drivers/acpi/acpica/rsxface.c create mode 100644 kernel/drivers/acpi/acpica/tbdata.c create mode 100644 kernel/drivers/acpi/acpica/tbfadt.c create mode 100644 kernel/drivers/acpi/acpica/tbfind.c create mode 100644 kernel/drivers/acpi/acpica/tbinstal.c create mode 100644 kernel/drivers/acpi/acpica/tbprint.c create mode 100644 kernel/drivers/acpi/acpica/tbutils.c create mode 100644 kernel/drivers/acpi/acpica/tbxface.c create mode 100644 kernel/drivers/acpi/acpica/tbxfload.c create mode 100644 kernel/drivers/acpi/acpica/tbxfroot.c create mode 100644 kernel/drivers/acpi/acpica/utaddress.c create mode 100644 kernel/drivers/acpi/acpica/utalloc.c create mode 100644 kernel/drivers/acpi/acpica/utbuffer.c create mode 100644 kernel/drivers/acpi/acpica/utcache.c create mode 100644 kernel/drivers/acpi/acpica/utcopy.c create mode 100644 kernel/drivers/acpi/acpica/utdebug.c create mode 100644 kernel/drivers/acpi/acpica/utdecode.c create mode 100644 kernel/drivers/acpi/acpica/utdelete.c create mode 100644 kernel/drivers/acpi/acpica/uterror.c create mode 100644 kernel/drivers/acpi/acpica/uteval.c create mode 100644 kernel/drivers/acpi/acpica/utexcep.c create mode 100644 kernel/drivers/acpi/acpica/utfileio.c create mode 100644 kernel/drivers/acpi/acpica/utglobal.c create mode 100644 kernel/drivers/acpi/acpica/uthex.c create mode 100644 kernel/drivers/acpi/acpica/utids.c create mode 100644 kernel/drivers/acpi/acpica/utinit.c create mode 100644 kernel/drivers/acpi/acpica/utlock.c create mode 100644 kernel/drivers/acpi/acpica/utmath.c create mode 100644 kernel/drivers/acpi/acpica/utmisc.c create mode 100644 kernel/drivers/acpi/acpica/utmutex.c create mode 100644 kernel/drivers/acpi/acpica/utobject.c create mode 100644 kernel/drivers/acpi/acpica/utosi.c create mode 100644 kernel/drivers/acpi/acpica/utownerid.c create mode 100644 kernel/drivers/acpi/acpica/utpredef.c create mode 100644 kernel/drivers/acpi/acpica/utprint.c create mode 100644 kernel/drivers/acpi/acpica/utresrc.c create mode 100644 kernel/drivers/acpi/acpica/utstate.c create mode 100644 kernel/drivers/acpi/acpica/utstring.c create mode 100644 kernel/drivers/acpi/acpica/uttrack.c create mode 100644 kernel/drivers/acpi/acpica/utuuid.c create mode 100644 kernel/drivers/acpi/acpica/utxface.c create mode 100644 kernel/drivers/acpi/acpica/utxferror.c create mode 100644 kernel/drivers/acpi/acpica/utxfinit.c create mode 100644 kernel/drivers/acpi/acpica/utxfmutex.c create mode 100644 kernel/drivers/acpi/apei/Kconfig create mode 100644 kernel/drivers/acpi/apei/Makefile create mode 100644 kernel/drivers/acpi/apei/apei-base.c create mode 100644 kernel/drivers/acpi/apei/apei-internal.h create mode 100644 kernel/drivers/acpi/apei/einj.c create mode 100644 kernel/drivers/acpi/apei/erst-dbg.c create mode 100644 kernel/drivers/acpi/apei/erst.c create mode 100644 kernel/drivers/acpi/apei/ghes.c create mode 100644 kernel/drivers/acpi/apei/hest.c create mode 100644 kernel/drivers/acpi/battery.c create mode 100644 kernel/drivers/acpi/battery.h create mode 100644 kernel/drivers/acpi/bgrt.c create mode 100644 kernel/drivers/acpi/blacklist.c create mode 100644 kernel/drivers/acpi/bus.c create mode 100644 kernel/drivers/acpi/button.c create mode 100644 kernel/drivers/acpi/cm_sbs.c create mode 100644 kernel/drivers/acpi/container.c create mode 100644 kernel/drivers/acpi/custom_method.c create mode 100644 kernel/drivers/acpi/debugfs.c create mode 100644 kernel/drivers/acpi/device_pm.c create mode 100644 kernel/drivers/acpi/dock.c create mode 100644 kernel/drivers/acpi/ec.c create mode 100644 kernel/drivers/acpi/ec_sys.c create mode 100644 kernel/drivers/acpi/event.c create mode 100644 kernel/drivers/acpi/fan.c create mode 100644 kernel/drivers/acpi/glue.c create mode 100644 kernel/drivers/acpi/gsi.c create mode 100644 kernel/drivers/acpi/hed.c create mode 100644 kernel/drivers/acpi/int340x_thermal.c create mode 100644 kernel/drivers/acpi/internal.h create mode 100644 kernel/drivers/acpi/ioapic.c create mode 100644 kernel/drivers/acpi/numa.c create mode 100644 kernel/drivers/acpi/nvs.c create mode 100644 kernel/drivers/acpi/osl.c create mode 100644 kernel/drivers/acpi/pci_irq.c create mode 100644 kernel/drivers/acpi/pci_link.c create mode 100644 kernel/drivers/acpi/pci_root.c create mode 100644 kernel/drivers/acpi/pci_slot.c create mode 100644 kernel/drivers/acpi/pmic/intel_pmic.c create mode 100644 kernel/drivers/acpi/pmic/intel_pmic.h create mode 100644 kernel/drivers/acpi/pmic/intel_pmic_crc.c create mode 100644 kernel/drivers/acpi/pmic/intel_pmic_xpower.c create mode 100644 kernel/drivers/acpi/power.c create mode 100644 kernel/drivers/acpi/proc.c create mode 100644 kernel/drivers/acpi/processor_core.c create mode 100644 kernel/drivers/acpi/processor_driver.c create mode 100644 kernel/drivers/acpi/processor_idle.c create mode 100644 kernel/drivers/acpi/processor_pdc.c create mode 100644 kernel/drivers/acpi/processor_perflib.c create mode 100644 kernel/drivers/acpi/processor_thermal.c create mode 100644 kernel/drivers/acpi/processor_throttling.c create mode 100644 kernel/drivers/acpi/property.c create mode 100644 kernel/drivers/acpi/reboot.c create mode 100644 kernel/drivers/acpi/resource.c create mode 100644 kernel/drivers/acpi/sbs.c create mode 100644 kernel/drivers/acpi/sbshc.c create mode 100644 kernel/drivers/acpi/sbshc.h create mode 100644 kernel/drivers/acpi/scan.c create mode 100644 kernel/drivers/acpi/sleep.c create mode 100644 kernel/drivers/acpi/sleep.h create mode 100644 kernel/drivers/acpi/sysfs.c create mode 100644 kernel/drivers/acpi/tables.c create mode 100644 kernel/drivers/acpi/thermal.c create mode 100644 kernel/drivers/acpi/utils.c create mode 100644 kernel/drivers/acpi/video.c create mode 100644 kernel/drivers/acpi/video_detect.c create mode 100644 kernel/drivers/acpi/wakeup.c (limited to 'kernel/drivers/acpi') diff --git a/kernel/drivers/acpi/Kconfig b/kernel/drivers/acpi/Kconfig new file mode 100644 index 000000000..ab2cbb51c --- /dev/null +++ b/kernel/drivers/acpi/Kconfig @@ -0,0 +1,433 @@ +# +# ACPI Configuration +# + +menuconfig ACPI + bool "ACPI (Advanced Configuration and Power Interface) Support" + depends on !IA64_HP_SIM + depends on IA64 || X86 || (ARM64 && EXPERT) + depends on PCI + select PNP + default y + help + Advanced Configuration and Power Interface (ACPI) support for + Linux requires an ACPI-compliant platform (hardware/firmware), + and assumes the presence of OS-directed configuration and power + management (OSPM) software. This option will enlarge your + kernel by about 70K. + + Linux ACPI provides a robust functional replacement for several + legacy configuration and power management interfaces, including + the Plug-and-Play BIOS specification (PnP BIOS), the + MultiProcessor Specification (MPS), and the Advanced Power + Management (APM) specification. If both ACPI and APM support + are configured, ACPI is used. + + The project home page for the Linux ACPI subsystem is here: + + + Linux support for ACPI is based on Intel Corporation's ACPI + Component Architecture (ACPI CA). For more information on the + ACPI CA, see: + + + ACPI is an open industry specification originally co-developed by + Hewlett-Packard, Intel, Microsoft, Phoenix, and Toshiba. Currently, + it is developed by the ACPI Specification Working Group (ASWG) under + the UEFI Forum and any UEFI member can join the ASWG and contribute + to the ACPI specification. + The specification is available at: + + + +if ACPI + +config ACPI_LEGACY_TABLES_LOOKUP + bool + +config ARCH_MIGHT_HAVE_ACPI_PDC + bool + +config ACPI_GENERIC_GSI + bool + +config ACPI_SYSTEM_POWER_STATES_SUPPORT + bool + +config ACPI_SLEEP + bool + depends on SUSPEND || HIBERNATION + depends on ACPI_SYSTEM_POWER_STATES_SUPPORT + default y + +config ACPI_PROCFS_POWER + bool "Deprecated power /proc/acpi directories" + depends on PROC_FS + help + For backwards compatibility, this option allows + deprecated power /proc/acpi/ directories to exist, even when + they have been replaced by functions in /sys. + The deprecated directories (and their replacements) include: + /proc/acpi/battery/* (/sys/class/power_supply/*) + /proc/acpi/ac_adapter/* (sys/class/power_supply/*) + This option has no effect on /proc/acpi/ directories + and functions, which do not yet exist in /sys + This option, together with the proc directories, will be + deleted in the future. + + Say N to delete power /proc/acpi/ directories that have moved to /sys/ + +config ACPI_EC_DEBUGFS + tristate "EC read/write access through /sys/kernel/debug/ec" + default n + help + Say N to disable Embedded Controller /sys/kernel/debug interface + + Be aware that using this interface can confuse your Embedded + Controller in a way that a normal reboot is not enough. You then + have to power off your system, and remove the laptop battery for + some seconds. + An Embedded Controller typically is available on laptops and reads + sensor values like battery state and temperature. + The kernel accesses the EC through ACPI parsed code provided by BIOS + tables. This option allows to access the EC directly without ACPI + code being involved. + Thus this option is a debug option that helps to write ACPI drivers + and can be used to identify ACPI code or EC firmware bugs. + +config ACPI_AC + tristate "AC Adapter" + depends on X86 + select POWER_SUPPLY + default y + help + This driver supports the AC Adapter object, which indicates + whether a system is on AC or not. If you have a system that can + switch between A/C and battery, say Y. + + To compile this driver as a module, choose M here: + the module will be called ac. + +config ACPI_BATTERY + tristate "Battery" + depends on X86 + select POWER_SUPPLY + default y + help + This driver adds support for battery information through + /proc/acpi/battery. If you have a mobile system with a battery, + say Y. + + To compile this driver as a module, choose M here: + the module will be called battery. + +config ACPI_BUTTON + tristate "Button" + depends on INPUT + default y + help + This driver handles events on the power, sleep, and lid buttons. + A daemon reads events from input devices or via netlink and + performs user-defined actions such as shutting down the system. + This is necessary for software-controlled poweroff. + + To compile this driver as a module, choose M here: + the module will be called button. + +config ACPI_VIDEO + tristate "Video" + depends on X86 && BACKLIGHT_CLASS_DEVICE + depends on INPUT + select THERMAL + help + This driver implements the ACPI Extensions For Display Adapters + for integrated graphics devices on motherboard, as specified in + ACPI 2.0 Specification, Appendix B. This supports basic operations + such as defining the video POST device, retrieving EDID information, + and setting up a video output. + + To compile this driver as a module, choose M here: + the module will be called video. + +config ACPI_FAN + tristate "Fan" + depends on THERMAL + default y + help + This driver supports ACPI fan devices, allowing user-mode + applications to perform basic fan control (on, off, status). + + To compile this driver as a module, choose M here: + the module will be called fan. + +config ACPI_DOCK + bool "Dock" + help + This driver supports ACPI-controlled docking stations and removable + drive bays such as the IBM Ultrabay and the Dell Module Bay. + +config ACPI_PROCESSOR + tristate "Processor" + select THERMAL + select CPU_IDLE + depends on X86 || IA64 + default y + help + This driver installs ACPI as the idle handler for Linux and uses + ACPI C2 and C3 processor states to save power on systems that + support it. It is required by several flavors of cpufreq + performance-state drivers. + + To compile this driver as a module, choose M here: + the module will be called processor. + +config ACPI_IPMI + tristate "IPMI" + depends on IPMI_SI + default n + help + This driver enables the ACPI to access the BMC controller. And it + uses the IPMI request/response message to communicate with BMC + controller, which can be found on on the server. + + To compile this driver as a module, choose M here: + the module will be called as acpi_ipmi. + +config ACPI_HOTPLUG_CPU + bool + depends on ACPI_PROCESSOR && HOTPLUG_CPU + select ACPI_CONTAINER + default y + +config ACPI_PROCESSOR_AGGREGATOR + tristate "Processor Aggregator" + depends on ACPI_PROCESSOR + depends on X86 + help + ACPI 4.0 defines processor Aggregator, which enables OS to perform + specific processor configuration and control that applies to all + processors in the platform. Currently only logical processor idling + is defined, which is to reduce power consumption. This driver + supports the new device. + +config ACPI_THERMAL + tristate "Thermal Zone" + depends on ACPI_PROCESSOR + select THERMAL + default y + help + This driver supports ACPI thermal zones. Most mobile and + some desktop systems support ACPI thermal zones. It is HIGHLY + recommended that this option be enabled, as your processor(s) + may be damaged without it. + + To compile this driver as a module, choose M here: + the module will be called thermal. + +config ACPI_NUMA + bool "NUMA support" + depends on NUMA + depends on (X86 || IA64) + default y if IA64_GENERIC || IA64_SGI_SN2 + +config ACPI_CUSTOM_DSDT_FILE + string "Custom DSDT Table file to include" + default "" + depends on !STANDALONE + help + This option supports a custom DSDT by linking it into the kernel. + See Documentation/acpi/dsdt-override.txt + + Enter the full path name to the file which includes the AmlCode + declaration. + + If unsure, don't enter a file name. + +config ACPI_CUSTOM_DSDT + bool + default ACPI_CUSTOM_DSDT_FILE != "" + +config ACPI_INITRD_TABLE_OVERRIDE + bool "ACPI tables override via initrd" + depends on BLK_DEV_INITRD && X86 + default n + help + This option provides functionality to override arbitrary ACPI tables + via initrd. No functional change if no ACPI tables are passed via + initrd, therefore it's safe to say Y. + See Documentation/acpi/initrd_table_override.txt for details + +config ACPI_DEBUG + bool "Debug Statements" + default n + help + The ACPI subsystem can produce debug output. Saying Y enables this + output and increases the kernel size by around 50K. + + Use the acpi.debug_layer and acpi.debug_level kernel command-line + parameters documented in Documentation/acpi/debug.txt and + Documentation/kernel-parameters.txt to control the type and + amount of debug output. + +config ACPI_PCI_SLOT + bool "PCI slot detection driver" + depends on SYSFS + default n + help + This driver creates entries in /sys/bus/pci/slots/ for all PCI + slots in the system. This can help correlate PCI bus addresses, + i.e., segment/bus/device/function tuples, with physical slots in + the system. If you are unsure, say N. + +config X86_PM_TIMER + bool "Power Management Timer Support" if EXPERT + depends on X86 + default y + help + The Power Management Timer is available on all ACPI-capable, + in most cases even if ACPI is unusable or blacklisted. + + This timing source is not affected by power management features + like aggressive processor idling, throttling, frequency and/or + voltage scaling, unlike the commonly used Time Stamp Counter + (TSC) timing source. + + You should nearly always say Y here because many modern + systems require this timer. + +config ACPI_CONTAINER + bool "Container and Module Devices" + default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU) + help + This driver supports ACPI Container and Module devices (IDs + ACPI0004, PNP0A05, and PNP0A06). + + This helps support hotplug of nodes, CPUs, and memory. + + To compile this driver as a module, choose M here: + the module will be called container. + +config ACPI_HOTPLUG_MEMORY + bool "Memory Hotplug" + depends on MEMORY_HOTPLUG + help + This driver supports ACPI memory hotplug. The driver + fields notifications on ACPI memory devices (PNP0C80), + which represent memory ranges that may be onlined or + offlined during runtime. + + If your hardware and firmware do not support adding or + removing memory devices at runtime, you need not enable + this driver. + + To compile this driver as a module, choose M here: + the module will be called acpi_memhotplug. + +config ACPI_HOTPLUG_IOAPIC + bool + depends on PCI + depends on X86_IO_APIC + default y + +config ACPI_SBS + tristate "Smart Battery System" + depends on X86 + select POWER_SUPPLY + help + This driver supports the Smart Battery System, another + type of access to battery information, found on some laptops. + + To compile this driver as a module, choose M here: + the modules will be called sbs and sbshc. + +config ACPI_HED + tristate "Hardware Error Device" + help + This driver supports the Hardware Error Device (PNP0C33), + which is used to report some hardware errors notified via + SCI, mainly the corrected errors. + +config ACPI_CUSTOM_METHOD + tristate "Allow ACPI methods to be inserted/replaced at run time" + depends on DEBUG_FS + default n + help + This debug facility allows ACPI AML methods to be inserted and/or + replaced without rebooting the system. For details refer to: + Documentation/acpi/method-customizing.txt. + + NOTE: This option is security sensitive, because it allows arbitrary + kernel memory to be written to by root (uid=0) users, allowing them + to bypass certain security measures (e.g. if root is not allowed to + load additional kernel modules after boot, this feature may be used + to override that restriction). + +config ACPI_BGRT + bool "Boottime Graphics Resource Table support" + depends on EFI && X86 + help + This driver adds support for exposing the ACPI Boottime Graphics + Resource Table, which allows the operating system to obtain + data from the firmware boot splash. It will appear under + /sys/firmware/acpi/bgrt/ . + +config ACPI_REDUCED_HARDWARE_ONLY + bool "Hardware-reduced ACPI support only" if EXPERT + def_bool n + help + This config item changes the way the ACPI code is built. When this + option is selected, the kernel will use a specialized version of + ACPICA that ONLY supports the ACPI "reduced hardware" mode. The + resulting kernel will be smaller but it will also be restricted to + running in ACPI reduced hardware mode ONLY. + + If you are unsure what to do, do not enable this option. + +source "drivers/acpi/apei/Kconfig" + +config ACPI_EXTLOG + tristate "Extended Error Log support" + depends on X86_MCE && X86_LOCAL_APIC + select UEFI_CPER + select RAS + default n + help + Certain usages such as Predictive Failure Analysis (PFA) require + more information about the error than what can be described in + processor machine check banks. Most server processors log + additional information about the error in processor uncore + registers. Since the addresses and layout of these registers vary + widely from one processor to another, system software cannot + readily make use of them. To complicate matters further, some of + the additional error information cannot be constructed without + detailed knowledge about platform topology. + + Enhanced MCA Logging allows firmware to provide additional error + information to system software, synchronous with MCE or CMCI. This + driver adds support for that functionality with corresponding + tracepoint which carries that information to userspace. + +menuconfig PMIC_OPREGION + bool "PMIC (Power Management Integrated Circuit) operation region support" + help + Select this option to enable support for ACPI operation + region of the PMIC chip. The operation region can be used + to control power rails and sensor reading/writing on the + PMIC chip. + +if PMIC_OPREGION +config CRC_PMIC_OPREGION + bool "ACPI operation region support for CrystalCove PMIC" + depends on INTEL_SOC_PMIC + help + This config adds ACPI operation region support for CrystalCove PMIC. + +config XPOWER_PMIC_OPREGION + bool "ACPI operation region support for XPower AXP288 PMIC" + depends on AXP288_ADC = y + help + This config adds ACPI operation region support for XPower AXP288 PMIC. + +endif + +endif # ACPI diff --git a/kernel/drivers/acpi/Makefile b/kernel/drivers/acpi/Makefile new file mode 100644 index 000000000..8a063e276 --- /dev/null +++ b/kernel/drivers/acpi/Makefile @@ -0,0 +1,97 @@ +# +# Makefile for the Linux ACPI interpreter +# + +ccflags-y := -Os +ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT + +# +# ACPI Boot-Time Table Parsing +# +obj-y += tables.o +obj-$(CONFIG_X86) += blacklist.o + +# +# ACPI Core Subsystem (Interpreter) +# +obj-y += acpi.o \ + acpica/ + +# All the builtin files are in the "acpi." module_param namespace. +acpi-y += osl.o utils.o reboot.o +acpi-y += nvs.o + +# Power management related files +acpi-y += wakeup.o +acpi-$(CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT) += sleep.o +acpi-y += device_pm.o +acpi-$(CONFIG_ACPI_SLEEP) += proc.o + + +# +# ACPI Bus and Device Drivers +# +acpi-y += bus.o glue.o +acpi-y += scan.o +acpi-y += resource.o +acpi-y += acpi_processor.o +acpi-y += processor_core.o +acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o +acpi-y += ec.o +acpi-$(CONFIG_ACPI_DOCK) += dock.o +acpi-y += pci_root.o pci_link.o pci_irq.o +acpi-y += acpi_lpss.o acpi_apd.o +acpi-y += acpi_platform.o +acpi-y += acpi_pnp.o +acpi-y += int340x_thermal.o +acpi-y += power.o +acpi-y += event.o +acpi-y += sysfs.o +acpi-y += property.o +acpi-$(CONFIG_X86) += acpi_cmos_rtc.o +acpi-$(CONFIG_DEBUG_FS) += debugfs.o +acpi-$(CONFIG_ACPI_NUMA) += numa.o +acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o +ifdef CONFIG_ACPI_VIDEO +acpi-y += video_detect.o +endif +acpi-y += acpi_lpat.o +acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o + +# These are (potentially) separate modules + +# IPMI may be used by other drivers, so it has to initialise before them +obj-$(CONFIG_ACPI_IPMI) += acpi_ipmi.o + +obj-$(CONFIG_ACPI_AC) += ac.o +obj-$(CONFIG_ACPI_BUTTON) += button.o +obj-$(CONFIG_ACPI_FAN) += fan.o +obj-$(CONFIG_ACPI_VIDEO) += video.o +obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o +obj-$(CONFIG_ACPI_PROCESSOR) += processor.o +obj-y += container.o +obj-$(CONFIG_ACPI_THERMAL) += thermal.o +obj-y += acpi_memhotplug.o +obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o +obj-$(CONFIG_ACPI_BATTERY) += battery.o +obj-$(CONFIG_ACPI_SBS) += sbshc.o +obj-$(CONFIG_ACPI_SBS) += sbs.o +obj-$(CONFIG_ACPI_HED) += hed.o +obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o +obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o +obj-$(CONFIG_ACPI_BGRT) += bgrt.o + +# processor has its own "processor." module_param namespace +processor-y := processor_driver.o processor_throttling.o +processor-y += processor_idle.o processor_thermal.o +processor-$(CONFIG_CPU_FREQ) += processor_perflib.o + +obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o + +obj-$(CONFIG_ACPI_APEI) += apei/ + +obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o + +obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o +obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o +obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o diff --git a/kernel/drivers/acpi/ac.c b/kernel/drivers/acpi/ac.c new file mode 100644 index 000000000..bbcc2b5a7 --- /dev/null +++ b/kernel/drivers/acpi/ac.c @@ -0,0 +1,462 @@ +/* + * acpi_ac.c - ACPI AC Adapter Driver ($Revision: 27 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_ACPI_PROCFS_POWER +#include +#include +#endif +#include +#include +#include +#include "battery.h" + +#define PREFIX "ACPI: " + +#define ACPI_AC_CLASS "ac_adapter" +#define ACPI_AC_DEVICE_NAME "AC Adapter" +#define ACPI_AC_FILE_STATE "state" +#define ACPI_AC_NOTIFY_STATUS 0x80 +#define ACPI_AC_STATUS_OFFLINE 0x00 +#define ACPI_AC_STATUS_ONLINE 0x01 +#define ACPI_AC_STATUS_UNKNOWN 0xFF + +#define _COMPONENT ACPI_AC_COMPONENT +ACPI_MODULE_NAME("ac"); + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI AC Adapter Driver"); +MODULE_LICENSE("GPL"); + + +static int acpi_ac_add(struct acpi_device *device); +static int acpi_ac_remove(struct acpi_device *device); +static void acpi_ac_notify(struct acpi_device *device, u32 event); + +static const struct acpi_device_id ac_device_ids[] = { + {"ACPI0003", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, ac_device_ids); + +#ifdef CONFIG_PM_SLEEP +static int acpi_ac_resume(struct device *dev); +#endif +static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); + +#ifdef CONFIG_ACPI_PROCFS_POWER +extern struct proc_dir_entry *acpi_lock_ac_dir(void); +extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); +static int acpi_ac_open_fs(struct inode *inode, struct file *file); +#endif + + +static int ac_sleep_before_get_state_ms; + +static struct acpi_driver acpi_ac_driver = { + .name = "ac", + .class = ACPI_AC_CLASS, + .ids = ac_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = acpi_ac_add, + .remove = acpi_ac_remove, + .notify = acpi_ac_notify, + }, + .drv.pm = &acpi_ac_pm, +}; + +struct acpi_ac { + struct power_supply *charger; + struct power_supply_desc charger_desc; + struct acpi_device * device; + unsigned long long state; + struct notifier_block battery_nb; +}; + +#define to_acpi_ac(x) power_supply_get_drvdata(x) + +#ifdef CONFIG_ACPI_PROCFS_POWER +static const struct file_operations acpi_ac_fops = { + .owner = THIS_MODULE, + .open = acpi_ac_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +/* -------------------------------------------------------------------------- + AC Adapter Management + -------------------------------------------------------------------------- */ + +static int acpi_ac_get_state(struct acpi_ac *ac) +{ + acpi_status status = AE_OK; + + if (!ac) + return -EINVAL; + + status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL, + &ac->state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Error reading AC Adapter state")); + ac->state = ACPI_AC_STATUS_UNKNOWN; + return -ENODEV; + } + + return 0; +} + +/* -------------------------------------------------------------------------- + sysfs I/F + -------------------------------------------------------------------------- */ +static int get_ac_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct acpi_ac *ac = to_acpi_ac(psy); + + if (!ac) + return -ENODEV; + + if (acpi_ac_get_state(ac)) + return -ENODEV; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = ac->state; + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +#ifdef CONFIG_ACPI_PROCFS_POWER +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_ac_dir; + +static int acpi_ac_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_ac *ac = seq->private; + + + if (!ac) + return 0; + + if (acpi_ac_get_state(ac)) { + seq_puts(seq, "ERROR: Unable to read AC Adapter state\n"); + return 0; + } + + seq_puts(seq, "state: "); + switch (ac->state) { + case ACPI_AC_STATUS_OFFLINE: + seq_puts(seq, "off-line\n"); + break; + case ACPI_AC_STATUS_ONLINE: + seq_puts(seq, "on-line\n"); + break; + default: + seq_puts(seq, "unknown\n"); + break; + } + + return 0; +} + +static int acpi_ac_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_ac_seq_show, PDE_DATA(inode)); +} + +static int acpi_ac_add_fs(struct acpi_ac *ac) +{ + struct proc_dir_entry *entry = NULL; + + printk(KERN_WARNING PREFIX "Deprecated procfs I/F for AC is loaded," + " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); + if (!acpi_device_dir(ac->device)) { + acpi_device_dir(ac->device) = + proc_mkdir(acpi_device_bid(ac->device), acpi_ac_dir); + if (!acpi_device_dir(ac->device)) + return -ENODEV; + } + + /* 'state' [R] */ + entry = proc_create_data(ACPI_AC_FILE_STATE, + S_IRUGO, acpi_device_dir(ac->device), + &acpi_ac_fops, ac); + if (!entry) + return -ENODEV; + return 0; +} + +static int acpi_ac_remove_fs(struct acpi_ac *ac) +{ + + if (acpi_device_dir(ac->device)) { + remove_proc_entry(ACPI_AC_FILE_STATE, + acpi_device_dir(ac->device)); + remove_proc_entry(acpi_device_bid(ac->device), acpi_ac_dir); + acpi_device_dir(ac->device) = NULL; + } + + return 0; +} +#endif + +/* -------------------------------------------------------------------------- + Driver Model + -------------------------------------------------------------------------- */ + +static void acpi_ac_notify(struct acpi_device *device, u32 event) +{ + struct acpi_ac *ac = acpi_driver_data(device); + + if (!ac) + return; + + switch (event) { + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + case ACPI_AC_NOTIFY_STATUS: + case ACPI_NOTIFY_BUS_CHECK: + case ACPI_NOTIFY_DEVICE_CHECK: + /* + * A buggy BIOS may notify AC first and then sleep for + * a specific time before doing actual operations in the + * EC event handler (_Qxx). This will cause the AC state + * reported by the ACPI event to be incorrect, so wait for a + * specific time for the EC event handler to make progress. + */ + if (ac_sleep_before_get_state_ms > 0) + msleep(ac_sleep_before_get_state_ms); + + acpi_ac_get_state(ac); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, + (u32) ac->state); + acpi_notifier_call_chain(device, event, (u32) ac->state); + kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + } + + return; +} + +static int acpi_ac_battery_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb); + struct acpi_bus_event *event = (struct acpi_bus_event *)data; + + /* + * On HP Pavilion dv6-6179er AC status notifications aren't triggered + * when adapter is plugged/unplugged. However, battery status + * notifcations are triggered when battery starts charging or + * discharging. Re-reading AC status triggers lost AC notifications, + * if AC status has changed. + */ + if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 && + event->type == ACPI_BATTERY_NOTIFY_STATUS) + acpi_ac_get_state(ac); + + return NOTIFY_OK; +} + +static int thinkpad_e530_quirk(const struct dmi_system_id *d) +{ + ac_sleep_before_get_state_ms = 1000; + return 0; +} + +static struct dmi_system_id ac_dmi_table[] = { + { + .callback = thinkpad_e530_quirk, + .ident = "thinkpad e530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), + }, + }, + {}, +}; + +static int acpi_ac_add(struct acpi_device *device) +{ + struct power_supply_config psy_cfg = {}; + int result = 0; + struct acpi_ac *ac = NULL; + + + if (!device) + return -EINVAL; + + ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL); + if (!ac) + return -ENOMEM; + + ac->device = device; + strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_AC_CLASS); + device->driver_data = ac; + + result = acpi_ac_get_state(ac); + if (result) + goto end; + + psy_cfg.drv_data = ac; + + ac->charger_desc.name = acpi_device_bid(device); +#ifdef CONFIG_ACPI_PROCFS_POWER + result = acpi_ac_add_fs(ac); + if (result) + goto end; +#endif + ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS; + ac->charger_desc.properties = ac_props; + ac->charger_desc.num_properties = ARRAY_SIZE(ac_props); + ac->charger_desc.get_property = get_ac_property; + ac->charger = power_supply_register(&ac->device->dev, + &ac->charger_desc, &psy_cfg); + if (IS_ERR(ac->charger)) { + result = PTR_ERR(ac->charger); + goto end; + } + + printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + acpi_device_name(device), acpi_device_bid(device), + ac->state ? "on-line" : "off-line"); + + ac->battery_nb.notifier_call = acpi_ac_battery_notify; + register_acpi_notifier(&ac->battery_nb); +end: + if (result) { +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_ac_remove_fs(ac); +#endif + kfree(ac); + } + + dmi_check_system(ac_dmi_table); + return result; +} + +#ifdef CONFIG_PM_SLEEP +static int acpi_ac_resume(struct device *dev) +{ + struct acpi_ac *ac; + unsigned old_state; + + if (!dev) + return -EINVAL; + + ac = acpi_driver_data(to_acpi_device(dev)); + if (!ac) + return -EINVAL; + + old_state = ac->state; + if (acpi_ac_get_state(ac)) + return 0; + if (old_state != ac->state) + kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + return 0; +} +#else +#define acpi_ac_resume NULL +#endif + +static int acpi_ac_remove(struct acpi_device *device) +{ + struct acpi_ac *ac = NULL; + + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + ac = acpi_driver_data(device); + + power_supply_unregister(ac->charger); + unregister_acpi_notifier(&ac->battery_nb); + +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_ac_remove_fs(ac); +#endif + + kfree(ac); + + return 0; +} + +static int __init acpi_ac_init(void) +{ + int result; + + if (acpi_disabled) + return -ENODEV; + +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_ac_dir = acpi_lock_ac_dir(); + if (!acpi_ac_dir) + return -ENODEV; +#endif + + + result = acpi_bus_register_driver(&acpi_ac_driver); + if (result < 0) { +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_unlock_ac_dir(acpi_ac_dir); +#endif + return -ENODEV; + } + + return 0; +} + +static void __exit acpi_ac_exit(void) +{ + acpi_bus_unregister_driver(&acpi_ac_driver); +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_unlock_ac_dir(acpi_ac_dir); +#endif +} +module_init(acpi_ac_init); +module_exit(acpi_ac_exit); diff --git a/kernel/drivers/acpi/acpi_apd.c b/kernel/drivers/acpi/acpi_apd.c new file mode 100644 index 000000000..3984ea96e --- /dev/null +++ b/kernel/drivers/acpi/acpi_apd.c @@ -0,0 +1,150 @@ +/* + * AMD ACPI support for ACPI2platform device. + * + * Copyright (c) 2014,2015 AMD Corporation. + * Authors: Ken Xue + * Wu, Jeff + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +ACPI_MODULE_NAME("acpi_apd"); +struct apd_private_data; + +/** + * ACPI_APD_SYSFS : add device attributes in sysfs + * ACPI_APD_PM : attach power domain to device + */ +#define ACPI_APD_SYSFS BIT(0) +#define ACPI_APD_PM BIT(1) + +/** + * struct apd_device_desc - a descriptor for apd device + * @flags: device flags like %ACPI_APD_SYSFS, %ACPI_APD_PM + * @fixed_clk_rate: fixed rate input clock source for acpi device; + * 0 means no fixed rate input clock source + * @setup: a hook routine to set device resource during create platform device + * + * Device description defined as acpi_device_id.driver_data + */ +struct apd_device_desc { + unsigned int flags; + unsigned int fixed_clk_rate; + int (*setup)(struct apd_private_data *pdata); +}; + +struct apd_private_data { + struct clk *clk; + struct acpi_device *adev; + const struct apd_device_desc *dev_desc; +}; + +#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE +#define APD_ADDR(desc) ((unsigned long)&desc) + +static int acpi_apd_setup(struct apd_private_data *pdata) +{ + const struct apd_device_desc *dev_desc = pdata->dev_desc; + struct clk *clk = ERR_PTR(-ENODEV); + + if (dev_desc->fixed_clk_rate) { + clk = clk_register_fixed_rate(&pdata->adev->dev, + dev_name(&pdata->adev->dev), + NULL, CLK_IS_ROOT, + dev_desc->fixed_clk_rate); + clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev)); + pdata->clk = clk; + } + + return 0; +} + +static struct apd_device_desc cz_i2c_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 133000000, +}; + +static struct apd_device_desc cz_uart_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 48000000, +}; + +#else + +#define APD_ADDR(desc) (0UL) + +#endif /* CONFIG_X86_AMD_PLATFORM_DEVICE */ + +/** +* Create platform device during acpi scan attach handle. +* Return value > 0 on success of creating device. +*/ +static int acpi_apd_create_device(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + const struct apd_device_desc *dev_desc = (void *)id->driver_data; + struct apd_private_data *pdata; + struct platform_device *pdev; + int ret; + + if (!dev_desc) { + pdev = acpi_create_platform_device(adev); + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; + } + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->adev = adev; + pdata->dev_desc = dev_desc; + + if (dev_desc->setup) { + ret = dev_desc->setup(pdata); + if (ret) + goto err_out; + } + + adev->driver_data = pdata; + pdev = acpi_create_platform_device(adev); + if (!IS_ERR_OR_NULL(pdev)) + return 1; + + ret = PTR_ERR(pdev); + adev->driver_data = NULL; + + err_out: + kfree(pdata); + return ret; +} + +static const struct acpi_device_id acpi_apd_device_ids[] = { + /* Generic apd devices */ + { "AMD0010", APD_ADDR(cz_i2c_desc) }, + { "AMD0020", APD_ADDR(cz_uart_desc) }, + { "AMD0030", }, + { } +}; + +static struct acpi_scan_handler apd_handler = { + .ids = acpi_apd_device_ids, + .attach = acpi_apd_create_device, +}; + +void __init acpi_apd_init(void) +{ + acpi_scan_add_handler(&apd_handler); +} diff --git a/kernel/drivers/acpi/acpi_cmos_rtc.c b/kernel/drivers/acpi/acpi_cmos_rtc.c new file mode 100644 index 000000000..81dc75033 --- /dev/null +++ b/kernel/drivers/acpi/acpi_cmos_rtc.c @@ -0,0 +1,90 @@ +/* + * ACPI support for CMOS RTC Address Space access + * + * Copyright (C) 2013, Intel Corporation + * Authors: Lan Tianyu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +ACPI_MODULE_NAME("cmos rtc"); + +static const struct acpi_device_id acpi_cmos_rtc_ids[] = { + { "PNP0B00" }, + { "PNP0B01" }, + { "PNP0B02" }, + {} +}; + +static acpi_status +acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + int i; + u8 *value = (u8 *)value64; + + if (address > 0xff || !value64) + return AE_BAD_PARAMETER; + + if (function != ACPI_WRITE && function != ACPI_READ) + return AE_BAD_PARAMETER; + + spin_lock_irq(&rtc_lock); + + for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value) + if (function == ACPI_READ) + *value = CMOS_READ(address); + else + CMOS_WRITE(*value, address); + + spin_unlock_irq(&rtc_lock); + + return AE_OK; +} + +static int acpi_install_cmos_rtc_space_handler(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + acpi_status status; + + status = acpi_install_address_space_handler(adev->handle, + ACPI_ADR_SPACE_CMOS, + &acpi_cmos_rtc_space_handler, + NULL, NULL); + if (ACPI_FAILURE(status)) { + pr_err(PREFIX "Error installing CMOS-RTC region handler\n"); + return -ENODEV; + } + + return 1; +} + +static void acpi_remove_cmos_rtc_space_handler(struct acpi_device *adev) +{ + if (ACPI_FAILURE(acpi_remove_address_space_handler(adev->handle, + ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler))) + pr_err(PREFIX "Error removing CMOS-RTC region handler\n"); +} + +static struct acpi_scan_handler cmos_rtc_handler = { + .ids = acpi_cmos_rtc_ids, + .attach = acpi_install_cmos_rtc_space_handler, + .detach = acpi_remove_cmos_rtc_space_handler, +}; + +void __init acpi_cmos_rtc_init(void) +{ + acpi_scan_add_handler(&cmos_rtc_handler); +} diff --git a/kernel/drivers/acpi/acpi_extlog.c b/kernel/drivers/acpi/acpi_extlog.c new file mode 100644 index 000000000..b3842ffc1 --- /dev/null +++ b/kernel/drivers/acpi/acpi_extlog.c @@ -0,0 +1,327 @@ +/* + * Extended Error Log driver + * + * Copyright (C) 2013 Intel Corp. + * Author: Chen, Gong + * + * This file is licensed under GPLv2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apei/apei-internal.h" +#include + +#define EXT_ELOG_ENTRY_MASK GENMASK_ULL(51, 0) /* elog entry address mask */ + +#define EXTLOG_DSM_REV 0x0 +#define EXTLOG_FN_ADDR 0x1 + +#define FLAG_OS_OPTIN BIT(0) +#define ELOG_ENTRY_VALID (1ULL<<63) +#define ELOG_ENTRY_LEN 0x1000 + +#define EMCA_BUG \ + "Can not request iomem region <0x%016llx-0x%016llx> - eMCA disabled\n" + +struct extlog_l1_head { + u32 ver; /* Header Version */ + u32 hdr_len; /* Header Length */ + u64 total_len; /* entire L1 Directory length including this header */ + u64 elog_base; /* MCA Error Log Directory base address */ + u64 elog_len; /* MCA Error Log Directory length */ + u32 flags; /* bit 0 - OS/VMM Opt-in */ + u8 rev0[12]; + u32 entries; /* Valid L1 Directory entries per logical processor */ + u8 rev1[12]; +}; + +static int old_edac_report_status; + +static u8 extlog_dsm_uuid[] __initdata = "663E35AF-CC10-41A4-88EA-5470AF055295"; + +/* L1 table related physical address */ +static u64 elog_base; +static size_t elog_size; +static u64 l1_dirbase; +static size_t l1_size; + +/* L1 table related virtual address */ +static void __iomem *extlog_l1_addr; +static void __iomem *elog_addr; + +static void *elog_buf; + +static u64 *l1_entry_base; +static u32 l1_percpu_entry; + +#define ELOG_IDX(cpu, bank) \ + (cpu_physical_id(cpu) * l1_percpu_entry + (bank)) + +#define ELOG_ENTRY_DATA(idx) \ + (*(l1_entry_base + (idx))) + +#define ELOG_ENTRY_ADDR(phyaddr) \ + (phyaddr - elog_base + (u8 *)elog_addr) + +static struct acpi_hest_generic_status *extlog_elog_entry_check(int cpu, int bank) +{ + int idx; + u64 data; + struct acpi_hest_generic_status *estatus; + + WARN_ON(cpu < 0); + idx = ELOG_IDX(cpu, bank); + data = ELOG_ENTRY_DATA(idx); + if ((data & ELOG_ENTRY_VALID) == 0) + return NULL; + + data &= EXT_ELOG_ENTRY_MASK; + estatus = (struct acpi_hest_generic_status *)ELOG_ENTRY_ADDR(data); + + /* if no valid data in elog entry, just return */ + if (estatus->block_status == 0) + return NULL; + + return estatus; +} + +static void __print_extlog_rcd(const char *pfx, + struct acpi_hest_generic_status *estatus, int cpu) +{ + static atomic_t seqno; + unsigned int curr_seqno; + char pfx_seq[64]; + + if (!pfx) { + if (estatus->error_severity <= CPER_SEV_CORRECTED) + pfx = KERN_INFO; + else + pfx = KERN_ERR; + } + curr_seqno = atomic_inc_return(&seqno); + snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}", pfx, curr_seqno); + printk("%s""Hardware error detected on CPU%d\n", pfx_seq, cpu); + cper_estatus_print(pfx_seq, estatus); +} + +static int print_extlog_rcd(const char *pfx, + struct acpi_hest_generic_status *estatus, int cpu) +{ + /* Not more than 2 messages every 5 seconds */ + static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); + static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); + struct ratelimit_state *ratelimit; + + if (estatus->error_severity == CPER_SEV_CORRECTED || + (estatus->error_severity == CPER_SEV_INFORMATIONAL)) + ratelimit = &ratelimit_corrected; + else + ratelimit = &ratelimit_uncorrected; + if (__ratelimit(ratelimit)) { + __print_extlog_rcd(pfx, estatus, cpu); + return 0; + } + + return 1; +} + +static int extlog_print(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct mce *mce = (struct mce *)data; + int bank = mce->bank; + int cpu = mce->extcpu; + struct acpi_hest_generic_status *estatus, *tmp; + struct acpi_hest_generic_data *gdata; + const uuid_le *fru_id = &NULL_UUID_LE; + char *fru_text = ""; + uuid_le *sec_type; + static u32 err_seq; + + estatus = extlog_elog_entry_check(cpu, bank); + if (estatus == NULL) + return NOTIFY_DONE; + + memcpy(elog_buf, (void *)estatus, ELOG_ENTRY_LEN); + /* clear record status to enable BIOS to update it again */ + estatus->block_status = 0; + + tmp = (struct acpi_hest_generic_status *)elog_buf; + + if (!ras_userspace_consumers()) { + print_extlog_rcd(NULL, tmp, cpu); + goto out; + } + + /* log event via trace */ + err_seq++; + gdata = (struct acpi_hest_generic_data *)(tmp + 1); + if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) + fru_id = (uuid_le *)gdata->fru_id; + if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) + fru_text = gdata->fru_text; + sec_type = (uuid_le *)gdata->section_type; + if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { + struct cper_sec_mem_err *mem = (void *)(gdata + 1); + if (gdata->error_data_length >= sizeof(*mem)) + trace_extlog_mem_event(mem, err_seq, fru_id, fru_text, + (u8)gdata->error_severity); + } + +out: + return NOTIFY_STOP; +} + +static bool __init extlog_get_l1addr(void) +{ + u8 uuid[16]; + acpi_handle handle; + union acpi_object *obj; + + acpi_str_to_uuid(extlog_dsm_uuid, uuid); + + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) + return false; + if (!acpi_check_dsm(handle, uuid, EXTLOG_DSM_REV, 1 << EXTLOG_FN_ADDR)) + return false; + obj = acpi_evaluate_dsm_typed(handle, uuid, EXTLOG_DSM_REV, + EXTLOG_FN_ADDR, NULL, ACPI_TYPE_INTEGER); + if (!obj) { + return false; + } else { + l1_dirbase = obj->integer.value; + ACPI_FREE(obj); + } + + /* Spec says L1 directory must be 4K aligned, bail out if it isn't */ + if (l1_dirbase & ((1 << 12) - 1)) { + pr_warn(FW_BUG "L1 Directory is invalid at physical %llx\n", + l1_dirbase); + return false; + } + + return true; +} +static struct notifier_block extlog_mce_dec = { + .notifier_call = extlog_print, +}; + +static int __init extlog_init(void) +{ + struct extlog_l1_head *l1_head; + void __iomem *extlog_l1_hdr; + size_t l1_hdr_size; + struct resource *r; + u64 cap; + int rc; + + rdmsrl(MSR_IA32_MCG_CAP, cap); + + if (!(cap & MCG_ELOG_P) || !extlog_get_l1addr()) + return -ENODEV; + + if (get_edac_report_status() == EDAC_REPORTING_FORCE) { + pr_warn("Not loading eMCA, error reporting force-enabled through EDAC.\n"); + return -EPERM; + } + + rc = -EINVAL; + /* get L1 header to fetch necessary information */ + l1_hdr_size = sizeof(struct extlog_l1_head); + r = request_mem_region(l1_dirbase, l1_hdr_size, "L1 DIR HDR"); + if (!r) { + pr_warn(FW_BUG EMCA_BUG, + (unsigned long long)l1_dirbase, + (unsigned long long)l1_dirbase + l1_hdr_size); + goto err; + } + + extlog_l1_hdr = acpi_os_map_iomem(l1_dirbase, l1_hdr_size); + l1_head = (struct extlog_l1_head *)extlog_l1_hdr; + l1_size = l1_head->total_len; + l1_percpu_entry = l1_head->entries; + elog_base = l1_head->elog_base; + elog_size = l1_head->elog_len; + acpi_os_unmap_iomem(extlog_l1_hdr, l1_hdr_size); + release_mem_region(l1_dirbase, l1_hdr_size); + + /* remap L1 header again based on completed information */ + r = request_mem_region(l1_dirbase, l1_size, "L1 Table"); + if (!r) { + pr_warn(FW_BUG EMCA_BUG, + (unsigned long long)l1_dirbase, + (unsigned long long)l1_dirbase + l1_size); + goto err; + } + extlog_l1_addr = acpi_os_map_iomem(l1_dirbase, l1_size); + l1_entry_base = (u64 *)((u8 *)extlog_l1_addr + l1_hdr_size); + + /* remap elog table */ + r = request_mem_region(elog_base, elog_size, "Elog Table"); + if (!r) { + pr_warn(FW_BUG EMCA_BUG, + (unsigned long long)elog_base, + (unsigned long long)elog_base + elog_size); + goto err_release_l1_dir; + } + elog_addr = acpi_os_map_iomem(elog_base, elog_size); + + rc = -ENOMEM; + /* allocate buffer to save elog record */ + elog_buf = kmalloc(ELOG_ENTRY_LEN, GFP_KERNEL); + if (elog_buf == NULL) + goto err_release_elog; + + /* + * eMCA event report method has higher priority than EDAC method, + * unless EDAC event report method is mandatory. + */ + old_edac_report_status = get_edac_report_status(); + set_edac_report_status(EDAC_REPORTING_DISABLED); + mce_register_decode_chain(&extlog_mce_dec); + /* enable OS to be involved to take over management from BIOS */ + ((struct extlog_l1_head *)extlog_l1_addr)->flags |= FLAG_OS_OPTIN; + + return 0; + +err_release_elog: + if (elog_addr) + acpi_os_unmap_iomem(elog_addr, elog_size); + release_mem_region(elog_base, elog_size); +err_release_l1_dir: + if (extlog_l1_addr) + acpi_os_unmap_iomem(extlog_l1_addr, l1_size); + release_mem_region(l1_dirbase, l1_size); +err: + pr_warn(FW_BUG "Extended error log disabled because of problems parsing f/w tables\n"); + return rc; +} + +static void __exit extlog_exit(void) +{ + set_edac_report_status(old_edac_report_status); + mce_unregister_decode_chain(&extlog_mce_dec); + ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN; + if (extlog_l1_addr) + acpi_os_unmap_iomem(extlog_l1_addr, l1_size); + if (elog_addr) + acpi_os_unmap_iomem(elog_addr, elog_size); + release_mem_region(elog_base, elog_size); + release_mem_region(l1_dirbase, l1_size); + kfree(elog_buf); +} + +module_init(extlog_init); +module_exit(extlog_exit); + +MODULE_AUTHOR("Chen, Gong "); +MODULE_DESCRIPTION("Extended MCA Error Log Driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/acpi_ipmi.c b/kernel/drivers/acpi/acpi_ipmi.c new file mode 100644 index 000000000..ac0f52f6d --- /dev/null +++ b/kernel/drivers/acpi/acpi_ipmi.c @@ -0,0 +1,661 @@ +/* + * acpi_ipmi.c - ACPI IPMI opregion + * + * Copyright (C) 2010, 2013 Intel Corporation + * Author: Zhao Yakui + * Lv Zheng + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include + +MODULE_AUTHOR("Zhao Yakui"); +MODULE_DESCRIPTION("ACPI IPMI Opregion driver"); +MODULE_LICENSE("GPL"); + +#define ACPI_IPMI_OK 0 +#define ACPI_IPMI_TIMEOUT 0x10 +#define ACPI_IPMI_UNKNOWN 0x07 +/* the IPMI timeout is 5s */ +#define IPMI_TIMEOUT (5000) +#define ACPI_IPMI_MAX_MSG_LENGTH 64 + +struct acpi_ipmi_device { + /* the device list attached to driver_data.ipmi_devices */ + struct list_head head; + + /* the IPMI request message list */ + struct list_head tx_msg_list; + + spinlock_t tx_msg_lock; + acpi_handle handle; + struct device *dev; + ipmi_user_t user_interface; + int ipmi_ifnum; /* IPMI interface number */ + long curr_msgid; + bool dead; + struct kref kref; +}; + +struct ipmi_driver_data { + struct list_head ipmi_devices; + struct ipmi_smi_watcher bmc_events; + struct ipmi_user_hndl ipmi_hndlrs; + struct mutex ipmi_lock; + + /* + * NOTE: IPMI System Interface Selection + * There is no system interface specified by the IPMI operation + * region access. We try to select one system interface with ACPI + * handle set. IPMI messages passed from the ACPI codes are sent + * to this selected global IPMI system interface. + */ + struct acpi_ipmi_device *selected_smi; +}; + +struct acpi_ipmi_msg { + struct list_head head; + + /* + * General speaking the addr type should be SI_ADDR_TYPE. And + * the addr channel should be BMC. + * In fact it can also be IPMB type. But we will have to + * parse it from the Netfn command buffer. It is so complex + * that it is skipped. + */ + struct ipmi_addr addr; + long tx_msgid; + + /* it is used to track whether the IPMI message is finished */ + struct completion tx_complete; + + struct kernel_ipmi_msg tx_message; + int msg_done; + + /* tx/rx data . And copy it from/to ACPI object buffer */ + u8 data[ACPI_IPMI_MAX_MSG_LENGTH]; + u8 rx_len; + + struct acpi_ipmi_device *device; + struct kref kref; +}; + +/* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */ +struct acpi_ipmi_buffer { + u8 status; + u8 length; + u8 data[ACPI_IPMI_MAX_MSG_LENGTH]; +}; + +static void ipmi_register_bmc(int iface, struct device *dev); +static void ipmi_bmc_gone(int iface); +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); + +static struct ipmi_driver_data driver_data = { + .ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices), + .bmc_events = { + .owner = THIS_MODULE, + .new_smi = ipmi_register_bmc, + .smi_gone = ipmi_bmc_gone, + }, + .ipmi_hndlrs = { + .ipmi_recv_hndl = ipmi_msg_handler, + }, + .ipmi_lock = __MUTEX_INITIALIZER(driver_data.ipmi_lock) +}; + +static struct acpi_ipmi_device * +ipmi_dev_alloc(int iface, struct device *dev, acpi_handle handle) +{ + struct acpi_ipmi_device *ipmi_device; + int err; + ipmi_user_t user; + + ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL); + if (!ipmi_device) + return NULL; + + kref_init(&ipmi_device->kref); + INIT_LIST_HEAD(&ipmi_device->head); + INIT_LIST_HEAD(&ipmi_device->tx_msg_list); + spin_lock_init(&ipmi_device->tx_msg_lock); + ipmi_device->handle = handle; + ipmi_device->dev = get_device(dev); + ipmi_device->ipmi_ifnum = iface; + + err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs, + ipmi_device, &user); + if (err) { + put_device(dev); + kfree(ipmi_device); + return NULL; + } + ipmi_device->user_interface = user; + + return ipmi_device; +} + +static void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device) +{ + ipmi_destroy_user(ipmi_device->user_interface); + put_device(ipmi_device->dev); + kfree(ipmi_device); +} + +static void ipmi_dev_release_kref(struct kref *kref) +{ + struct acpi_ipmi_device *ipmi = + container_of(kref, struct acpi_ipmi_device, kref); + + ipmi_dev_release(ipmi); +} + +static void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device) +{ + list_del(&ipmi_device->head); + if (driver_data.selected_smi == ipmi_device) + driver_data.selected_smi = NULL; + + /* + * Always setting dead flag after deleting from the list or + * list_for_each_entry() codes must get changed. + */ + ipmi_device->dead = true; +} + +static struct acpi_ipmi_device *acpi_ipmi_dev_get(void) +{ + struct acpi_ipmi_device *ipmi_device = NULL; + + mutex_lock(&driver_data.ipmi_lock); + if (driver_data.selected_smi) { + ipmi_device = driver_data.selected_smi; + kref_get(&ipmi_device->kref); + } + mutex_unlock(&driver_data.ipmi_lock); + + return ipmi_device; +} + +static void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device) +{ + kref_put(&ipmi_device->kref, ipmi_dev_release_kref); +} + +static struct acpi_ipmi_msg *ipmi_msg_alloc(void) +{ + struct acpi_ipmi_device *ipmi; + struct acpi_ipmi_msg *ipmi_msg; + + ipmi = acpi_ipmi_dev_get(); + if (!ipmi) + return NULL; + + ipmi_msg = kzalloc(sizeof(struct acpi_ipmi_msg), GFP_KERNEL); + if (!ipmi_msg) { + acpi_ipmi_dev_put(ipmi); + return NULL; + } + + kref_init(&ipmi_msg->kref); + init_completion(&ipmi_msg->tx_complete); + INIT_LIST_HEAD(&ipmi_msg->head); + ipmi_msg->device = ipmi; + ipmi_msg->msg_done = ACPI_IPMI_UNKNOWN; + + return ipmi_msg; +} + +static void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg) +{ + acpi_ipmi_dev_put(tx_msg->device); + kfree(tx_msg); +} + +static void ipmi_msg_release_kref(struct kref *kref) +{ + struct acpi_ipmi_msg *tx_msg = + container_of(kref, struct acpi_ipmi_msg, kref); + + ipmi_msg_release(tx_msg); +} + +static struct acpi_ipmi_msg *acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg) +{ + kref_get(&tx_msg->kref); + + return tx_msg; +} + +static void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg) +{ + kref_put(&tx_msg->kref, ipmi_msg_release_kref); +} + +#define IPMI_OP_RGN_NETFN(offset) ((offset >> 8) & 0xff) +#define IPMI_OP_RGN_CMD(offset) (offset & 0xff) +static int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg, + acpi_physical_address address, + acpi_integer *value) +{ + struct kernel_ipmi_msg *msg; + struct acpi_ipmi_buffer *buffer; + struct acpi_ipmi_device *device; + unsigned long flags; + + msg = &tx_msg->tx_message; + + /* + * IPMI network function and command are encoded in the address + * within the IPMI OpRegion; see ACPI 4.0, sec 5.5.2.4.3. + */ + msg->netfn = IPMI_OP_RGN_NETFN(address); + msg->cmd = IPMI_OP_RGN_CMD(address); + msg->data = tx_msg->data; + + /* + * value is the parameter passed by the IPMI opregion space handler. + * It points to the IPMI request message buffer + */ + buffer = (struct acpi_ipmi_buffer *)value; + + /* copy the tx message data */ + if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) { + dev_WARN_ONCE(tx_msg->device->dev, true, + "Unexpected request (msg len %d).\n", + buffer->length); + return -EINVAL; + } + msg->data_len = buffer->length; + memcpy(tx_msg->data, buffer->data, msg->data_len); + + /* + * now the default type is SYSTEM_INTERFACE and channel type is BMC. + * If the netfn is APP_REQUEST and the cmd is SEND_MESSAGE, + * the addr type should be changed to IPMB. Then we will have to parse + * the IPMI request message buffer to get the IPMB address. + * If so, please fix me. + */ + tx_msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + tx_msg->addr.channel = IPMI_BMC_CHANNEL; + tx_msg->addr.data[0] = 0; + + /* Get the msgid */ + device = tx_msg->device; + + spin_lock_irqsave(&device->tx_msg_lock, flags); + device->curr_msgid++; + tx_msg->tx_msgid = device->curr_msgid; + spin_unlock_irqrestore(&device->tx_msg_lock, flags); + + return 0; +} + +static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, + acpi_integer *value) +{ + struct acpi_ipmi_buffer *buffer; + + /* + * value is also used as output parameter. It represents the response + * IPMI message returned by IPMI command. + */ + buffer = (struct acpi_ipmi_buffer *)value; + + /* + * If the flag of msg_done is not set, it means that the IPMI command is + * not executed correctly. + */ + buffer->status = msg->msg_done; + if (msg->msg_done != ACPI_IPMI_OK) + return; + + /* + * If the IPMI response message is obtained correctly, the status code + * will be ACPI_IPMI_OK + */ + buffer->length = msg->rx_len; + memcpy(buffer->data, msg->data, msg->rx_len); +} + +static void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) +{ + struct acpi_ipmi_msg *tx_msg; + unsigned long flags; + + /* + * NOTE: On-going ipmi_recv_msg + * ipmi_msg_handler() may still be invoked by ipmi_si after + * flushing. But it is safe to do a fast flushing on module_exit() + * without waiting for all ipmi_recv_msg(s) to complete from + * ipmi_msg_handler() as it is ensured by ipmi_si that all + * ipmi_recv_msg(s) are freed after invoking ipmi_destroy_user(). + */ + spin_lock_irqsave(&ipmi->tx_msg_lock, flags); + while (!list_empty(&ipmi->tx_msg_list)) { + tx_msg = list_first_entry(&ipmi->tx_msg_list, + struct acpi_ipmi_msg, + head); + list_del(&tx_msg->head); + spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags); + + /* wake up the sleep thread on the Tx msg */ + complete(&tx_msg->tx_complete); + acpi_ipmi_msg_put(tx_msg); + spin_lock_irqsave(&ipmi->tx_msg_lock, flags); + } + spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags); +} + +static void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi, + struct acpi_ipmi_msg *msg) +{ + struct acpi_ipmi_msg *tx_msg, *temp; + bool msg_found = false; + unsigned long flags; + + spin_lock_irqsave(&ipmi->tx_msg_lock, flags); + list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) { + if (msg == tx_msg) { + msg_found = true; + list_del(&tx_msg->head); + break; + } + } + spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags); + + if (msg_found) + acpi_ipmi_msg_put(tx_msg); +} + +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) +{ + struct acpi_ipmi_device *ipmi_device = user_msg_data; + bool msg_found = false; + struct acpi_ipmi_msg *tx_msg, *temp; + struct device *dev = ipmi_device->dev; + unsigned long flags; + + if (msg->user != ipmi_device->user_interface) { + dev_warn(dev, + "Unexpected response is returned. returned user %p, expected user %p\n", + msg->user, ipmi_device->user_interface); + goto out_msg; + } + + spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); + list_for_each_entry_safe(tx_msg, temp, &ipmi_device->tx_msg_list, head) { + if (msg->msgid == tx_msg->tx_msgid) { + msg_found = true; + list_del(&tx_msg->head); + break; + } + } + spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); + + if (!msg_found) { + dev_warn(dev, + "Unexpected response (msg id %ld) is returned.\n", + msg->msgid); + goto out_msg; + } + + /* copy the response data to Rx_data buffer */ + if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) { + dev_WARN_ONCE(dev, true, + "Unexpected response (msg len %d).\n", + msg->msg.data_len); + goto out_comp; + } + + /* response msg is an error msg */ + msg->recv_type = IPMI_RESPONSE_RECV_TYPE; + if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE && + msg->msg.data_len == 1) { + if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) { + dev_WARN_ONCE(dev, true, + "Unexpected response (timeout).\n"); + tx_msg->msg_done = ACPI_IPMI_TIMEOUT; + } + goto out_comp; + } + + tx_msg->rx_len = msg->msg.data_len; + memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len); + tx_msg->msg_done = ACPI_IPMI_OK; + +out_comp: + complete(&tx_msg->tx_complete); + acpi_ipmi_msg_put(tx_msg); +out_msg: + ipmi_free_recv_msg(msg); +} + +static void ipmi_register_bmc(int iface, struct device *dev) +{ + struct acpi_ipmi_device *ipmi_device, *temp; + int err; + struct ipmi_smi_info smi_data; + acpi_handle handle; + + err = ipmi_get_smi_info(iface, &smi_data); + if (err) + return; + + if (smi_data.addr_src != SI_ACPI) + goto err_ref; + handle = smi_data.addr_info.acpi_info.acpi_handle; + if (!handle) + goto err_ref; + + ipmi_device = ipmi_dev_alloc(iface, smi_data.dev, handle); + if (!ipmi_device) { + dev_warn(smi_data.dev, "Can't create IPMI user interface\n"); + goto err_ref; + } + + mutex_lock(&driver_data.ipmi_lock); + list_for_each_entry(temp, &driver_data.ipmi_devices, head) { + /* + * if the corresponding ACPI handle is already added + * to the device list, don't add it again. + */ + if (temp->handle == handle) + goto err_lock; + } + if (!driver_data.selected_smi) + driver_data.selected_smi = ipmi_device; + list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices); + mutex_unlock(&driver_data.ipmi_lock); + + put_device(smi_data.dev); + return; + +err_lock: + mutex_unlock(&driver_data.ipmi_lock); + ipmi_dev_release(ipmi_device); +err_ref: + put_device(smi_data.dev); + return; +} + +static void ipmi_bmc_gone(int iface) +{ + struct acpi_ipmi_device *ipmi_device, *temp; + bool dev_found = false; + + mutex_lock(&driver_data.ipmi_lock); + list_for_each_entry_safe(ipmi_device, temp, + &driver_data.ipmi_devices, head) { + if (ipmi_device->ipmi_ifnum != iface) { + dev_found = true; + __ipmi_dev_kill(ipmi_device); + break; + } + } + if (!driver_data.selected_smi) + driver_data.selected_smi = list_first_entry_or_null( + &driver_data.ipmi_devices, + struct acpi_ipmi_device, head); + mutex_unlock(&driver_data.ipmi_lock); + + if (dev_found) { + ipmi_flush_tx_msg(ipmi_device); + acpi_ipmi_dev_put(ipmi_device); + } +} + +/* + * This is the IPMI opregion space handler. + * @function: indicates the read/write. In fact as the IPMI message is driven + * by command, only write is meaningful. + * @address: This contains the netfn/command of IPMI request message. + * @bits : not used. + * @value : it is an in/out parameter. It points to the IPMI message buffer. + * Before the IPMI message is sent, it represents the actual request + * IPMI message. After the IPMI message is finished, it represents + * the response IPMI message returned by IPMI command. + * @handler_context: IPMI device context. + */ +static acpi_status +acpi_ipmi_space_handler(u32 function, acpi_physical_address address, + u32 bits, acpi_integer *value, + void *handler_context, void *region_context) +{ + struct acpi_ipmi_msg *tx_msg; + struct acpi_ipmi_device *ipmi_device; + int err; + acpi_status status; + unsigned long flags; + + /* + * IPMI opregion message. + * IPMI message is firstly written to the BMC and system software + * can get the respsonse. So it is unmeaningful for the read access + * of IPMI opregion. + */ + if ((function & ACPI_IO_MASK) == ACPI_READ) + return AE_TYPE; + + tx_msg = ipmi_msg_alloc(); + if (!tx_msg) + return AE_NOT_EXIST; + ipmi_device = tx_msg->device; + + if (acpi_format_ipmi_request(tx_msg, address, value) != 0) { + ipmi_msg_release(tx_msg); + return AE_TYPE; + } + + acpi_ipmi_msg_get(tx_msg); + mutex_lock(&driver_data.ipmi_lock); + /* Do not add a tx_msg that can not be flushed. */ + if (ipmi_device->dead) { + mutex_unlock(&driver_data.ipmi_lock); + ipmi_msg_release(tx_msg); + return AE_NOT_EXIST; + } + spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); + list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); + spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); + mutex_unlock(&driver_data.ipmi_lock); + + err = ipmi_request_settime(ipmi_device->user_interface, + &tx_msg->addr, + tx_msg->tx_msgid, + &tx_msg->tx_message, + NULL, 0, 0, IPMI_TIMEOUT); + if (err) { + status = AE_ERROR; + goto out_msg; + } + wait_for_completion(&tx_msg->tx_complete); + + acpi_format_ipmi_response(tx_msg, value); + status = AE_OK; + +out_msg: + ipmi_cancel_tx_msg(ipmi_device, tx_msg); + acpi_ipmi_msg_put(tx_msg); + return status; +} + +static int __init acpi_ipmi_init(void) +{ + int result; + acpi_status status; + + if (acpi_disabled) + return 0; + + status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_IPMI, + &acpi_ipmi_space_handler, + NULL, NULL); + if (ACPI_FAILURE(status)) { + pr_warn("Can't register IPMI opregion space handle\n"); + return -EINVAL; + } + result = ipmi_smi_watcher_register(&driver_data.bmc_events); + if (result) + pr_err("Can't register IPMI system interface watcher\n"); + + return result; +} + +static void __exit acpi_ipmi_exit(void) +{ + struct acpi_ipmi_device *ipmi_device; + + if (acpi_disabled) + return; + + ipmi_smi_watcher_unregister(&driver_data.bmc_events); + + /* + * When one smi_watcher is unregistered, it is only deleted + * from the smi_watcher list. But the smi_gone callback function + * is not called. So explicitly uninstall the ACPI IPMI oregion + * handler and free it. + */ + mutex_lock(&driver_data.ipmi_lock); + while (!list_empty(&driver_data.ipmi_devices)) { + ipmi_device = list_first_entry(&driver_data.ipmi_devices, + struct acpi_ipmi_device, + head); + __ipmi_dev_kill(ipmi_device); + mutex_unlock(&driver_data.ipmi_lock); + + ipmi_flush_tx_msg(ipmi_device); + acpi_ipmi_dev_put(ipmi_device); + + mutex_lock(&driver_data.ipmi_lock); + } + mutex_unlock(&driver_data.ipmi_lock); + acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_IPMI, + &acpi_ipmi_space_handler); +} + +module_init(acpi_ipmi_init); +module_exit(acpi_ipmi_exit); diff --git a/kernel/drivers/acpi/acpi_lpat.c b/kernel/drivers/acpi/acpi_lpat.c new file mode 100644 index 000000000..feb61c163 --- /dev/null +++ b/kernel/drivers/acpi/acpi_lpat.c @@ -0,0 +1,161 @@ +/* + * acpi_lpat.c - LPAT table processing functions + * + * Copyright (C) 2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +/** + * acpi_lpat_raw_to_temp(): Return temperature from raw value through + * LPAT conversion table + * + * @lpat_table: the temperature_raw mapping table structure + * @raw: the raw value, used as a key to get the temerature from the + * above mapping table + * + * A positive converted temperarure value will be returned on success, + * a negative errno will be returned in error cases. + */ +int acpi_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table, + int raw) +{ + int i, delta_temp, delta_raw, temp; + struct acpi_lpat *lpat = lpat_table->lpat; + + for (i = 0; i < lpat_table->lpat_count - 1; i++) { + if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) || + (raw <= lpat[i].raw && raw >= lpat[i+1].raw)) + break; + } + + if (i == lpat_table->lpat_count - 1) + return -ENOENT; + + delta_temp = lpat[i+1].temp - lpat[i].temp; + delta_raw = lpat[i+1].raw - lpat[i].raw; + temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw; + + return temp; +} +EXPORT_SYMBOL_GPL(acpi_lpat_raw_to_temp); + +/** + * acpi_lpat_temp_to_raw(): Return raw value from temperature through + * LPAT conversion table + * + * @lpat: the temperature_raw mapping table + * @temp: the temperature, used as a key to get the raw value from the + * above mapping table + * + * A positive converted temperature value will be returned on success, + * a negative errno will be returned in error cases. + */ +int acpi_lpat_temp_to_raw(struct acpi_lpat_conversion_table *lpat_table, + int temp) +{ + int i, delta_temp, delta_raw, raw; + struct acpi_lpat *lpat = lpat_table->lpat; + + for (i = 0; i < lpat_table->lpat_count - 1; i++) { + if (temp >= lpat[i].temp && temp <= lpat[i+1].temp) + break; + } + + if (i == lpat_table->lpat_count - 1) + return -ENOENT; + + delta_temp = lpat[i+1].temp - lpat[i].temp; + delta_raw = lpat[i+1].raw - lpat[i].raw; + raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp; + + return raw; +} +EXPORT_SYMBOL_GPL(acpi_lpat_temp_to_raw); + +/** + * acpi_lpat_get_conversion_table(): Parse ACPI LPAT table if present. + * + * @handle: Handle to acpi device + * + * Parse LPAT table to a struct of type acpi_lpat_table. On success + * it returns a pointer to newly allocated table. This table must + * be freed by the caller when finished processing, using a call to + * acpi_lpat_free_conversion_table. + */ +struct acpi_lpat_conversion_table *acpi_lpat_get_conversion_table(acpi_handle + handle) +{ + struct acpi_lpat_conversion_table *lpat_table = NULL; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj_p, *obj_e; + int *lpat, i; + acpi_status status; + + status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer); + if (ACPI_FAILURE(status)) + return NULL; + + obj_p = (union acpi_object *)buffer.pointer; + if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) || + (obj_p->package.count % 2) || (obj_p->package.count < 4)) + goto out; + + lpat = kcalloc(obj_p->package.count, sizeof(int), GFP_KERNEL); + if (!lpat) + goto out; + + for (i = 0; i < obj_p->package.count; i++) { + obj_e = &obj_p->package.elements[i]; + if (obj_e->type != ACPI_TYPE_INTEGER) { + kfree(lpat); + goto out; + } + lpat[i] = (s64)obj_e->integer.value; + } + + lpat_table = kzalloc(sizeof(*lpat_table), GFP_KERNEL); + if (!lpat_table) { + kfree(lpat); + goto out; + } + + lpat_table->lpat = (struct acpi_lpat *)lpat; + lpat_table->lpat_count = obj_p->package.count / 2; + +out: + kfree(buffer.pointer); + return lpat_table; +} +EXPORT_SYMBOL_GPL(acpi_lpat_get_conversion_table); + +/** + * acpi_lpat_free_conversion_table(): Free LPAT table. + * + * @lpat_table: the temperature_raw mapping table structure + * + * Frees the LPAT table previously allocated by a call to + * acpi_lpat_get_conversion_table. + */ +void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table + *lpat_table) +{ + if (lpat_table) { + kfree(lpat_table->lpat); + kfree(lpat_table); + } +} +EXPORT_SYMBOL_GPL(acpi_lpat_free_conversion_table); + +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/acpi_lpss.c b/kernel/drivers/acpi/acpi_lpss.c new file mode 100644 index 000000000..37fb19047 --- /dev/null +++ b/kernel/drivers/acpi/acpi_lpss.c @@ -0,0 +1,746 @@ +/* + * ACPI support for Intel Lynxpoint LPSS. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg + * Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +ACPI_MODULE_NAME("acpi_lpss"); + +#ifdef CONFIG_X86_INTEL_LPSS + +#define LPSS_ADDR(desc) ((unsigned long)&desc) + +#define LPSS_CLK_SIZE 0x04 +#define LPSS_LTR_SIZE 0x18 + +/* Offsets relative to LPSS_PRIVATE_OFFSET */ +#define LPSS_CLK_DIVIDER_DEF_MASK (BIT(1) | BIT(16)) +#define LPSS_RESETS 0x04 +#define LPSS_RESETS_RESET_FUNC BIT(0) +#define LPSS_RESETS_RESET_APB BIT(1) +#define LPSS_GENERAL 0x08 +#define LPSS_GENERAL_LTR_MODE_SW BIT(2) +#define LPSS_GENERAL_UART_RTS_OVRD BIT(3) +#define LPSS_SW_LTR 0x10 +#define LPSS_AUTO_LTR 0x14 +#define LPSS_LTR_SNOOP_REQ BIT(15) +#define LPSS_LTR_SNOOP_MASK 0x0000FFFF +#define LPSS_LTR_SNOOP_LAT_1US 0x800 +#define LPSS_LTR_SNOOP_LAT_32US 0xC00 +#define LPSS_LTR_SNOOP_LAT_SHIFT 5 +#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000 +#define LPSS_LTR_MAX_VAL 0x3FF +#define LPSS_TX_INT 0x20 +#define LPSS_TX_INT_MASK BIT(1) + +#define LPSS_PRV_REG_COUNT 9 + +/* LPSS Flags */ +#define LPSS_CLK BIT(0) +#define LPSS_CLK_GATE BIT(1) +#define LPSS_CLK_DIVIDER BIT(2) +#define LPSS_LTR BIT(3) +#define LPSS_SAVE_CTX BIT(4) + +struct lpss_private_data; + +struct lpss_device_desc { + unsigned int flags; + const char *clk_con_id; + unsigned int prv_offset; + size_t prv_size_override; + void (*setup)(struct lpss_private_data *pdata); +}; + +static struct lpss_device_desc lpss_dma_desc = { + .flags = LPSS_CLK, +}; + +struct lpss_private_data { + void __iomem *mmio_base; + resource_size_t mmio_size; + unsigned int fixed_clk_rate; + struct clk *clk; + const struct lpss_device_desc *dev_desc; + u32 prv_reg_ctx[LPSS_PRV_REG_COUNT]; +}; + +/* UART Component Parameter Register */ +#define LPSS_UART_CPR 0xF4 +#define LPSS_UART_CPR_AFCE BIT(4) + +static void lpss_uart_setup(struct lpss_private_data *pdata) +{ + unsigned int offset; + u32 val; + + offset = pdata->dev_desc->prv_offset + LPSS_TX_INT; + val = readl(pdata->mmio_base + offset); + writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset); + + val = readl(pdata->mmio_base + LPSS_UART_CPR); + if (!(val & LPSS_UART_CPR_AFCE)) { + offset = pdata->dev_desc->prv_offset + LPSS_GENERAL; + val = readl(pdata->mmio_base + offset); + val |= LPSS_GENERAL_UART_RTS_OVRD; + writel(val, pdata->mmio_base + offset); + } +} + +static void lpss_deassert_reset(struct lpss_private_data *pdata) +{ + unsigned int offset; + u32 val; + + offset = pdata->dev_desc->prv_offset + LPSS_RESETS; + val = readl(pdata->mmio_base + offset); + val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC; + writel(val, pdata->mmio_base + offset); +} + +#define LPSS_I2C_ENABLE 0x6c + +static void byt_i2c_setup(struct lpss_private_data *pdata) +{ + lpss_deassert_reset(pdata); + + if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset)) + pdata->fixed_clk_rate = 133000000; + + writel(0, pdata->mmio_base + LPSS_I2C_ENABLE); +} + +static struct lpss_device_desc lpt_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, + .prv_offset = 0x800, +}; + +static struct lpss_device_desc lpt_i2c_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR, + .prv_offset = 0x800, +}; + +static struct lpss_device_desc lpt_uart_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, + .clk_con_id = "baudclk", + .prv_offset = 0x800, + .setup = lpss_uart_setup, +}; + +static struct lpss_device_desc lpt_sdio_dev_desc = { + .flags = LPSS_LTR, + .prv_offset = 0x1000, + .prv_size_override = 0x1018, +}; + +static struct lpss_device_desc byt_pwm_dev_desc = { + .flags = LPSS_SAVE_CTX, +}; + +static struct lpss_device_desc byt_uart_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .clk_con_id = "baudclk", + .prv_offset = 0x800, + .setup = lpss_uart_setup, +}; + +static struct lpss_device_desc byt_spi_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .prv_offset = 0x400, +}; + +static struct lpss_device_desc byt_sdio_dev_desc = { + .flags = LPSS_CLK, +}; + +static struct lpss_device_desc byt_i2c_dev_desc = { + .flags = LPSS_CLK | LPSS_SAVE_CTX, + .prv_offset = 0x800, + .setup = byt_i2c_setup, +}; + +static struct lpss_device_desc bsw_spi_dev_desc = { + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .prv_offset = 0x400, + .setup = lpss_deassert_reset, +}; + +#else + +#define LPSS_ADDR(desc) (0UL) + +#endif /* CONFIG_X86_INTEL_LPSS */ + +static const struct acpi_device_id acpi_lpss_device_ids[] = { + /* Generic LPSS devices */ + { "INTL9C60", LPSS_ADDR(lpss_dma_desc) }, + + /* Lynxpoint LPSS devices */ + { "INT33C0", LPSS_ADDR(lpt_dev_desc) }, + { "INT33C1", LPSS_ADDR(lpt_dev_desc) }, + { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) }, + { "INT33C7", }, + + /* BayTrail LPSS devices */ + { "80860F09", LPSS_ADDR(byt_pwm_dev_desc) }, + { "80860F0A", LPSS_ADDR(byt_uart_dev_desc) }, + { "80860F0E", LPSS_ADDR(byt_spi_dev_desc) }, + { "80860F14", LPSS_ADDR(byt_sdio_dev_desc) }, + { "80860F41", LPSS_ADDR(byt_i2c_dev_desc) }, + { "INT33B2", }, + { "INT33FC", }, + + /* Braswell LPSS devices */ + { "80862288", LPSS_ADDR(byt_pwm_dev_desc) }, + { "8086228A", LPSS_ADDR(byt_uart_dev_desc) }, + { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) }, + { "808622C1", LPSS_ADDR(byt_i2c_dev_desc) }, + + { "INT3430", LPSS_ADDR(lpt_dev_desc) }, + { "INT3431", LPSS_ADDR(lpt_dev_desc) }, + { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) }, + { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) }, + { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) }, + { "INT3437", }, + + /* Wildcat Point LPSS devices */ + { "INT3438", LPSS_ADDR(lpt_dev_desc) }, + + { } +}; + +#ifdef CONFIG_X86_INTEL_LPSS + +static int is_memory(struct acpi_resource *res, void *not_used) +{ + struct resource r; + return !acpi_dev_resource_memory(res, &r); +} + +/* LPSS main clock device. */ +static struct platform_device *lpss_clk_dev; + +static inline void lpt_register_clock_device(void) +{ + lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0); +} + +static int register_device_clock(struct acpi_device *adev, + struct lpss_private_data *pdata) +{ + const struct lpss_device_desc *dev_desc = pdata->dev_desc; + const char *devname = dev_name(&adev->dev); + struct clk *clk = ERR_PTR(-ENODEV); + struct lpss_clk_data *clk_data; + const char *parent, *clk_name; + void __iomem *prv_base; + + if (!lpss_clk_dev) + lpt_register_clock_device(); + + clk_data = platform_get_drvdata(lpss_clk_dev); + if (!clk_data) + return -ENODEV; + clk = clk_data->clk; + + if (!pdata->mmio_base + || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE) + return -ENODATA; + + parent = clk_data->name; + prv_base = pdata->mmio_base + dev_desc->prv_offset; + + if (pdata->fixed_clk_rate) { + clk = clk_register_fixed_rate(NULL, devname, parent, 0, + pdata->fixed_clk_rate); + goto out; + } + + if (dev_desc->flags & LPSS_CLK_GATE) { + clk = clk_register_gate(NULL, devname, parent, 0, + prv_base, 0, 0, NULL); + parent = devname; + } + + if (dev_desc->flags & LPSS_CLK_DIVIDER) { + /* Prevent division by zero */ + if (!readl(prv_base)) + writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base); + + clk_name = kasprintf(GFP_KERNEL, "%s-div", devname); + if (!clk_name) + return -ENOMEM; + clk = clk_register_fractional_divider(NULL, clk_name, parent, + 0, prv_base, + 1, 15, 16, 15, 0, NULL); + parent = clk_name; + + clk_name = kasprintf(GFP_KERNEL, "%s-update", devname); + if (!clk_name) { + kfree(parent); + return -ENOMEM; + } + clk = clk_register_gate(NULL, clk_name, parent, + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, + prv_base, 31, 0, NULL); + kfree(parent); + kfree(clk_name); + } +out: + if (IS_ERR(clk)) + return PTR_ERR(clk); + + pdata->clk = clk; + clk_register_clkdev(clk, dev_desc->clk_con_id, devname); + return 0; +} + +static int acpi_lpss_create_device(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + struct lpss_device_desc *dev_desc; + struct lpss_private_data *pdata; + struct resource_entry *rentry; + struct list_head resource_list; + struct platform_device *pdev; + int ret; + + dev_desc = (struct lpss_device_desc *)id->driver_data; + if (!dev_desc) { + pdev = acpi_create_platform_device(adev); + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; + } + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); + if (ret < 0) + goto err_out; + + list_for_each_entry(rentry, &resource_list, node) + if (resource_type(rentry->res) == IORESOURCE_MEM) { + if (dev_desc->prv_size_override) + pdata->mmio_size = dev_desc->prv_size_override; + else + pdata->mmio_size = resource_size(rentry->res); + pdata->mmio_base = ioremap(rentry->res->start, + pdata->mmio_size); + if (!pdata->mmio_base) + goto err_out; + break; + } + + acpi_dev_free_resource_list(&resource_list); + + pdata->dev_desc = dev_desc; + + if (dev_desc->setup) + dev_desc->setup(pdata); + + if (dev_desc->flags & LPSS_CLK) { + ret = register_device_clock(adev, pdata); + if (ret) { + /* Skip the device, but continue the namespace scan. */ + ret = 0; + goto err_out; + } + } + + /* + * This works around a known issue in ACPI tables where LPSS devices + * have _PS0 and _PS3 without _PSC (and no power resources), so + * acpi_bus_init_power() will assume that the BIOS has put them into D0. + */ + ret = acpi_device_fix_up_power(adev); + if (ret) { + /* Skip the device, but continue the namespace scan. */ + ret = 0; + goto err_out; + } + + adev->driver_data = pdata; + pdev = acpi_create_platform_device(adev); + if (!IS_ERR_OR_NULL(pdev)) { + return 1; + } + + ret = PTR_ERR(pdev); + adev->driver_data = NULL; + + err_out: + kfree(pdata); + return ret; +} + +static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg) +{ + return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); +} + +static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata, + unsigned int reg) +{ + writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg); +} + +static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) +{ + struct acpi_device *adev; + struct lpss_private_data *pdata; + unsigned long flags; + int ret; + + ret = acpi_bus_get_device(ACPI_HANDLE(dev), &adev); + if (WARN_ON(ret)) + return ret; + + spin_lock_irqsave(&dev->power.lock, flags); + if (pm_runtime_suspended(dev)) { + ret = -EAGAIN; + goto out; + } + pdata = acpi_driver_data(adev); + if (WARN_ON(!pdata || !pdata->mmio_base)) { + ret = -ENODEV; + goto out; + } + *val = __lpss_reg_read(pdata, reg); + + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + return ret; +} + +static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u32 ltr_value = 0; + unsigned int reg; + int ret; + + reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR; + ret = lpss_reg_read(dev, reg, <r_value); + if (ret) + return ret; + + return snprintf(buf, PAGE_SIZE, "%08x\n", ltr_value); +} + +static ssize_t lpss_ltr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 ltr_mode = 0; + char *outstr; + int ret; + + ret = lpss_reg_read(dev, LPSS_GENERAL, <r_mode); + if (ret) + return ret; + + outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto"; + return sprintf(buf, "%s\n", outstr); +} + +static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL); +static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL); + +static struct attribute *lpss_attrs[] = { + &dev_attr_auto_ltr.attr, + &dev_attr_sw_ltr.attr, + &dev_attr_ltr_mode.attr, + NULL, +}; + +static struct attribute_group lpss_attr_group = { + .attrs = lpss_attrs, + .name = "lpss_ltr", +}; + +static void acpi_lpss_set_ltr(struct device *dev, s32 val) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + u32 ltr_mode, ltr_val; + + ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL); + if (val < 0) { + if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) { + ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW; + __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); + } + return; + } + ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK; + if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) { + ltr_val |= LPSS_LTR_SNOOP_LAT_32US; + val = LPSS_LTR_MAX_VAL; + } else if (val > LPSS_LTR_MAX_VAL) { + ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ; + val >>= LPSS_LTR_SNOOP_LAT_SHIFT; + } else { + ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ; + } + ltr_val |= val; + __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR); + if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) { + ltr_mode |= LPSS_GENERAL_LTR_MODE_SW; + __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); + } +} + +#ifdef CONFIG_PM +/** + * acpi_lpss_save_ctx() - Save the private registers of LPSS device + * @dev: LPSS device + * @pdata: pointer to the private data of the LPSS device + * + * Most LPSS devices have private registers which may loose their context when + * the device is powered down. acpi_lpss_save_ctx() saves those registers into + * prv_reg_ctx array. + */ +static void acpi_lpss_save_ctx(struct device *dev, + struct lpss_private_data *pdata) +{ + unsigned int i; + + for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { + unsigned long offset = i * sizeof(u32); + + pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset); + dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n", + pdata->prv_reg_ctx[i], offset); + } +} + +/** + * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device + * @dev: LPSS device + * @pdata: pointer to the private data of the LPSS device + * + * Restores the registers that were previously stored with acpi_lpss_save_ctx(). + */ +static void acpi_lpss_restore_ctx(struct device *dev, + struct lpss_private_data *pdata) +{ + unsigned int i; + + /* + * The following delay is needed or the subsequent write operations may + * fail. The LPSS devices are actually PCI devices and the PCI spec + * expects 10ms delay before the device can be accessed after D3 to D0 + * transition. + */ + msleep(10); + + for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { + unsigned long offset = i * sizeof(u32); + + __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset); + dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n", + pdata->prv_reg_ctx[i], offset); + } +} + +#ifdef CONFIG_PM_SLEEP +static int acpi_lpss_suspend_late(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = pm_generic_suspend_late(dev); + if (ret) + return ret; + + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_save_ctx(dev, pdata); + + return acpi_dev_suspend_late(dev); +} + +static int acpi_lpss_resume_early(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = acpi_dev_resume_early(dev); + if (ret) + return ret; + + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_restore_ctx(dev, pdata); + + return pm_generic_resume_early(dev); +} +#endif /* CONFIG_PM_SLEEP */ + +static int acpi_lpss_runtime_suspend(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = pm_generic_runtime_suspend(dev); + if (ret) + return ret; + + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_save_ctx(dev, pdata); + + return acpi_dev_runtime_suspend(dev); +} + +static int acpi_lpss_runtime_resume(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = acpi_dev_runtime_resume(dev); + if (ret) + return ret; + + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_restore_ctx(dev, pdata); + + return pm_generic_runtime_resume(dev); +} +#endif /* CONFIG_PM */ + +static struct dev_pm_domain acpi_lpss_pm_domain = { + .ops = { +#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP + .prepare = acpi_subsys_prepare, + .complete = acpi_subsys_complete, + .suspend = acpi_subsys_suspend, + .suspend_late = acpi_lpss_suspend_late, + .resume_early = acpi_lpss_resume_early, + .freeze = acpi_subsys_freeze, + .poweroff = acpi_subsys_suspend, + .poweroff_late = acpi_lpss_suspend_late, + .restore_early = acpi_lpss_resume_early, +#endif + .runtime_suspend = acpi_lpss_runtime_suspend, + .runtime_resume = acpi_lpss_runtime_resume, +#endif + }, +}; + +static int acpi_lpss_platform_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct platform_device *pdev = to_platform_device(data); + struct lpss_private_data *pdata; + struct acpi_device *adev; + const struct acpi_device_id *id; + + id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev); + if (!id || !id->driver_data) + return 0; + + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) + return 0; + + pdata = acpi_driver_data(adev); + if (!pdata) + return 0; + + if (pdata->mmio_base && + pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { + dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n"); + return 0; + } + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + pdev->dev.pm_domain = &acpi_lpss_pm_domain; + if (pdata->dev_desc->flags & LPSS_LTR) + return sysfs_create_group(&pdev->dev.kobj, + &lpss_attr_group); + break; + case BUS_NOTIFY_DEL_DEVICE: + if (pdata->dev_desc->flags & LPSS_LTR) + sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group); + pdev->dev.pm_domain = NULL; + break; + default: + break; + } + + return 0; +} + +static struct notifier_block acpi_lpss_nb = { + .notifier_call = acpi_lpss_platform_notify, +}; + +static void acpi_lpss_bind(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR)) + return; + + if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) + dev->power.set_latency_tolerance = acpi_lpss_set_ltr; + else + dev_err(dev, "MMIO size insufficient to access LTR\n"); +} + +static void acpi_lpss_unbind(struct device *dev) +{ + dev->power.set_latency_tolerance = NULL; +} + +static struct acpi_scan_handler lpss_handler = { + .ids = acpi_lpss_device_ids, + .attach = acpi_lpss_create_device, + .bind = acpi_lpss_bind, + .unbind = acpi_lpss_unbind, +}; + +void __init acpi_lpss_init(void) +{ + if (!lpt_clk_init()) { + bus_register_notifier(&platform_bus_type, &acpi_lpss_nb); + acpi_scan_add_handler(&lpss_handler); + } +} + +#else + +static struct acpi_scan_handler lpss_handler = { + .ids = acpi_lpss_device_ids, +}; + +void __init acpi_lpss_init(void) +{ + acpi_scan_add_handler(&lpss_handler); +} + +#endif /* CONFIG_X86_INTEL_LPSS */ diff --git a/kernel/drivers/acpi/acpi_memhotplug.c b/kernel/drivers/acpi/acpi_memhotplug.c new file mode 100644 index 000000000..ee28f4d15 --- /dev/null +++ b/kernel/drivers/acpi/acpi_memhotplug.c @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2004, 2013 Intel Corporation + * Author: Naveen B S + * Author: Rafael J. Wysocki + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * ACPI based HotPlug driver that supports Memory Hotplug + * This driver fields notifications from firmware for memory add + * and remove operations and alerts the VM of the affected memory + * ranges. + */ + +#include +#include +#include + +#include "internal.h" + +#define ACPI_MEMORY_DEVICE_CLASS "memory" +#define ACPI_MEMORY_DEVICE_HID "PNP0C80" +#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device" + +#define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT + +#undef PREFIX +#define PREFIX "ACPI:memory_hp:" + +ACPI_MODULE_NAME("acpi_memhotplug"); + +static const struct acpi_device_id memory_device_ids[] = { + {ACPI_MEMORY_DEVICE_HID, 0}, + {"", 0}, +}; + +#ifdef CONFIG_ACPI_HOTPLUG_MEMORY + +/* Memory Device States */ +#define MEMORY_INVALID_STATE 0 +#define MEMORY_POWER_ON_STATE 1 +#define MEMORY_POWER_OFF_STATE 2 + +static int acpi_memory_device_add(struct acpi_device *device, + const struct acpi_device_id *not_used); +static void acpi_memory_device_remove(struct acpi_device *device); + +static struct acpi_scan_handler memory_device_handler = { + .ids = memory_device_ids, + .attach = acpi_memory_device_add, + .detach = acpi_memory_device_remove, + .hotplug = { + .enabled = true, + }, +}; + +struct acpi_memory_info { + struct list_head list; + u64 start_addr; /* Memory Range start physical addr */ + u64 length; /* Memory Range length */ + unsigned short caching; /* memory cache attribute */ + unsigned short write_protect; /* memory read/write attribute */ + unsigned int enabled:1; +}; + +struct acpi_memory_device { + struct acpi_device * device; + unsigned int state; /* State of the memory device */ + struct list_head res_list; +}; + +static acpi_status +acpi_memory_get_resource(struct acpi_resource *resource, void *context) +{ + struct acpi_memory_device *mem_device = context; + struct acpi_resource_address64 address64; + struct acpi_memory_info *info, *new; + acpi_status status; + + status = acpi_resource_to_address64(resource, &address64); + if (ACPI_FAILURE(status) || + (address64.resource_type != ACPI_MEMORY_RANGE)) + return AE_OK; + + list_for_each_entry(info, &mem_device->res_list, list) { + /* Can we combine the resource range information? */ + if ((info->caching == address64.info.mem.caching) && + (info->write_protect == address64.info.mem.write_protect) && + (info->start_addr + info->length == address64.address.minimum)) { + info->length += address64.address.address_length; + return AE_OK; + } + } + + new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL); + if (!new) + return AE_ERROR; + + INIT_LIST_HEAD(&new->list); + new->caching = address64.info.mem.caching; + new->write_protect = address64.info.mem.write_protect; + new->start_addr = address64.address.minimum; + new->length = address64.address.address_length; + list_add_tail(&new->list, &mem_device->res_list); + + return AE_OK; +} + +static void +acpi_memory_free_device_resources(struct acpi_memory_device *mem_device) +{ + struct acpi_memory_info *info, *n; + + list_for_each_entry_safe(info, n, &mem_device->res_list, list) + kfree(info); + INIT_LIST_HEAD(&mem_device->res_list); +} + +static int +acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) +{ + acpi_status status; + + if (!list_empty(&mem_device->res_list)) + return 0; + + status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS, + acpi_memory_get_resource, mem_device); + if (ACPI_FAILURE(status)) { + acpi_memory_free_device_resources(mem_device); + return -EINVAL; + } + + return 0; +} + +static int acpi_memory_check_device(struct acpi_memory_device *mem_device) +{ + unsigned long long current_status; + + /* Get device present/absent information from the _STA */ + if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, + METHOD_NAME__STA, NULL, + ¤t_status))) + return -ENODEV; + /* + * Check for device status. Device should be + * present/enabled/functioning. + */ + if (!((current_status & ACPI_STA_DEVICE_PRESENT) + && (current_status & ACPI_STA_DEVICE_ENABLED) + && (current_status & ACPI_STA_DEVICE_FUNCTIONING))) + return -ENODEV; + + return 0; +} + +static unsigned long acpi_meminfo_start_pfn(struct acpi_memory_info *info) +{ + return PFN_DOWN(info->start_addr); +} + +static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info) +{ + return PFN_UP(info->start_addr + info->length-1); +} + +static int acpi_bind_memblk(struct memory_block *mem, void *arg) +{ + return acpi_bind_one(&mem->dev, arg); +} + +static int acpi_bind_memory_blocks(struct acpi_memory_info *info, + struct acpi_device *adev) +{ + return walk_memory_range(acpi_meminfo_start_pfn(info), + acpi_meminfo_end_pfn(info), adev, + acpi_bind_memblk); +} + +static int acpi_unbind_memblk(struct memory_block *mem, void *arg) +{ + acpi_unbind_one(&mem->dev); + return 0; +} + +static void acpi_unbind_memory_blocks(struct acpi_memory_info *info) +{ + walk_memory_range(acpi_meminfo_start_pfn(info), + acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk); +} + +static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) +{ + acpi_handle handle = mem_device->device->handle; + int result, num_enabled = 0; + struct acpi_memory_info *info; + int node; + + node = acpi_get_node(handle); + /* + * Tell the VM there is more memory here... + * Note: Assume that this function returns zero on success + * We don't have memory-hot-add rollback function,now. + * (i.e. memory-hot-remove function) + */ + list_for_each_entry(info, &mem_device->res_list, list) { + if (info->enabled) { /* just sanity check...*/ + num_enabled++; + continue; + } + /* + * If the memory block size is zero, please ignore it. + * Don't try to do the following memory hotplug flowchart. + */ + if (!info->length) + continue; + if (node < 0) + node = memory_add_physaddr_to_nid(info->start_addr); + + result = add_memory(node, info->start_addr, info->length); + + /* + * If the memory block has been used by the kernel, add_memory() + * returns -EEXIST. If add_memory() returns the other error, it + * means that this memory block is not used by the kernel. + */ + if (result && result != -EEXIST) + continue; + + result = acpi_bind_memory_blocks(info, mem_device->device); + if (result) { + acpi_unbind_memory_blocks(info); + return -ENODEV; + } + + info->enabled = 1; + + /* + * Add num_enable even if add_memory() returns -EEXIST, so the + * device is bound to this driver. + */ + num_enabled++; + } + if (!num_enabled) { + dev_err(&mem_device->device->dev, "add_memory failed\n"); + mem_device->state = MEMORY_INVALID_STATE; + return -EINVAL; + } + /* + * Sometimes the memory device will contain several memory blocks. + * When one memory block is hot-added to the system memory, it will + * be regarded as a success. + * Otherwise if the last memory block can't be hot-added to the system + * memory, it will be failure and the memory device can't be bound with + * driver. + */ + return 0; +} + +static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device) +{ + acpi_handle handle = mem_device->device->handle; + struct acpi_memory_info *info, *n; + int nid = acpi_get_node(handle); + + list_for_each_entry_safe(info, n, &mem_device->res_list, list) { + if (!info->enabled) + continue; + + if (nid == NUMA_NO_NODE) + nid = memory_add_physaddr_to_nid(info->start_addr); + + acpi_unbind_memory_blocks(info); + remove_memory(nid, info->start_addr, info->length); + list_del(&info->list); + kfree(info); + } +} + +static void acpi_memory_device_free(struct acpi_memory_device *mem_device) +{ + if (!mem_device) + return; + + acpi_memory_free_device_resources(mem_device); + mem_device->device->driver_data = NULL; + kfree(mem_device); +} + +static int acpi_memory_device_add(struct acpi_device *device, + const struct acpi_device_id *not_used) +{ + struct acpi_memory_device *mem_device; + int result; + + if (!device) + return -EINVAL; + + mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL); + if (!mem_device) + return -ENOMEM; + + INIT_LIST_HEAD(&mem_device->res_list); + mem_device->device = device; + sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); + sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); + device->driver_data = mem_device; + + /* Get the range from the _CRS */ + result = acpi_memory_get_device_resources(mem_device); + if (result) { + device->driver_data = NULL; + kfree(mem_device); + return result; + } + + /* Set the device state */ + mem_device->state = MEMORY_POWER_ON_STATE; + + result = acpi_memory_check_device(mem_device); + if (result) { + acpi_memory_device_free(mem_device); + return 0; + } + + result = acpi_memory_enable_device(mem_device); + if (result) { + dev_err(&device->dev, "acpi_memory_enable_device() error\n"); + acpi_memory_device_free(mem_device); + return result; + } + + dev_dbg(&device->dev, "Memory device configured by ACPI\n"); + return 1; +} + +static void acpi_memory_device_remove(struct acpi_device *device) +{ + struct acpi_memory_device *mem_device; + + if (!device || !acpi_driver_data(device)) + return; + + mem_device = acpi_driver_data(device); + acpi_memory_remove_memory(mem_device); + acpi_memory_device_free(mem_device); +} + +static bool __initdata acpi_no_memhotplug; + +void __init acpi_memory_hotplug_init(void) +{ + if (acpi_no_memhotplug) { + memory_device_handler.attach = NULL; + acpi_scan_add_handler(&memory_device_handler); + return; + } + acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory"); +} + +static int __init disable_acpi_memory_hotplug(char *str) +{ + acpi_no_memhotplug = true; + return 1; +} +__setup("acpi_no_memhotplug", disable_acpi_memory_hotplug); + +#else + +static struct acpi_scan_handler memory_device_handler = { + .ids = memory_device_ids, +}; + +void __init acpi_memory_hotplug_init(void) +{ + acpi_scan_add_handler(&memory_device_handler); +} + +#endif /* CONFIG_ACPI_HOTPLUG_MEMORY */ diff --git a/kernel/drivers/acpi/acpi_pad.c b/kernel/drivers/acpi/acpi_pad.c new file mode 100644 index 000000000..6bc9cbc01 --- /dev/null +++ b/kernel/drivers/acpi/acpi_pad.c @@ -0,0 +1,502 @@ +/* + * acpi_pad.c ACPI Processor Aggregator Driver + * + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad" +#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" +#define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 +static DEFINE_MUTEX(isolated_cpus_lock); +static DEFINE_MUTEX(round_robin_lock); + +static unsigned long power_saving_mwait_eax; + +static unsigned char tsc_detected_unstable; +static unsigned char tsc_marked_unstable; + +static void power_saving_mwait_init(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned int highest_cstate = 0; + unsigned int highest_subcstate = 0; + int i; + + if (!boot_cpu_has(X86_FEATURE_MWAIT)) + return; + if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) + return; + + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) + return; + + edx >>= MWAIT_SUBSTATE_SIZE; + for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { + if (edx & MWAIT_SUBSTATE_MASK) { + highest_cstate = i; + highest_subcstate = edx & MWAIT_SUBSTATE_MASK; + } + } + power_saving_mwait_eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) | + (highest_subcstate - 1); + +#if defined(CONFIG_X86) + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + case X86_VENDOR_INTEL: + /* + * AMD Fam10h TSC will tick in all + * C/P/S0/S1 states when this bit is set. + */ + if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + tsc_detected_unstable = 1; + break; + default: + /* TSC could halt in idle */ + tsc_detected_unstable = 1; + } +#endif +} + +static unsigned long cpu_weight[NR_CPUS]; +static int tsk_in_cpu[NR_CPUS] = {[0 ... NR_CPUS-1] = -1}; +static DECLARE_BITMAP(pad_busy_cpus_bits, NR_CPUS); +static void round_robin_cpu(unsigned int tsk_index) +{ + struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits); + cpumask_var_t tmp; + int cpu; + unsigned long min_weight = -1; + unsigned long uninitialized_var(preferred_cpu); + + if (!alloc_cpumask_var(&tmp, GFP_KERNEL)) + return; + + mutex_lock(&round_robin_lock); + cpumask_clear(tmp); + for_each_cpu(cpu, pad_busy_cpus) + cpumask_or(tmp, tmp, topology_thread_cpumask(cpu)); + cpumask_andnot(tmp, cpu_online_mask, tmp); + /* avoid HT sibilings if possible */ + if (cpumask_empty(tmp)) + cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus); + if (cpumask_empty(tmp)) { + mutex_unlock(&round_robin_lock); + return; + } + for_each_cpu(cpu, tmp) { + if (cpu_weight[cpu] < min_weight) { + min_weight = cpu_weight[cpu]; + preferred_cpu = cpu; + } + } + + if (tsk_in_cpu[tsk_index] != -1) + cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus); + tsk_in_cpu[tsk_index] = preferred_cpu; + cpumask_set_cpu(preferred_cpu, pad_busy_cpus); + cpu_weight[preferred_cpu]++; + mutex_unlock(&round_robin_lock); + + set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu)); +} + +static void exit_round_robin(unsigned int tsk_index) +{ + struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits); + cpumask_clear_cpu(tsk_in_cpu[tsk_index], pad_busy_cpus); + tsk_in_cpu[tsk_index] = -1; +} + +static unsigned int idle_pct = 5; /* percentage */ +static unsigned int round_robin_time = 1; /* second */ +static int power_saving_thread(void *data) +{ + struct sched_param param = {.sched_priority = 1}; + int do_sleep; + unsigned int tsk_index = (unsigned long)data; + u64 last_jiffies = 0; + + sched_setscheduler(current, SCHED_RR, ¶m); + + while (!kthread_should_stop()) { + unsigned long expire_time; + + try_to_freeze(); + + /* round robin to cpus */ + expire_time = last_jiffies + round_robin_time * HZ; + if (time_before(expire_time, jiffies)) { + last_jiffies = jiffies; + round_robin_cpu(tsk_index); + } + + do_sleep = 0; + + expire_time = jiffies + HZ * (100 - idle_pct) / 100; + + while (!need_resched()) { + if (tsc_detected_unstable && !tsc_marked_unstable) { + /* TSC could halt in idle, so notify users */ + mark_tsc_unstable("TSC halts in idle"); + tsc_marked_unstable = 1; + } + local_irq_disable(); + tick_broadcast_enable(); + tick_broadcast_enter(); + stop_critical_timings(); + + mwait_idle_with_hints(power_saving_mwait_eax, 1); + + start_critical_timings(); + tick_broadcast_exit(); + local_irq_enable(); + + if (time_before(expire_time, jiffies)) { + do_sleep = 1; + break; + } + } + + /* + * current sched_rt has threshold for rt task running time. + * When a rt task uses 95% CPU time, the rt thread will be + * scheduled out for 5% CPU time to not starve other tasks. But + * the mechanism only works when all CPUs have RT task running, + * as if one CPU hasn't RT task, RT task from other CPUs will + * borrow CPU time from this CPU and cause RT task use > 95% + * CPU time. To make 'avoid starvation' work, takes a nap here. + */ + if (unlikely(do_sleep)) + schedule_timeout_killable(HZ * idle_pct / 100); + + /* If an external event has set the need_resched flag, then + * we need to deal with it, or this loop will continue to + * spin without calling __mwait(). + */ + if (unlikely(need_resched())) + schedule(); + } + + exit_round_robin(tsk_index); + return 0; +} + +static struct task_struct *ps_tsks[NR_CPUS]; +static unsigned int ps_tsk_num; +static int create_power_saving_task(void) +{ + int rc; + + ps_tsks[ps_tsk_num] = kthread_run(power_saving_thread, + (void *)(unsigned long)ps_tsk_num, + "acpi_pad/%d", ps_tsk_num); + + if (IS_ERR(ps_tsks[ps_tsk_num])) { + rc = PTR_ERR(ps_tsks[ps_tsk_num]); + ps_tsks[ps_tsk_num] = NULL; + } else { + rc = 0; + ps_tsk_num++; + } + + return rc; +} + +static void destroy_power_saving_task(void) +{ + if (ps_tsk_num > 0) { + ps_tsk_num--; + kthread_stop(ps_tsks[ps_tsk_num]); + ps_tsks[ps_tsk_num] = NULL; + } +} + +static void set_power_saving_task_num(unsigned int num) +{ + if (num > ps_tsk_num) { + while (ps_tsk_num < num) { + if (create_power_saving_task()) + return; + } + } else if (num < ps_tsk_num) { + while (ps_tsk_num > num) + destroy_power_saving_task(); + } +} + +static void acpi_pad_idle_cpus(unsigned int num_cpus) +{ + get_online_cpus(); + + num_cpus = min_t(unsigned int, num_cpus, num_online_cpus()); + set_power_saving_task_num(num_cpus); + + put_online_cpus(); +} + +static uint32_t acpi_pad_idle_cpus_num(void) +{ + return ps_tsk_num; +} + +static ssize_t acpi_pad_rrtime_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (kstrtoul(buf, 0, &num)) + return -EINVAL; + if (num < 1 || num >= 100) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + round_robin_time = num; + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_rrtime_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", round_robin_time); +} +static DEVICE_ATTR(rrtime, S_IRUGO|S_IWUSR, + acpi_pad_rrtime_show, + acpi_pad_rrtime_store); + +static ssize_t acpi_pad_idlepct_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (kstrtoul(buf, 0, &num)) + return -EINVAL; + if (num < 1 || num >= 100) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + idle_pct = num; + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_idlepct_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", idle_pct); +} +static DEVICE_ATTR(idlepct, S_IRUGO|S_IWUSR, + acpi_pad_idlepct_show, + acpi_pad_idlepct_store); + +static ssize_t acpi_pad_idlecpus_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long num; + if (kstrtoul(buf, 0, &num)) + return -EINVAL; + mutex_lock(&isolated_cpus_lock); + acpi_pad_idle_cpus(num); + mutex_unlock(&isolated_cpus_lock); + return count; +} + +static ssize_t acpi_pad_idlecpus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cpumap_print_to_pagebuf(false, buf, + to_cpumask(pad_busy_cpus_bits)); +} + +static DEVICE_ATTR(idlecpus, S_IRUGO|S_IWUSR, + acpi_pad_idlecpus_show, + acpi_pad_idlecpus_store); + +static int acpi_pad_add_sysfs(struct acpi_device *device) +{ + int result; + + result = device_create_file(&device->dev, &dev_attr_idlecpus); + if (result) + return -ENODEV; + result = device_create_file(&device->dev, &dev_attr_idlepct); + if (result) { + device_remove_file(&device->dev, &dev_attr_idlecpus); + return -ENODEV; + } + result = device_create_file(&device->dev, &dev_attr_rrtime); + if (result) { + device_remove_file(&device->dev, &dev_attr_idlecpus); + device_remove_file(&device->dev, &dev_attr_idlepct); + return -ENODEV; + } + return 0; +} + +static void acpi_pad_remove_sysfs(struct acpi_device *device) +{ + device_remove_file(&device->dev, &dev_attr_idlecpus); + device_remove_file(&device->dev, &dev_attr_idlepct); + device_remove_file(&device->dev, &dev_attr_rrtime); +} + +/* + * Query firmware how many CPUs should be idle + * return -1 on failure + */ +static int acpi_pad_pur(acpi_handle handle) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *package; + int num = -1; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer))) + return num; + + if (!buffer.length || !buffer.pointer) + return num; + + package = buffer.pointer; + + if (package->type == ACPI_TYPE_PACKAGE && + package->package.count == 2 && + package->package.elements[0].integer.value == 1) /* rev 1 */ + + num = package->package.elements[1].integer.value; + + kfree(buffer.pointer); + return num; +} + +static void acpi_pad_handle_notify(acpi_handle handle) +{ + int num_cpus; + uint32_t idle_cpus; + struct acpi_buffer param = { + .length = 4, + .pointer = (void *)&idle_cpus, + }; + + mutex_lock(&isolated_cpus_lock); + num_cpus = acpi_pad_pur(handle); + if (num_cpus < 0) { + mutex_unlock(&isolated_cpus_lock); + return; + } + acpi_pad_idle_cpus(num_cpus); + idle_cpus = acpi_pad_idle_cpus_num(); + acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, 0, ¶m); + mutex_unlock(&isolated_cpus_lock); +} + +static void acpi_pad_notify(acpi_handle handle, u32 event, + void *data) +{ + struct acpi_device *device = data; + + switch (event) { + case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: + acpi_pad_handle_notify(handle); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, 0); + break; + default: + pr_warn("Unsupported event [0x%x]\n", event); + break; + } +} + +static int acpi_pad_add(struct acpi_device *device) +{ + acpi_status status; + + strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); + + if (acpi_pad_add_sysfs(device)) + return -ENODEV; + + status = acpi_install_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, acpi_pad_notify, device); + if (ACPI_FAILURE(status)) { + acpi_pad_remove_sysfs(device); + return -ENODEV; + } + + return 0; +} + +static int acpi_pad_remove(struct acpi_device *device) +{ + mutex_lock(&isolated_cpus_lock); + acpi_pad_idle_cpus(0); + mutex_unlock(&isolated_cpus_lock); + + acpi_remove_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, acpi_pad_notify); + acpi_pad_remove_sysfs(device); + return 0; +} + +static const struct acpi_device_id pad_device_ids[] = { + {"ACPI000C", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, pad_device_ids); + +static struct acpi_driver acpi_pad_driver = { + .name = "processor_aggregator", + .class = ACPI_PROCESSOR_AGGREGATOR_CLASS, + .ids = pad_device_ids, + .ops = { + .add = acpi_pad_add, + .remove = acpi_pad_remove, + }, +}; + +static int __init acpi_pad_init(void) +{ + power_saving_mwait_init(); + if (power_saving_mwait_eax == 0) + return -EINVAL; + + return acpi_bus_register_driver(&acpi_pad_driver); +} + +static void __exit acpi_pad_exit(void) +{ + acpi_bus_unregister_driver(&acpi_pad_driver); +} + +module_init(acpi_pad_init); +module_exit(acpi_pad_exit); +MODULE_AUTHOR("Shaohua Li"); +MODULE_DESCRIPTION("ACPI Processor Aggregator Driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/acpi_platform.c b/kernel/drivers/acpi/acpi_platform.c new file mode 100644 index 000000000..4bf75597f --- /dev/null +++ b/kernel/drivers/acpi/acpi_platform.c @@ -0,0 +1,118 @@ +/* + * ACPI support for platform bus type. + * + * Copyright (C) 2012, Intel Corporation + * Authors: Mika Westerberg + * Mathias Nyman + * Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +ACPI_MODULE_NAME("platform"); + +static const struct acpi_device_id forbidden_id_list[] = { + {"PNP0000", 0}, /* PIC */ + {"PNP0100", 0}, /* Timer */ + {"PNP0200", 0}, /* AT DMA Controller */ + {"", 0}, +}; + +/** + * acpi_create_platform_device - Create platform device for ACPI device node + * @adev: ACPI device node to create a platform device for. + * + * Check if the given @adev can be represented as a platform device and, if + * that's the case, create and register a platform device, populate its common + * resources and returns a pointer to it. Otherwise, return %NULL. + * + * Name of the platform device will be the same as @adev's. + */ +struct platform_device *acpi_create_platform_device(struct acpi_device *adev) +{ + struct platform_device *pdev = NULL; + struct acpi_device *acpi_parent; + struct platform_device_info pdevinfo; + struct resource_entry *rentry; + struct list_head resource_list; + struct resource *resources = NULL; + int count; + + /* If the ACPI node already has a physical device attached, skip it. */ + if (adev->physical_node_count) + return NULL; + + if (!acpi_match_device_ids(adev, forbidden_id_list)) + return ERR_PTR(-EINVAL); + + INIT_LIST_HEAD(&resource_list); + count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); + if (count < 0) { + return NULL; + } else if (count > 0) { + resources = kmalloc(count * sizeof(struct resource), + GFP_KERNEL); + if (!resources) { + dev_err(&adev->dev, "No memory for resources\n"); + acpi_dev_free_resource_list(&resource_list); + return ERR_PTR(-ENOMEM); + } + count = 0; + list_for_each_entry(rentry, &resource_list, node) + resources[count++] = *rentry->res; + + acpi_dev_free_resource_list(&resource_list); + } + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + /* + * If the ACPI node has a parent and that parent has a physical device + * attached to it, that physical device should be the parent of the + * platform device we are about to create. + */ + pdevinfo.parent = NULL; + acpi_parent = adev->parent; + if (acpi_parent) { + struct acpi_device_physical_node *entry; + struct list_head *list; + + mutex_lock(&acpi_parent->physical_node_lock); + list = &acpi_parent->physical_node_list; + if (!list_empty(list)) { + entry = list_first_entry(list, + struct acpi_device_physical_node, + node); + pdevinfo.parent = entry->dev; + } + mutex_unlock(&acpi_parent->physical_node_lock); + } + pdevinfo.name = dev_name(&adev->dev); + pdevinfo.id = -1; + pdevinfo.res = resources; + pdevinfo.num_res = count; + pdevinfo.fwnode = acpi_fwnode_handle(adev); + pdevinfo.dma_mask = DMA_BIT_MASK(32); + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) + dev_err(&adev->dev, "platform device creation failed: %ld\n", + PTR_ERR(pdev)); + else + dev_dbg(&adev->dev, "created platform device %s\n", + dev_name(&pdev->dev)); + + kfree(resources); + return pdev; +} +EXPORT_SYMBOL_GPL(acpi_create_platform_device); diff --git a/kernel/drivers/acpi/acpi_pnp.c b/kernel/drivers/acpi/acpi_pnp.c new file mode 100644 index 000000000..ff6d8adc9 --- /dev/null +++ b/kernel/drivers/acpi/acpi_pnp.c @@ -0,0 +1,389 @@ +/* + * ACPI support for PNP bus type + * + * Copyright (C) 2014, Intel Corporation + * Authors: Zhang Rui + * Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +static const struct acpi_device_id acpi_pnp_device_ids[] = { + /* pata_isapnp */ + {"PNP0600"}, /* Generic ESDI/IDE/ATA compatible hard disk controller */ + /* floppy */ + {"PNP0700"}, + /* ipmi_si */ + {"IPI0001"}, + /* tpm_inf_pnp */ + {"IFX0101"}, /* Infineon TPMs */ + {"IFX0102"}, /* Infineon TPMs */ + /*tpm_tis */ + {"PNP0C31"}, /* TPM */ + {"ATM1200"}, /* Atmel */ + {"IFX0102"}, /* Infineon */ + {"BCM0101"}, /* Broadcom */ + {"BCM0102"}, /* Broadcom */ + {"NSC1200"}, /* National */ + {"ICO0102"}, /* Intel */ + /* ide */ + {"PNP0600"}, /* Generic ESDI/IDE/ATA compatible hard disk controller */ + /* ns558 */ + {"ASB16fd"}, /* AdLib NSC16 */ + {"AZT3001"}, /* AZT1008 */ + {"CDC0001"}, /* Opl3-SAx */ + {"CSC0001"}, /* CS4232 */ + {"CSC000f"}, /* CS4236 */ + {"CSC0101"}, /* CS4327 */ + {"CTL7001"}, /* SB16 */ + {"CTL7002"}, /* AWE64 */ + {"CTL7005"}, /* Vibra16 */ + {"ENS2020"}, /* SoundscapeVIVO */ + {"ESS0001"}, /* ES1869 */ + {"ESS0005"}, /* ES1878 */ + {"ESS6880"}, /* ES688 */ + {"IBM0012"}, /* CS4232 */ + {"OPT0001"}, /* OPTi Audio16 */ + {"YMH0006"}, /* Opl3-SA */ + {"YMH0022"}, /* Opl3-SAx */ + {"PNPb02f"}, /* Generic */ + /* i8042 kbd */ + {"PNP0300"}, + {"PNP0301"}, + {"PNP0302"}, + {"PNP0303"}, + {"PNP0304"}, + {"PNP0305"}, + {"PNP0306"}, + {"PNP0309"}, + {"PNP030a"}, + {"PNP030b"}, + {"PNP0320"}, + {"PNP0343"}, + {"PNP0344"}, + {"PNP0345"}, + {"CPQA0D7"}, + /* i8042 aux */ + {"AUI0200"}, + {"FJC6000"}, + {"FJC6001"}, + {"PNP0f03"}, + {"PNP0f0b"}, + {"PNP0f0e"}, + {"PNP0f12"}, + {"PNP0f13"}, + {"PNP0f19"}, + {"PNP0f1c"}, + {"SYN0801"}, + /* fcpnp */ + {"AVM0900"}, + /* radio-cadet */ + {"MSM0c24"}, /* ADS Cadet AM/FM Radio Card */ + /* radio-gemtek */ + {"ADS7183"}, /* AOpen FX-3D/Pro Radio */ + /* radio-sf16fmr2 */ + {"MFRad13"}, /* tuner subdevice of SF16-FMD2 */ + /* ene_ir */ + {"ENE0100"}, + {"ENE0200"}, + {"ENE0201"}, + {"ENE0202"}, + /* fintek-cir */ + {"FIT0002"}, /* CIR */ + /* ite-cir */ + {"ITE8704"}, /* Default model */ + {"ITE8713"}, /* CIR found in EEEBox 1501U */ + {"ITE8708"}, /* Bridged IT8512 */ + {"ITE8709"}, /* SRAM-Bridged IT8512 */ + /* nuvoton-cir */ + {"WEC0530"}, /* CIR */ + {"NTN0530"}, /* CIR for new chip's pnp id */ + /* Winbond CIR */ + {"WEC1022"}, + /* wbsd */ + {"WEC0517"}, + {"WEC0518"}, + /* Winbond CIR */ + {"TCM5090"}, /* 3Com Etherlink III (TP) */ + {"TCM5091"}, /* 3Com Etherlink III */ + {"TCM5094"}, /* 3Com Etherlink III (combo) */ + {"TCM5095"}, /* 3Com Etherlink III (TPO) */ + {"TCM5098"}, /* 3Com Etherlink III (TPC) */ + {"PNP80f7"}, /* 3Com Etherlink III compatible */ + {"PNP80f8"}, /* 3Com Etherlink III compatible */ + /* nsc-ircc */ + {"NSC6001"}, + {"HWPC224"}, + {"IBM0071"}, + /* smsc-ircc2 */ + {"SMCf010"}, + /* sb1000 */ + {"GIC1000"}, + /* parport_pc */ + {"PNP0400"}, /* Standard LPT Printer Port */ + {"PNP0401"}, /* ECP Printer Port */ + /* apple-gmux */ + {"APP000B"}, + /* system */ + {"PNP0c02"}, /* General ID for reserving resources */ + {"PNP0c01"}, /* memory controller */ + /* rtc_cmos */ + {"PNP0b00"}, + {"PNP0b01"}, + {"PNP0b02"}, + /* c6xdigio */ + {"PNP0400"}, /* Standard LPT Printer Port */ + {"PNP0401"}, /* ECP Printer Port */ + /* ni_atmio.c */ + {"NIC1900"}, + {"NIC2400"}, + {"NIC2500"}, + {"NIC2600"}, + {"NIC2700"}, + /* serial */ + {"AAC000F"}, /* Archtek America Corp. Archtek SmartLink Modem 3334BT Plug & Play */ + {"ADC0001"}, /* Anchor Datacomm BV. SXPro 144 External Data Fax Modem Plug & Play */ + {"ADC0002"}, /* SXPro 288 External Data Fax Modem Plug & Play */ + {"AEI0250"}, /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ + {"AEI1240"}, /* Actiontec ISA PNP 56K X2 Fax Modem */ + {"AKY1021"}, /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + {"AZT4001"}, /* AZT3005 PnP SOUND DEVICE */ + {"BDP3336"}, /* Best Data Products Inc. Smart One 336F PnP Modem */ + {"BRI0A49"}, /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + {"BRI1400"}, /* Boca Research 33,600 ACF Modem */ + {"BRI3400"}, /* Boca 33.6 Kbps Internal FD34FSVD */ + {"BRI0A49"}, /* Boca 33.6 Kbps Internal FD34FSVD */ + {"BDP3336"}, /* Best Data Products Inc. Smart One 336F PnP Modem */ + {"CPI4050"}, /* Computer Peripherals Inc. EuroViVa CommCenter-33.6 SP PnP */ + {"CTL3001"}, /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + {"CTL3011"}, /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + {"DAV0336"}, /* Davicom ISA 33.6K Modem */ + {"DMB1032"}, /* Creative Modem Blaster Flash56 DI5601-1 */ + {"DMB2001"}, /* Creative Modem Blaster V.90 DI5660 */ + {"ETT0002"}, /* E-Tech CyberBULLET PC56RVP */ + {"FUJ0202"}, /* Fujitsu 33600 PnP-I2 R Plug & Play */ + {"FUJ0205"}, /* Fujitsu FMV-FX431 Plug & Play */ + {"FUJ0206"}, /* Fujitsu 33600 PnP-I4 R Plug & Play */ + {"FUJ0209"}, /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + {"GVC000F"}, /* Archtek SmartLink Modem 3334BT Plug & Play */ + {"GVC0303"}, /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ + {"HAY0001"}, /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + {"HAY000C"}, /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + {"HAY000D"}, /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + {"HAY5670"}, /* Hayes Accura 56K Ext Fax Modem PnP */ + {"HAY5674"}, /* Hayes Accura 56K Ext Fax Modem PnP */ + {"HAY5675"}, /* Hayes Accura 56K Fax Modem PnP */ + {"HAYF000"}, /* Hayes 288, V.34 + FAX */ + {"HAYF001"}, /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + {"IBM0033"}, /* IBM Thinkpad 701 Internal Modem Voice */ + {"PNP4972"}, /* Intermec CV60 touchscreen port */ + {"IXDC801"}, /* Intertex 28k8 33k6 Voice EXT PnP */ + {"IXDC901"}, /* Intertex 33k6 56k Voice EXT PnP */ + {"IXDD801"}, /* Intertex 28k8 33k6 Voice SP EXT PnP */ + {"IXDD901"}, /* Intertex 33k6 56k Voice SP EXT PnP */ + {"IXDF401"}, /* Intertex 28k8 33k6 Voice SP INT PnP */ + {"IXDF801"}, /* Intertex 28k8 33k6 Voice SP EXT PnP */ + {"IXDF901"}, /* Intertex 33k6 56k Voice SP EXT PnP */ + {"KOR4522"}, /* KORTEX 28800 Externe PnP */ + {"KORF661"}, /* KXPro 33.6 Vocal ASVD PnP */ + {"LAS4040"}, /* LASAT Internet 33600 PnP */ + {"LAS4540"}, /* Lasat Safire 560 PnP */ + {"LAS5440"}, /* Lasat Safire 336 PnP */ + {"MNP0281"}, /* Microcom TravelPorte FAST V.34 Plug & Play */ + {"MNP0336"}, /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + {"MNP0339"}, /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + {"MNP0342"}, /* Microcom DeskPorte 28.8P Plug & Play */ + {"MNP0500"}, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + {"MNP0501"}, /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + {"MNP0502"}, /* Microcom DeskPorte 28.8S Internal Plug & Play */ + {"MOT1105"}, /* Motorola BitSURFR Plug & Play */ + {"MOT1111"}, /* Motorola TA210 Plug & Play */ + {"MOT1114"}, /* Motorola HMTA 200 (ISDN) Plug & Play */ + {"MOT1115"}, /* Motorola BitSURFR Plug & Play */ + {"MOT1190"}, /* Motorola Lifestyle 28.8 Internal */ + {"MOT1501"}, /* Motorola V.3400 Plug & Play */ + {"MOT1502"}, /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + {"MOT1505"}, /* Motorola Power 28.8 V.34 Plug & Play */ + {"MOT1509"}, /* Motorola ModemSURFR External 28.8 Plug & Play */ + {"MOT150A"}, /* Motorola Premier 33.6 Desktop Plug & Play */ + {"MOT150F"}, /* Motorola VoiceSURFR 56K External PnP */ + {"MOT1510"}, /* Motorola ModemSURFR 56K External PnP */ + {"MOT1550"}, /* Motorola ModemSURFR 56K Internal PnP */ + {"MOT1560"}, /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + {"MOT1580"}, /* Motorola Premier 33.6 Internal Plug & Play */ + {"MOT15B0"}, /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + {"MOT15F0"}, /* Motorola VoiceSURFR 56K Internal PnP */ + {"MVX00A1"}, /* Deskline K56 Phone System PnP */ + {"MVX00F2"}, /* PC Rider K56 Phone System PnP */ + {"nEC8241"}, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ + {"PMC2430"}, /* Pace 56 Voice Internal Plug & Play Modem */ + {"PNP0500"}, /* Generic standard PC COM port */ + {"PNP0501"}, /* Generic 16550A-compatible COM port */ + {"PNPC000"}, /* Compaq 14400 Modem */ + {"PNPC001"}, /* Compaq 2400/9600 Modem */ + {"PNPC031"}, /* Dial-Up Networking Serial Cable between 2 PCs */ + {"PNPC032"}, /* Dial-Up Networking Parallel Cable between 2 PCs */ + {"PNPC100"}, /* Standard 9600 bps Modem */ + {"PNPC101"}, /* Standard 14400 bps Modem */ + {"PNPC102"}, /* Standard 28800 bps Modem */ + {"PNPC103"}, /* Standard Modem */ + {"PNPC104"}, /* Standard 9600 bps Modem */ + {"PNPC105"}, /* Standard 14400 bps Modem */ + {"PNPC106"}, /* Standard 28800 bps Modem */ + {"PNPC107"}, /* Standard Modem */ + {"PNPC108"}, /* Standard 9600 bps Modem */ + {"PNPC109"}, /* Standard 14400 bps Modem */ + {"PNPC10A"}, /* Standard 28800 bps Modem */ + {"PNPC10B"}, /* Standard Modem */ + {"PNPC10C"}, /* Standard 9600 bps Modem */ + {"PNPC10D"}, /* Standard 14400 bps Modem */ + {"PNPC10E"}, /* Standard 28800 bps Modem */ + {"PNPC10F"}, /* Standard Modem */ + {"PNP2000"}, /* Standard PCMCIA Card Modem */ + {"ROK0030"}, /* Rockwell 33.6 DPF Internal PnP, Modular Technology 33.6 Internal PnP */ + {"ROK0100"}, /* KORTEX 14400 Externe PnP */ + {"ROK4120"}, /* Rockwell 28.8 */ + {"ROK4920"}, /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + {"RSS00A0"}, /* Rockwell 33.6 DPF External PnP, BT Prologue 33.6 External PnP, Modular Technology 33.6 External PnP */ + {"RSS0262"}, /* Viking 56K FAX INT */ + {"RSS0250"}, /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ + {"SUP1310"}, /* SupraExpress 28.8 Data/Fax PnP modem */ + {"SUP1381"}, /* SupraExpress 336i PnP Voice Modem */ + {"SUP1421"}, /* SupraExpress 33.6 Data/Fax PnP modem */ + {"SUP1590"}, /* SupraExpress 33.6 Data/Fax PnP modem */ + {"SUP1620"}, /* SupraExpress 336i Sp ASVD */ + {"SUP1760"}, /* SupraExpress 33.6 Data/Fax PnP modem */ + {"SUP2171"}, /* SupraExpress 56i Sp Intl */ + {"TEX0011"}, /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + {"UAC000F"}, /* Archtek SmartLink Modem 3334BT Plug & Play */ + {"USR0000"}, /* 3Com Corp. Gateway Telepath IIvi 33.6 */ + {"USR0002"}, /* U.S. Robotics Sporster 33.6K Fax INT PnP */ + {"USR0004"}, /* Sportster Vi 14.4 PnP FAX Voicemail */ + {"USR0006"}, /* U.S. Robotics 33.6K Voice INT PnP */ + {"USR0007"}, /* U.S. Robotics 33.6K Voice EXT PnP */ + {"USR0009"}, /* U.S. Robotics Courier V.Everything INT PnP */ + {"USR2002"}, /* U.S. Robotics 33.6K Voice INT PnP */ + {"USR2070"}, /* U.S. Robotics 56K Voice INT PnP */ + {"USR2080"}, /* U.S. Robotics 56K Voice EXT PnP */ + {"USR3031"}, /* U.S. Robotics 56K FAX INT */ + {"USR3050"}, /* U.S. Robotics 56K FAX INT */ + {"USR3070"}, /* U.S. Robotics 56K Voice INT PnP */ + {"USR3080"}, /* U.S. Robotics 56K Voice EXT PnP */ + {"USR3090"}, /* U.S. Robotics 56K Voice INT PnP */ + {"USR9100"}, /* U.S. Robotics 56K Message */ + {"USR9160"}, /* U.S. Robotics 56K FAX EXT PnP */ + {"USR9170"}, /* U.S. Robotics 56K FAX INT PnP */ + {"USR9180"}, /* U.S. Robotics 56K Voice EXT PnP */ + {"USR9190"}, /* U.S. Robotics 56K Voice INT PnP */ + {"WACFXXX"}, /* Wacom tablets */ + {"FPI2002"}, /* Compaq touchscreen */ + {"FUJ02B2"}, /* Fujitsu Stylistic touchscreens */ + {"FUJ02B3"}, + {"FUJ02B4"}, /* Fujitsu Stylistic LT touchscreens */ + {"FUJ02B6"}, /* Passive Fujitsu Stylistic touchscreens */ + {"FUJ02B7"}, + {"FUJ02B8"}, + {"FUJ02B9"}, + {"FUJ02BC"}, + {"FUJ02E5"}, /* Fujitsu Wacom Tablet PC device */ + {"FUJ02E6"}, /* Fujitsu P-series tablet PC device */ + {"FUJ02E7"}, /* Fujitsu Wacom 2FGT Tablet PC device */ + {"FUJ02E9"}, /* Fujitsu Wacom 1FGT Tablet PC device */ + {"LTS0001"}, /* LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in disguise) */ + {"WCI0003"}, /* Rockwell's (PORALiNK) 33600 INT PNP */ + {"WEC1022"}, /* Winbond CIR port, should not be probed. We should keep track of it to prevent the legacy serial driver from probing it */ + /* scl200wdt */ + {"NSC0800"}, /* National Semiconductor PC87307/PC97307 watchdog component */ + /* mpu401 */ + {"PNPb006"}, + /* cs423x-pnpbios */ + {"CSC0100"}, + {"CSC0103"}, + {"CSC0110"}, + {"CSC0000"}, + {"GIM0100"}, /* Guillemot Turtlebeach something appears to be cs4232 compatible */ + /* es18xx-pnpbios */ + {"ESS1869"}, + {"ESS1879"}, + /* snd-opl3sa2-pnpbios */ + {"YMH0021"}, + {"NMX2210"}, /* Gateway Solo 2500 */ + {""}, +}; + +static bool matching_id(char *idstr, char *list_id) +{ + int i; + + if (memcmp(idstr, list_id, 3)) + return false; + + for (i = 3; i < 7; i++) { + char c = toupper(idstr[i]); + + if (!isxdigit(c) + || (list_id[i] != 'X' && c != toupper(list_id[i]))) + return false; + } + return true; +} + +static bool acpi_pnp_match(char *idstr, const struct acpi_device_id **matchid) +{ + const struct acpi_device_id *devid; + + for (devid = acpi_pnp_device_ids; devid->id[0]; devid++) + if (matching_id(idstr, (char *)devid->id)) { + if (matchid) + *matchid = devid; + + return true; + } + + return false; +} + +static int acpi_pnp_attach(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + return 1; +} + +static struct acpi_scan_handler acpi_pnp_handler = { + .ids = acpi_pnp_device_ids, + .match = acpi_pnp_match, + .attach = acpi_pnp_attach, +}; + +/* + * For CMOS RTC devices, the PNP ACPI scan handler does not work, because + * there is a CMOS RTC ACPI scan handler installed already, so we need to + * check those devices and enumerate them to the PNP bus directly. + */ +static int is_cmos_rtc_device(struct acpi_device *adev) +{ + struct acpi_device_id ids[] = { + { "PNP0B00" }, + { "PNP0B01" }, + { "PNP0B02" }, + {""}, + }; + return !acpi_match_device_ids(adev, ids); +} + +bool acpi_is_pnp_device(struct acpi_device *adev) +{ + return adev->handler == &acpi_pnp_handler || is_cmos_rtc_device(adev); +} +EXPORT_SYMBOL_GPL(acpi_is_pnp_device); + +void __init acpi_pnp_init(void) +{ + acpi_scan_add_handler(&acpi_pnp_handler); +} diff --git a/kernel/drivers/acpi/acpi_processor.c b/kernel/drivers/acpi/acpi_processor.c new file mode 100644 index 000000000..58f335ca2 --- /dev/null +++ b/kernel/drivers/acpi/acpi_processor.c @@ -0,0 +1,504 @@ +/* + * acpi_processor.c - ACPI processor enumeration support + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004 Dominik Brodowski + * Copyright (C) 2004 Anil S Keshavamurthy + * Copyright (C) 2013, Intel Corporation + * Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include "internal.h" + +#define _COMPONENT ACPI_PROCESSOR_COMPONENT + +ACPI_MODULE_NAME("processor"); + +DEFINE_PER_CPU(struct acpi_processor *, processors); +EXPORT_PER_CPU_SYMBOL(processors); + +/* -------------------------------------------------------------------------- + Errata Handling + -------------------------------------------------------------------------- */ + +struct acpi_processor_errata errata __read_mostly; +EXPORT_SYMBOL_GPL(errata); + +static int acpi_processor_errata_piix4(struct pci_dev *dev) +{ + u8 value1 = 0; + u8 value2 = 0; + + + if (!dev) + return -EINVAL; + + /* + * Note that 'dev' references the PIIX4 ACPI Controller. + */ + + switch (dev->revision) { + case 0: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n")); + break; + case 1: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n")); + break; + case 2: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n")); + break; + case 3: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n")); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n")); + break; + } + + switch (dev->revision) { + + case 0: /* PIIX4 A-step */ + case 1: /* PIIX4 B-step */ + /* + * See specification changes #13 ("Manual Throttle Duty Cycle") + * and #14 ("Enabling and Disabling Manual Throttle"), plus + * erratum #5 ("STPCLK# Deassertion Time") from the January + * 2002 PIIX4 specification update. Applies to only older + * PIIX4 models. + */ + errata.piix4.throttle = 1; + + case 2: /* PIIX4E */ + case 3: /* PIIX4M */ + /* + * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA + * Livelock") from the January 2002 PIIX4 specification update. + * Applies to all PIIX4 models. + */ + + /* + * BM-IDE + * ------ + * Find the PIIX4 IDE Controller and get the Bus Master IDE + * Status register address. We'll use this later to read + * each IDE controller's DMA status to make sure we catch all + * DMA activity. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + errata.piix4.bmisx = pci_resource_start(dev, 4); + pci_dev_put(dev); + } + + /* + * Type-F DMA + * ---------- + * Find the PIIX4 ISA Controller and read the Motherboard + * DMA controller's status to see if Type-F (Fast) DMA mode + * is enabled (bit 7) on either channel. Note that we'll + * disable C3 support if this is enabled, as some legacy + * devices won't operate well if fast DMA is disabled. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + pci_read_config_byte(dev, 0x76, &value1); + pci_read_config_byte(dev, 0x77, &value2); + if ((value1 & 0x80) || (value2 & 0x80)) + errata.piix4.fdma = 1; + pci_dev_put(dev); + } + + break; + } + + if (errata.piix4.bmisx) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus master activity detection (BM-IDE) erratum enabled\n")); + if (errata.piix4.fdma) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Type-F DMA livelock erratum (C3 disabled)\n")); + + return 0; +} + +static int acpi_processor_errata(void) +{ + int result = 0; + struct pci_dev *dev = NULL; + + /* + * PIIX4 + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, + PCI_ANY_ID, NULL); + if (dev) { + result = acpi_processor_errata_piix4(dev); + pci_dev_put(dev); + } + + return result; +} + +/* -------------------------------------------------------------------------- + Initialization + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +static int acpi_processor_hotadd_init(struct acpi_processor *pr) +{ + unsigned long long sta; + acpi_status status; + int ret; + + if (pr->phys_id == PHYS_CPUID_INVALID) + return -ENODEV; + + status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) + return -ENODEV; + + cpu_maps_update_begin(); + cpu_hotplug_begin(); + + ret = acpi_map_cpu(pr->handle, pr->phys_id, &pr->id); + if (ret) + goto out; + + ret = arch_register_cpu(pr->id); + if (ret) { + acpi_unmap_cpu(pr->id); + goto out; + } + + /* + * CPU got hot-added, but cpu_data is not initialized yet. Set a flag + * to delay cpu_idle/throttling initialization and do it when the CPU + * gets online for the first time. + */ + pr_info("CPU%d has been hot-added\n", pr->id); + pr->flags.need_hotplug_init = 1; + +out: + cpu_hotplug_done(); + cpu_maps_update_done(); + return ret; +} +#else +static inline int acpi_processor_hotadd_init(struct acpi_processor *pr) +{ + return -ENODEV; +} +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +static int acpi_processor_get_info(struct acpi_device *device) +{ + union acpi_object object = { 0 }; + struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; + struct acpi_processor *pr = acpi_driver_data(device); + phys_cpuid_t phys_id; + int cpu_index, device_declaration = 0; + acpi_status status = AE_OK; + static int cpu0_initialized; + unsigned long long value; + + acpi_processor_errata(); + + /* + * Check to see if we have bus mastering arbitration control. This + * is required for proper C3 usage (to maintain cache coherency). + */ + if (acpi_gbl_FADT.pm2_control_block && acpi_gbl_FADT.pm2_control_length) { + pr->flags.bm_control = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus mastering arbitration control present\n")); + } else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No bus mastering arbitration control\n")); + + if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { + /* Declared with "Processor" statement; match ProcessorID */ + status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, + "Failed to evaluate processor object (0x%x)\n", + status); + return -ENODEV; + } + + pr->acpi_id = object.processor.proc_id; + } else { + /* + * Declared with "Device" statement; match _UID. + * Note that we don't handle string _UIDs yet. + */ + status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, + NULL, &value); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, + "Failed to evaluate processor _UID (0x%x)\n", + status); + return -ENODEV; + } + device_declaration = 1; + pr->acpi_id = value; + } + + phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id); + if (phys_id == PHYS_CPUID_INVALID) + acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n"); + pr->phys_id = phys_id; + + cpu_index = acpi_map_cpuid(pr->phys_id, pr->acpi_id); + if (!cpu0_initialized && !acpi_has_cpu_in_madt()) { + cpu0_initialized = 1; + /* + * Handle UP system running SMP kernel, with no CPU + * entry in MADT + */ + if ((cpu_index == -1) && (num_online_cpus() == 1)) + cpu_index = 0; + } + pr->id = cpu_index; + + /* + * Extra Processor objects may be enumerated on MP systems with + * less than the max # of CPUs. They should be ignored _iff + * they are physically not present. + */ + if (pr->id == -1) { + int ret = acpi_processor_hotadd_init(pr); + if (ret) + return ret; + } + + /* + * On some boxes several processors use the same processor bus id. + * But they are located in different scope. For example: + * \_SB.SCK0.CPU0 + * \_SB.SCK1.CPU0 + * Rename the processor device bus id. And the new bus id will be + * generated as the following format: + * CPU+CPU ID. + */ + sprintf(acpi_device_bid(device), "CPU%X", pr->id); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, + pr->acpi_id)); + + if (!object.processor.pblk_address) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n")); + else if (object.processor.pblk_length != 6) + dev_err(&device->dev, "Invalid PBLK length [%d]\n", + object.processor.pblk_length); + else { + pr->throttling.address = object.processor.pblk_address; + pr->throttling.duty_offset = acpi_gbl_FADT.duty_offset; + pr->throttling.duty_width = acpi_gbl_FADT.duty_width; + + pr->pblk = object.processor.pblk_address; + + /* + * We don't care about error returns - we just try to mark + * these reserved so that nobody else is confused into thinking + * that this region might be unused.. + * + * (In particular, allocating the IO range for Cardbus) + */ + request_region(pr->throttling.address, 6, "ACPI CPU throttle"); + } + + /* + * If ACPI describes a slot number for this CPU, we can use it to + * ensure we get the right value in the "physical id" field + * of /proc/cpuinfo + */ + status = acpi_evaluate_integer(pr->handle, "_SUN", NULL, &value); + if (ACPI_SUCCESS(status)) + arch_fix_phys_package_id(pr->id, value); + + return 0; +} + +/* + * Do not put anything in here which needs the core to be online. + * For example MSR access or setting up things which check for cpuinfo_x86 + * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. + * Such things have to be put in and set up by the processor driver's .probe(). + */ +static DEFINE_PER_CPU(void *, processor_device_array); + +static int acpi_processor_add(struct acpi_device *device, + const struct acpi_device_id *id) +{ + struct acpi_processor *pr; + struct device *dev; + int result = 0; + + pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); + if (!pr) + return -ENOMEM; + + if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) { + result = -ENOMEM; + goto err_free_pr; + } + + pr->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); + device->driver_data = pr; + + result = acpi_processor_get_info(device); + if (result) /* Processor is not physically present or unavailable */ + return 0; + +#ifdef CONFIG_SMP + if (pr->id >= setup_max_cpus && pr->id != 0) + return 0; +#endif + + BUG_ON(pr->id >= nr_cpu_ids); + + /* + * Buggy BIOS check. + * ACPI id of processors can be reported wrongly by the BIOS. + * Don't trust it blindly + */ + if (per_cpu(processor_device_array, pr->id) != NULL && + per_cpu(processor_device_array, pr->id) != device) { + dev_warn(&device->dev, + "BIOS reported wrong ACPI id %d for the processor\n", + pr->id); + /* Give up, but do not abort the namespace scan. */ + goto err; + } + /* + * processor_device_array is not cleared on errors to allow buggy BIOS + * checks. + */ + per_cpu(processor_device_array, pr->id) = device; + per_cpu(processors, pr->id) = pr; + + dev = get_cpu_device(pr->id); + if (!dev) { + result = -ENODEV; + goto err; + } + + result = acpi_bind_one(dev, device); + if (result) + goto err; + + pr->dev = dev; + + /* Trigger the processor driver's .probe() if present. */ + if (device_attach(dev) >= 0) + return 1; + + dev_err(dev, "Processor driver could not be attached\n"); + acpi_unbind_one(dev); + + err: + free_cpumask_var(pr->throttling.shared_cpu_map); + device->driver_data = NULL; + per_cpu(processors, pr->id) = NULL; + err_free_pr: + kfree(pr); + return result; +} + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +/* -------------------------------------------------------------------------- + Removal + -------------------------------------------------------------------------- */ + +static void acpi_processor_remove(struct acpi_device *device) +{ + struct acpi_processor *pr; + + if (!device || !acpi_driver_data(device)) + return; + + pr = acpi_driver_data(device); + if (pr->id >= nr_cpu_ids) + goto out; + + /* + * The only reason why we ever get here is CPU hot-removal. The CPU is + * already offline and the ACPI device removal locking prevents it from + * being put back online at this point. + * + * Unbind the driver from the processor device and detach it from the + * ACPI companion object. + */ + device_release_driver(pr->dev); + acpi_unbind_one(pr->dev); + + /* Clean up. */ + per_cpu(processor_device_array, pr->id) = NULL; + per_cpu(processors, pr->id) = NULL; + + cpu_maps_update_begin(); + cpu_hotplug_begin(); + + /* Remove the CPU. */ + arch_unregister_cpu(pr->id); + acpi_unmap_cpu(pr->id); + + cpu_hotplug_done(); + cpu_maps_update_done(); + + try_offline_node(cpu_to_node(pr->id)); + + out: + free_cpumask_var(pr->throttling.shared_cpu_map); + kfree(pr); +} +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +/* + * The following ACPI IDs are known to be suitable for representing as + * processor devices. + */ +static const struct acpi_device_id processor_device_ids[] = { + + { ACPI_PROCESSOR_OBJECT_HID, }, + { ACPI_PROCESSOR_DEVICE_HID, }, + + { } +}; + +static struct acpi_scan_handler __refdata processor_handler = { + .ids = processor_device_ids, + .attach = acpi_processor_add, +#ifdef CONFIG_ACPI_HOTPLUG_CPU + .detach = acpi_processor_remove, +#endif + .hotplug = { + .enabled = true, + }, +}; + +void __init acpi_processor_init(void) +{ + acpi_scan_add_handler_with_hotplug(&processor_handler, "processor"); +} diff --git a/kernel/drivers/acpi/acpica/Makefile b/kernel/drivers/acpi/acpica/Makefile new file mode 100644 index 000000000..c1a963581 --- /dev/null +++ b/kernel/drivers/acpi/acpica/Makefile @@ -0,0 +1,185 @@ +# +# Makefile for ACPICA Core interpreter +# + +ccflags-y := -Os -DBUILDING_ACPICA +ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT + +# use acpi.o to put all files here into acpi.o modparam namespace +obj-y += acpi.o + +acpi-y := \ + dsargs.o \ + dscontrol.o \ + dsfield.o \ + dsinit.o \ + dsmethod.o \ + dsmthdat.o \ + dsobject.o \ + dsopcode.o \ + dsutils.o \ + dswexec.o \ + dswload.o \ + dswload2.o \ + dswscope.o \ + dswstate.o + +acpi-y += \ + evevent.o \ + evgpe.o \ + evgpeblk.o \ + evgpeinit.o \ + evgpeutil.o \ + evglock.o \ + evhandler.o \ + evmisc.o \ + evregion.o \ + evrgnini.o \ + evsci.o \ + evxface.o \ + evxfevnt.o \ + evxfgpe.o \ + evxfregn.o + +acpi-y += \ + exconfig.o \ + exconvrt.o \ + excreate.o \ + exdebug.o \ + exdump.o \ + exfield.o \ + exfldio.o \ + exmutex.o \ + exnames.o \ + exoparg1.o \ + exoparg2.o \ + exoparg3.o \ + exoparg6.o \ + exprep.o \ + exmisc.o \ + exregion.o \ + exresnte.o \ + exresolv.o \ + exresop.o \ + exstore.o \ + exstoren.o \ + exstorob.o \ + exsystem.o \ + exutils.o + +acpi-y += \ + hwacpi.o \ + hwesleep.o \ + hwgpe.o \ + hwpci.o \ + hwregs.o \ + hwsleep.o \ + hwvalid.o \ + hwxface.o \ + hwxfsleep.o + +acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o + +acpi-y += \ + nsaccess.o \ + nsalloc.o \ + nsarguments.o \ + nsconvert.o \ + nsdump.o \ + nseval.o \ + nsinit.o \ + nsload.o \ + nsnames.o \ + nsobject.o \ + nsparse.o \ + nspredef.o \ + nsprepkg.o \ + nsrepair.o \ + nsrepair2.o \ + nssearch.o \ + nsutils.o \ + nswalk.o \ + nsxfeval.o \ + nsxfname.o \ + nsxfobj.o + +acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o + +acpi-y += \ + psargs.o \ + psloop.o \ + psobject.o \ + psopcode.o \ + psopinfo.o \ + psparse.o \ + psscope.o \ + pstree.o \ + psutils.o \ + pswalk.o \ + psxface.o + +acpi-y += \ + rsaddr.o \ + rscalc.o \ + rscreate.o \ + rsdump.o \ + rsdumpinfo.o \ + rsinfo.o \ + rsio.o \ + rsirq.o \ + rslist.o \ + rsmemory.o \ + rsmisc.o \ + rsserial.o \ + rsutils.o \ + rsxface.o + +acpi-y += \ + tbdata.o \ + tbfadt.o \ + tbfind.o \ + tbinstal.o \ + tbprint.o \ + tbutils.o \ + tbxface.o \ + tbxfload.o \ + tbxfroot.o + +acpi-y += \ + utaddress.o \ + utalloc.o \ + utbuffer.o \ + utcopy.o \ + utexcep.o \ + utdebug.o \ + utdecode.o \ + utdelete.o \ + uterror.o \ + uteval.o \ + utglobal.o \ + uthex.o \ + utids.o \ + utinit.o \ + utlock.o \ + utmath.o \ + utmisc.o \ + utmutex.o \ + utobject.o \ + utosi.o \ + utownerid.o \ + utpredef.o \ + utresrc.o \ + utstate.o \ + utstring.o \ + utxface.o \ + utxfinit.o \ + utxferror.o \ + utxfmutex.o + +acpi-$(ACPI_FUTURE_USAGE) += \ + utcache.o \ + utfileio.o \ + utprint.o \ + uttrack.o \ + utuuid.o + diff --git a/kernel/drivers/acpi/acpica/acapps.h b/kernel/drivers/acpi/acpica/acapps.h new file mode 100644 index 000000000..e9f0833e8 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acapps.h @@ -0,0 +1,173 @@ +/****************************************************************************** + * + * Module Name: acapps - common include for ACPI applications/tools + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _ACAPPS +#define _ACAPPS + +/* Common info for tool signons */ + +#define ACPICA_NAME "Intel ACPI Component Architecture" +#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2015 Intel Corporation" + +#if ACPI_MACHINE_WIDTH == 64 +#define ACPI_WIDTH "-64" + +#elif ACPI_MACHINE_WIDTH == 32 +#define ACPI_WIDTH "-32" + +#else +#error unknown ACPI_MACHINE_WIDTH +#define ACPI_WIDTH "-??" + +#endif + +/* Macros for signons and file headers */ + +#define ACPI_COMMON_SIGNON(utility_name) \ + "\n%s\n%s version %8.8X%s\n%s\n\n", \ + ACPICA_NAME, \ + utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, \ + ACPICA_COPYRIGHT + +#define ACPI_COMMON_HEADER(utility_name, prefix) \ + "%s%s\n%s%s version %8.8X%s\n%s%s\n%s\n", \ + prefix, ACPICA_NAME, \ + prefix, utility_name, ((u32) ACPI_CA_VERSION), ACPI_WIDTH, \ + prefix, ACPICA_COPYRIGHT, \ + prefix + +/* Macros for usage messages */ + +#define ACPI_USAGE_HEADER(usage) \ + acpi_os_printf ("Usage: %s\nOptions:\n", usage); + +#define ACPI_USAGE_TEXT(description) \ + acpi_os_printf (description); + +#define ACPI_OPTION(name, description) \ + acpi_os_printf (" %-18s%s\n", name, description); + +#define FILE_SUFFIX_DISASSEMBLY "dsl" +#define ACPI_TABLE_FILE_SUFFIX ".dat" + +/* + * getopt + */ +int acpi_getopt(int argc, char **argv, char *opts); + +int acpi_getopt_argument(int argc, char **argv); + +extern int acpi_gbl_optind; +extern int acpi_gbl_opterr; +extern int acpi_gbl_sub_opt_char; +extern char *acpi_gbl_optarg; + +/* + * cmfsize - Common get file size function + */ +u32 cm_get_file_size(ACPI_FILE file); + +#ifndef ACPI_DUMP_APP +/* + * adisasm + */ +acpi_status +ad_aml_disassemble(u8 out_to_file, + char *filename, char *prefix, char **out_filename); + +void ad_print_statistics(void); + +acpi_status ad_find_dsdt(u8 **dsdt_ptr, u32 *dsdt_length); + +void ad_dump_tables(void); + +acpi_status ad_get_local_tables(void); + +acpi_status +ad_parse_table(struct acpi_table_header *table, + acpi_owner_id * owner_id, u8 load_table, u8 external); + +acpi_status ad_display_tables(char *filename, struct acpi_table_header *table); + +acpi_status ad_display_statistics(void); + +/* + * adwalk + */ +void +acpi_dm_cross_reference_namespace(union acpi_parse_object *parse_tree_root, + struct acpi_namespace_node *namespace_root, + acpi_owner_id owner_id); + +void acpi_dm_dump_tree(union acpi_parse_object *origin); + +void acpi_dm_find_orphan_methods(union acpi_parse_object *origin); + +void +acpi_dm_finish_namespace_load(union acpi_parse_object *parse_tree_root, + struct acpi_namespace_node *namespace_root, + acpi_owner_id owner_id); + +void +acpi_dm_convert_resource_indexes(union acpi_parse_object *parse_tree_root, + struct acpi_namespace_node *namespace_root); + +/* + * adfile + */ +acpi_status ad_initialize(void); + +char *fl_generate_filename(char *input_filename, char *suffix); + +acpi_status +fl_split_input_pathname(char *input_path, + char **out_directory_path, char **out_filename); + +char *ad_generate_filename(char *prefix, char *table_id); + +void +ad_write_table(struct acpi_table_header *table, + u32 length, char *table_name, char *oem_table_id); +#endif + +#endif /* _ACAPPS */ diff --git a/kernel/drivers/acpi/acpica/accommon.h b/kernel/drivers/acpi/acpica/accommon.h new file mode 100644 index 000000000..853aa2dbd --- /dev/null +++ b/kernel/drivers/acpi/acpica/accommon.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * Name: accommon.h - Common include files for generation of ACPICA source + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACCOMMON_H__ +#define __ACCOMMON_H__ + +/* + * Common set of includes for all ACPICA source files. + * We put them here because we don't want to duplicate them + * in the the source code again and again. + * + * Note: The order of these include files is important. + */ +#include /* Global configuration constants */ +#include "acmacros.h" /* C macros */ +#include "aclocal.h" /* Internal data types */ +#include "acobject.h" /* ACPI internal object */ +#include "acstruct.h" /* Common structures */ +#include "acglobal.h" /* All global variables */ +#include "achware.h" /* Hardware defines and interfaces */ +#include "acutils.h" /* Utility interfaces */ + +#endif /* __ACCOMMON_H__ */ diff --git a/kernel/drivers/acpi/acpica/acdebug.h b/kernel/drivers/acpi/acpica/acdebug.h new file mode 100644 index 000000000..4169bb87a --- /dev/null +++ b/kernel/drivers/acpi/acpica/acdebug.h @@ -0,0 +1,282 @@ +/****************************************************************************** + * + * Name: acdebug.h - ACPI/AML debugger + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACDEBUG_H__ +#define __ACDEBUG_H__ + +#define ACPI_DEBUG_BUFFER_SIZE 0x4000 /* 16K buffer for return objects */ + +struct acpi_db_command_info { + char *name; /* Command Name */ + u8 min_args; /* Minimum arguments required */ +}; + +struct acpi_db_command_help { + u8 line_count; /* Number of help lines */ + char *invocation; /* Command Invocation */ + char *description; /* Command Description */ +}; + +struct acpi_db_argument_info { + char *name; /* Argument Name */ +}; + +struct acpi_db_execute_walk { + u32 count; + u32 max_count; +}; + +#define PARAM_LIST(pl) pl +#define DBTEST_OUTPUT_LEVEL(lvl) if (acpi_gbl_db_opt_verbose) +#define VERBOSE_PRINT(fp) DBTEST_OUTPUT_LEVEL(lvl) {\ + acpi_os_printf PARAM_LIST(fp);} + +#define EX_NO_SINGLE_STEP 1 +#define EX_SINGLE_STEP 2 + +/* + * dbxface - external debugger interfaces + */ +acpi_status acpi_db_initialize(void); + +void acpi_db_terminate(void); + +acpi_status +acpi_db_single_step(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, u32 op_type); + +/* + * dbcmds - debug commands and output routines + */ +struct acpi_namespace_node *acpi_db_convert_to_node(char *in_string); + +void acpi_db_display_table_info(char *table_arg); + +void acpi_db_display_template(char *buffer_arg); + +void acpi_db_unload_acpi_table(char *name); + +void acpi_db_send_notify(char *name, u32 value); + +void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg); + +acpi_status acpi_db_sleep(char *object_arg); + +void acpi_db_display_locks(void); + +void acpi_db_display_resources(char *object_arg); + +ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_display_gpes(void)) + +void acpi_db_display_handlers(void); + +ACPI_HW_DEPENDENT_RETURN_VOID(void + acpi_db_generate_gpe(char *gpe_arg, + char *block_arg)) +ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_generate_sci(void)) + +void acpi_db_execute_test(char *type_arg); + +/* + * dbconvert - miscellaneous conversion routines + */ +acpi_status acpi_db_hex_char_to_value(int hex_char, u8 *return_value); + +acpi_status acpi_db_convert_to_package(char *string, union acpi_object *object); + +acpi_status +acpi_db_convert_to_object(acpi_object_type type, + char *string, union acpi_object *object); + +u8 *acpi_db_encode_pld_buffer(struct acpi_pld_info *pld_info); + +void acpi_db_dump_pld_buffer(union acpi_object *obj_desc); + +/* + * dbmethod - control method commands + */ +void +acpi_db_set_method_breakpoint(char *location, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +void acpi_db_set_method_call_breakpoint(union acpi_parse_object *op); + +void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg); + +acpi_status acpi_db_disassemble_method(char *name); + +void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op); + +void acpi_db_batch_execute(char *count_arg); + +/* + * dbnames - namespace commands + */ +void acpi_db_set_scope(char *name); + +void acpi_db_dump_namespace(char *start_arg, char *depth_arg); + +void acpi_db_dump_namespace_paths(void); + +void acpi_db_dump_namespace_by_owner(char *owner_arg, char *depth_arg); + +acpi_status acpi_db_find_name_in_namespace(char *name_arg); + +void acpi_db_check_predefined_names(void); + +acpi_status +acpi_db_display_objects(char *obj_type_arg, char *display_count_arg); + +void acpi_db_check_integrity(void); + +void acpi_db_find_references(char *object_arg); + +void acpi_db_get_bus_info(void); + +/* + * dbdisply - debug display commands + */ +void acpi_db_display_method_info(union acpi_parse_object *op); + +void acpi_db_decode_and_display_object(char *target, char *output_type); + +void +acpi_db_display_result_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status acpi_db_display_all_methods(char *display_count_arg); + +void acpi_db_display_arguments(void); + +void acpi_db_display_locals(void); + +void acpi_db_display_results(void); + +void acpi_db_display_calling_tree(void); + +void acpi_db_display_object_type(char *object_arg); + +void +acpi_db_display_argument_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +/* + * dbexec - debugger control method execution + */ +void +acpi_db_execute(char *name, char **args, acpi_object_type * types, u32 flags); + +void +acpi_db_create_execution_threads(char *num_threads_arg, + char *num_loops_arg, char *method_name_arg); + +void acpi_db_delete_objects(u32 count, union acpi_object *objects); + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS +u32 acpi_db_get_cache_info(struct acpi_memory_list *cache); +#endif + +/* + * dbfileio - Debugger file I/O commands + */ +acpi_object_type +acpi_db_match_argument(char *user_argument, + struct acpi_db_argument_info *arguments); + +void acpi_db_close_debug_file(void); + +void acpi_db_open_debug_file(char *name); + +acpi_status acpi_db_load_acpi_table(char *filename); + +acpi_status +acpi_db_get_table_from_file(char *filename, struct acpi_table_header **table); + +/* + * dbhistry - debugger HISTORY command + */ +void acpi_db_add_to_history(char *command_line); + +void acpi_db_display_history(void); + +char *acpi_db_get_from_history(char *command_num_arg); + +char *acpi_db_get_history_by_index(u32 commandd_num); + +/* + * dbinput - user front-end to the AML debugger + */ +acpi_status +acpi_db_command_dispatch(char *input_buffer, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context); + +acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op); + +char *acpi_db_get_next_token(char *string, + char **next, acpi_object_type * return_type); + +/* + * dbstats - Generation and display of ACPI table statistics + */ +void acpi_db_generate_statistics(union acpi_parse_object *root, u8 is_method); + +acpi_status acpi_db_display_statistics(char *type_arg); + +/* + * dbutils - AML debugger utilities + */ +void acpi_db_set_output_destination(u32 where); + +void acpi_db_dump_external_object(union acpi_object *obj_desc, u32 level); + +void acpi_db_prep_namestring(char *name); + +struct acpi_namespace_node *acpi_db_local_ns_lookup(char *name); + +void acpi_db_uint32_to_hex_string(u32 value, char *buffer); + +#endif /* __ACDEBUG_H__ */ diff --git a/kernel/drivers/acpi/acpica/acdispat.h b/kernel/drivers/acpi/acpica/acdispat.h new file mode 100644 index 000000000..408f04bca --- /dev/null +++ b/kernel/drivers/acpi/acpica/acdispat.h @@ -0,0 +1,357 @@ +/****************************************************************************** + * + * Name: acdispat.h - dispatcher (parser to interpreter interface) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _ACDISPAT_H_ +#define _ACDISPAT_H_ + +#define NAMEOF_LOCAL_NTE "__L0" +#define NAMEOF_ARG_NTE "__A0" + +/* + * dsargs - execution of dynamic arguments for static objects + */ +acpi_status +acpi_ds_get_buffer_field_arguments(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_get_bank_field_arguments(union acpi_operand_object *obj_desc); + +acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *rgn_desc); + +acpi_status acpi_ds_get_buffer_arguments(union acpi_operand_object *obj_desc); + +acpi_status acpi_ds_get_package_arguments(union acpi_operand_object *obj_desc); + +/* + * dscontrol - support for execution control opcodes + */ +acpi_status +acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_exec_end_control_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +/* + * dsopcode - support for late operand evaluation + */ +acpi_status +acpi_ds_eval_buffer_field_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_eval_table_region_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_eval_bank_field_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status acpi_ds_initialize_region(acpi_handle obj_handle); + +/* + * dsexec - Parser/Interpreter interface, method execution callbacks + */ +acpi_status +acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state, + union acpi_operand_object *result_obj); + +acpi_status +acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op); + +acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *state); + +/* + * dsfield - Parser/Interpreter interface for AML fields + */ +acpi_status +acpi_ds_create_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_bank_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_index_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_buffer_field(union acpi_parse_object *op, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_init_field_objects(union acpi_parse_object *op, + struct acpi_walk_state *walk_state); + +/* + * dsload - Parser/Interpreter interface + */ +acpi_status +acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number); + +/* dsload - pass 1 namespace load callbacks */ + +acpi_status +acpi_ds_load1_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op); + +acpi_status acpi_ds_load1_end_op(struct acpi_walk_state *walk_state); + +/* dsload - pass 2 namespace load callbacks */ + +acpi_status +acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op); + +acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state); + +/* + * dsmthdat - method data (locals/args) + */ +acpi_status +acpi_ds_store_object_to_local(u8 type, + u32 index, + union acpi_operand_object *src_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_method_data_get_entry(u16 opcode, + u32 index, + struct acpi_walk_state *walk_state, + union acpi_operand_object ***node); + +void acpi_ds_method_data_delete_all(struct acpi_walk_state *walk_state); + +u8 acpi_ds_is_method_value(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_method_data_get_value(u8 type, + u32 index, + struct acpi_walk_state *walk_state, + union acpi_operand_object **dest_desc); + +acpi_status +acpi_ds_method_data_init_args(union acpi_operand_object **params, + u32 max_param_count, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_method_data_get_node(u8 type, + u32 index, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **node); + +void acpi_ds_method_data_init(struct acpi_walk_state *walk_state); + +/* + * dsmethod - Parser/Interpreter interface - control method parsing + */ +acpi_status +acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, + union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_call_control_method(struct acpi_thread_state *thread, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_restart_control_method(struct acpi_walk_state *walk_state, + union acpi_operand_object *return_desc); + +void +acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_method_error(acpi_status status, struct acpi_walk_state *walk_state); + +/* + * dsinit + */ +acpi_status +acpi_ds_initialize_objects(u32 table_index, + struct acpi_namespace_node *start_node); + +/* + * dsobject - Parser/Interpreter interface - object initialization and conversion + */ +acpi_status +acpi_ds_build_internal_buffer_obj(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 buffer_length, + union acpi_operand_object **obj_desc_ptr); + +acpi_status +acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 package_length, + union acpi_operand_object **obj_desc); + +acpi_status +acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u16 opcode, union acpi_operand_object **obj_desc); + +acpi_status +acpi_ds_create_node(struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + union acpi_parse_object *op); + +/* + * dsutils - Parser/Interpreter interface utility routines + */ +void acpi_ds_clear_implicit_return(struct acpi_walk_state *walk_state); + +u8 +acpi_ds_do_implicit_return(union acpi_operand_object *return_desc, + struct acpi_walk_state *walk_state, + u8 add_reference); + +u8 +acpi_ds_is_result_used(union acpi_parse_object *op, + struct acpi_walk_state *walk_state); + +void +acpi_ds_delete_result_if_not_used(union acpi_parse_object *op, + union acpi_operand_object *result_obj, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_operand(struct acpi_walk_state *walk_state, + union acpi_parse_object *arg, u32 args_remaining); + +acpi_status +acpi_ds_create_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *first_arg); + +acpi_status acpi_ds_resolve_operands(struct acpi_walk_state *walk_state); + +void acpi_ds_clear_operands(struct acpi_walk_state *walk_state); + +acpi_status acpi_ds_evaluate_name_path(struct acpi_walk_state *walk_state); + +/* + * dswscope - Scope Stack manipulation + */ +acpi_status +acpi_ds_scope_stack_push(struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_walk_state *walk_state); + +acpi_status acpi_ds_scope_stack_pop(struct acpi_walk_state *walk_state); + +void acpi_ds_scope_stack_clear(struct acpi_walk_state *walk_state); + +/* + * dswstate - parser WALK_STATE management routines + */ +acpi_status +acpi_ds_obj_stack_push(void *object, struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_obj_stack_pop(u32 pop_count, struct acpi_walk_state *walk_state); + +struct acpi_walk_state * acpi_ds_create_walk_state(acpi_owner_id owner_id, + union acpi_parse_object + *origin, + union acpi_operand_object + *mth_desc, + struct acpi_thread_state + *thread); + +acpi_status +acpi_ds_init_aml_walk(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + struct acpi_namespace_node *method_node, + u8 * aml_start, + u32 aml_length, + struct acpi_evaluate_info *info, u8 pass_number); + +void +acpi_ds_obj_stack_pop_and_delete(u32 pop_count, + struct acpi_walk_state *walk_state); + +void acpi_ds_delete_walk_state(struct acpi_walk_state *walk_state); + +struct acpi_walk_state *acpi_ds_pop_walk_state(struct acpi_thread_state + *thread); + +void +acpi_ds_push_walk_state(struct acpi_walk_state *walk_state, + struct acpi_thread_state *thread); + +acpi_status acpi_ds_result_stack_clear(struct acpi_walk_state *walk_state); + +struct acpi_walk_state *acpi_ds_get_current_walk_state(struct acpi_thread_state + *thread); + +acpi_status +acpi_ds_result_pop(union acpi_operand_object **object, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_result_push(union acpi_operand_object *object, + struct acpi_walk_state *walk_state); + +#endif /* _ACDISPAT_H_ */ diff --git a/kernel/drivers/acpi/acpica/acevents.h b/kernel/drivers/acpi/acpica/acevents.h new file mode 100644 index 000000000..228704b78 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acevents.h @@ -0,0 +1,253 @@ +/****************************************************************************** + * + * Name: acevents.h - Event subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACEVENTS_H__ +#define __ACEVENTS_H__ + +/* + * evevent + */ +acpi_status acpi_ev_initialize_events(void); + +acpi_status acpi_ev_install_xrupt_handlers(void); + +u32 acpi_ev_fixed_event_detect(void); + +/* + * evmisc + */ +u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node); + +u32 acpi_ev_get_gpe_number_index(u32 gpe_number); + +acpi_status +acpi_ev_queue_notify_request(struct acpi_namespace_node *node, + u32 notify_value); + +/* + * evglock - Global Lock support + */ +acpi_status acpi_ev_init_global_lock_handler(void); + +ACPI_HW_DEPENDENT_RETURN_OK(acpi_status + acpi_ev_acquire_global_lock(u16 timeout)) +ACPI_HW_DEPENDENT_RETURN_OK(acpi_status acpi_ev_release_global_lock(void)) +acpi_status acpi_ev_remove_global_lock_handler(void); + +/* + * evgpe - Low-level GPE support + */ +u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list); + +acpi_status +acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info); + +struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, + u32 gpe_number); + +struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number, + struct acpi_gpe_block_info + *gpe_block); + +acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info); + +/* + * evgpeblk - Upper-level GPE block support + */ +acpi_status +acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, + u64 address, + u8 space_id, + u32 register_count, + u16 gpe_block_base_number, + u32 interrupt_number, + struct acpi_gpe_block_info **return_gpe_block); + +acpi_status +acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); + +ACPI_HW_DEPENDENT_RETURN_OK(acpi_status + acpi_ev_delete_gpe_block(struct acpi_gpe_block_info + *gpe_block)) + +u32 +acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, + struct acpi_gpe_event_info *gpe_event_info, + u32 gpe_number); + +/* + * evgpeinit - GPE initialization and update + */ +acpi_status acpi_ev_gpe_initialize(void); + +ACPI_HW_DEPENDENT_RETURN_VOID(void + acpi_ev_update_gpes(acpi_owner_id table_owner_id)) + +acpi_status +acpi_ev_match_gpe_method(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +/* + * evgpeutil - GPE utilities + */ +acpi_status +acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context); + +acpi_status +acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + +acpi_status +acpi_ev_get_gpe_xrupt_block(u32 interrupt_number, + struct acpi_gpe_xrupt_info **gpe_xrupt_block); + +acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt); + +acpi_status +acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); + +/* + * evhandler - Address space handling + */ +u8 +acpi_ev_has_default_handler(struct acpi_namespace_node *node, + acpi_adr_space_type space_id); + +acpi_status acpi_ev_install_region_handlers(void); + +acpi_status +acpi_ev_install_space_handler(struct acpi_namespace_node *node, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler, + acpi_adr_space_setup setup, void *context); + +/* + * evregion - Operation region support + */ +acpi_status acpi_ev_initialize_op_regions(void); + +acpi_status +acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + union acpi_operand_object *field_obj, + u32 function, + u32 region_offset, u32 bit_width, u64 *value); + +acpi_status +acpi_ev_attach_region(union acpi_operand_object *handler_obj, + union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked); + +void +acpi_ev_detach_region(union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked); + +acpi_status +acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, + acpi_adr_space_type space_id); + +acpi_status +acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function); + +/* + * evregini - Region initialization and setup + */ +acpi_status +acpi_ev_system_memory_region_setup(acpi_handle handle, + u32 function, + void *handler_context, + void **region_context); + +acpi_status +acpi_ev_io_space_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_pci_config_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_cmos_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_pci_bar_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_default_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_initialize_region(union acpi_operand_object *region_obj, + u8 acpi_ns_locked); + +/* + * evsci - SCI (System Control Interrupt) handling/dispatch + */ +u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context); + +u32 acpi_ev_sci_dispatch(void); + +u32 acpi_ev_install_sci_handler(void); + +acpi_status acpi_ev_remove_all_sci_handlers(void); + +ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_ev_terminate(void)) +#endif /* __ACEVENTS_H__ */ diff --git a/kernel/drivers/acpi/acpica/acglobal.h b/kernel/drivers/acpi/acpica/acglobal.h new file mode 100644 index 000000000..166ee9554 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acglobal.h @@ -0,0 +1,383 @@ +/****************************************************************************** + * + * Name: acglobal.h - Declarations for global variables + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACGLOBAL_H__ +#define __ACGLOBAL_H__ + +/***************************************************************************** + * + * Globals related to the ACPI tables + * + ****************************************************************************/ + +/* Master list of all ACPI tables that were found in the RSDT/XSDT */ + +ACPI_GLOBAL(struct acpi_table_list, acpi_gbl_root_table_list); + +/* DSDT information. Used to check for DSDT corruption */ + +ACPI_GLOBAL(struct acpi_table_header *, acpi_gbl_DSDT); +ACPI_GLOBAL(struct acpi_table_header, acpi_gbl_original_dsdt_header); + +#if (!ACPI_REDUCED_HARDWARE) +ACPI_GLOBAL(struct acpi_table_facs *, acpi_gbl_FACS); + +#endif /* !ACPI_REDUCED_HARDWARE */ + +/* These addresses are calculated from the FADT Event Block addresses */ + +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1a_status); +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1a_enable); + +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1b_status); +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1b_enable); + +/* + * Handle both ACPI 1.0 and ACPI 2.0+ Integer widths. The integer width is + * determined by the revision of the DSDT: If the DSDT revision is less than + * 2, use only the lower 32 bits of the internal 64-bit Integer. + */ +ACPI_GLOBAL(u8, acpi_gbl_integer_bit_width); +ACPI_GLOBAL(u8, acpi_gbl_integer_byte_width); +ACPI_GLOBAL(u8, acpi_gbl_integer_nybble_width); + +/***************************************************************************** + * + * Mutual exclusion within ACPICA subsystem + * + ****************************************************************************/ + +/* + * Predefined mutex objects. This array contains the + * actual OS mutex handles, indexed by the local ACPI_MUTEX_HANDLEs. + * (The table maps local handles to the real OS handles) + */ +ACPI_GLOBAL(struct acpi_mutex_info, acpi_gbl_mutex_info[ACPI_NUM_MUTEX]); + +/* + * Global lock mutex is an actual AML mutex object + * Global lock semaphore works in conjunction with the actual global lock + * Global lock spinlock is used for "pending" handshake + */ +ACPI_GLOBAL(union acpi_operand_object *, acpi_gbl_global_lock_mutex); +ACPI_GLOBAL(acpi_semaphore, acpi_gbl_global_lock_semaphore); +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_global_lock_pending_lock); +ACPI_GLOBAL(u16, acpi_gbl_global_lock_handle); +ACPI_GLOBAL(u8, acpi_gbl_global_lock_acquired); +ACPI_GLOBAL(u8, acpi_gbl_global_lock_present); +ACPI_GLOBAL(u8, acpi_gbl_global_lock_pending); + +/* + * Spinlocks are used for interfaces that can be possibly called at + * interrupt level + */ +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_gpe_lock); /* For GPE data structs and registers */ +ACPI_GLOBAL(acpi_raw_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */ +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_reference_count_lock); + +/* Mutex for _OSI support */ + +ACPI_GLOBAL(acpi_mutex, acpi_gbl_osi_mutex); + +/* Reader/Writer lock is used for namespace walk and dynamic table unload */ + +ACPI_GLOBAL(struct acpi_rw_lock, acpi_gbl_namespace_rw_lock); + +/***************************************************************************** + * + * Miscellaneous globals + * + ****************************************************************************/ + +/* Object caches */ + +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_namespace_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_state_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_ps_node_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_ps_node_ext_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_operand_cache); + +/* System */ + +ACPI_INIT_GLOBAL(u32, acpi_gbl_startup_flags, 0); +ACPI_INIT_GLOBAL(u8, acpi_gbl_shutdown, TRUE); + +/* Global handlers */ + +ACPI_GLOBAL(struct acpi_global_notify_handler, acpi_gbl_global_notify[2]); +ACPI_GLOBAL(acpi_exception_handler, acpi_gbl_exception_handler); +ACPI_GLOBAL(acpi_init_handler, acpi_gbl_init_handler); +ACPI_GLOBAL(acpi_table_handler, acpi_gbl_table_handler); +ACPI_GLOBAL(void *, acpi_gbl_table_handler_context); +ACPI_GLOBAL(acpi_interface_handler, acpi_gbl_interface_handler); +ACPI_GLOBAL(struct acpi_sci_handler_info *, acpi_gbl_sci_handler_list); + +/* Owner ID support */ + +ACPI_GLOBAL(u32, acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS]); +ACPI_GLOBAL(u8, acpi_gbl_last_owner_id_index); +ACPI_GLOBAL(u8, acpi_gbl_next_owner_id_offset); + +/* Initialization sequencing */ + +ACPI_GLOBAL(u8, acpi_gbl_reg_methods_executed); + +/* Misc */ + +ACPI_GLOBAL(u32, acpi_gbl_original_mode); +ACPI_GLOBAL(u32, acpi_gbl_ns_lookup_count); +ACPI_GLOBAL(u32, acpi_gbl_ps_find_count); +ACPI_GLOBAL(u16, acpi_gbl_pm1_enable_register_save); +ACPI_GLOBAL(u8, acpi_gbl_debugger_configuration); +ACPI_GLOBAL(u8, acpi_gbl_step_to_next_call); +ACPI_GLOBAL(u8, acpi_gbl_acpi_hardware_present); +ACPI_GLOBAL(u8, acpi_gbl_events_initialized); +ACPI_GLOBAL(struct acpi_interface_info *, acpi_gbl_supported_interfaces); +ACPI_GLOBAL(struct acpi_address_range *, + acpi_gbl_address_range_list[ACPI_ADDRESS_RANGE_MAX]); + +/* Other miscellaneous, declared and initialized in utglobal */ + +extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT]; +extern const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS]; +extern const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS]; +extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS]; +extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + +/* Lists for tracking memory allocations (debug only) */ + +ACPI_GLOBAL(struct acpi_memory_list *, acpi_gbl_global_list); +ACPI_GLOBAL(struct acpi_memory_list *, acpi_gbl_ns_node_list); +ACPI_GLOBAL(u8, acpi_gbl_display_final_mem_stats); +ACPI_GLOBAL(u8, acpi_gbl_disable_mem_tracking); +#endif + +/***************************************************************************** + * + * Namespace globals + * + ****************************************************************************/ + +#if !defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY) +#define NUM_PREDEFINED_NAMES 10 +#else +#define NUM_PREDEFINED_NAMES 9 +#endif + +ACPI_GLOBAL(struct acpi_namespace_node, acpi_gbl_root_node_struct); +ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_root_node); +ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_fadt_gpe_device); +ACPI_GLOBAL(union acpi_operand_object *, acpi_gbl_module_code_list); + +extern const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES]; +extern const struct acpi_predefined_names + acpi_gbl_pre_defined_names[NUM_PREDEFINED_NAMES]; + +#ifdef ACPI_DEBUG_OUTPUT +ACPI_GLOBAL(u32, acpi_gbl_current_node_count); +ACPI_GLOBAL(u32, acpi_gbl_current_node_size); +ACPI_GLOBAL(u32, acpi_gbl_max_concurrent_node_count); +ACPI_GLOBAL(acpi_size *, acpi_gbl_entry_stack_pointer); +ACPI_GLOBAL(acpi_size *, acpi_gbl_lowest_stack_pointer); +ACPI_GLOBAL(u32, acpi_gbl_deepest_nesting); +ACPI_INIT_GLOBAL(u32, acpi_gbl_nesting_level, 0); +#endif + +/***************************************************************************** + * + * Interpreter globals + * + ****************************************************************************/ + +ACPI_GLOBAL(struct acpi_thread_state *, acpi_gbl_current_walk_list); + +/* Control method single step flag */ + +ACPI_GLOBAL(u8, acpi_gbl_cm_single_step); + +/***************************************************************************** + * + * Hardware globals + * + ****************************************************************************/ + +extern struct acpi_bit_register_info + acpi_gbl_bit_register_info[ACPI_NUM_BITREG]; + +ACPI_GLOBAL(u8, acpi_gbl_sleep_type_a); +ACPI_GLOBAL(u8, acpi_gbl_sleep_type_b); + +/***************************************************************************** + * + * Event and GPE globals + * + ****************************************************************************/ + +#if (!ACPI_REDUCED_HARDWARE) + +ACPI_GLOBAL(u8, acpi_gbl_all_gpes_initialized); +ACPI_GLOBAL(struct acpi_gpe_xrupt_info *, acpi_gbl_gpe_xrupt_list_head); +ACPI_GLOBAL(struct acpi_gpe_block_info *, + acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]); +ACPI_GLOBAL(acpi_gbl_event_handler, acpi_gbl_global_event_handler); +ACPI_GLOBAL(void *, acpi_gbl_global_event_handler_context); +ACPI_GLOBAL(struct acpi_fixed_event_handler, + acpi_gbl_fixed_event_handlers[ACPI_NUM_FIXED_EVENTS]); + +extern struct acpi_fixed_event_info + acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS]; + +#endif /* !ACPI_REDUCED_HARDWARE */ + +/***************************************************************************** + * + * Debug support + * + ****************************************************************************/ + +/* Event counters */ + +ACPI_GLOBAL(u32, acpi_method_count); +ACPI_GLOBAL(u32, acpi_gpe_count); +ACPI_GLOBAL(u32, acpi_sci_count); +ACPI_GLOBAL(u32, acpi_fixed_event_count[ACPI_NUM_FIXED_EVENTS]); + +/* Support for dynamic control method tracing mechanism */ + +ACPI_GLOBAL(u32, acpi_gbl_original_dbg_level); +ACPI_GLOBAL(u32, acpi_gbl_original_dbg_layer); +ACPI_GLOBAL(u32, acpi_gbl_trace_dbg_level); +ACPI_GLOBAL(u32, acpi_gbl_trace_dbg_layer); + +/***************************************************************************** + * + * Debugger and Disassembler globals + * + ****************************************************************************/ + +ACPI_INIT_GLOBAL(u8, acpi_gbl_db_output_flags, ACPI_DB_CONSOLE_OUTPUT); + +#ifdef ACPI_DISASSEMBLER + +/* Do not disassemble buffers to resource descriptors */ + +ACPI_INIT_GLOBAL(u8, acpi_gbl_no_resource_disassembly, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_force_aml_disassembly, FALSE); + +ACPI_GLOBAL(u8, acpi_gbl_db_opt_disasm); +ACPI_GLOBAL(u8, acpi_gbl_db_opt_verbose); +ACPI_GLOBAL(u8, acpi_gbl_num_external_methods); +ACPI_GLOBAL(u32, acpi_gbl_resolved_external_methods); +ACPI_GLOBAL(struct acpi_external_list *, acpi_gbl_external_list); +ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list); +#endif + +#ifdef ACPI_DEBUGGER + +ACPI_INIT_GLOBAL(u8, acpi_gbl_db_terminate_threads, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE); + +ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods); +ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_region_support); +ACPI_GLOBAL(u8, acpi_gbl_db_output_to_file); +ACPI_GLOBAL(char *, acpi_gbl_db_buffer); +ACPI_GLOBAL(char *, acpi_gbl_db_filename); +ACPI_GLOBAL(u32, acpi_gbl_db_debug_level); +ACPI_GLOBAL(u32, acpi_gbl_db_console_debug_level); +ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_db_scope_node); + +ACPI_GLOBAL(char *, acpi_gbl_db_args[ACPI_DEBUGGER_MAX_ARGS]); +ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]); + +/* These buffers should all be the same size */ + +ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]); +ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]); +ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]); +ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]); + +/* + * Statistic globals + */ +ACPI_GLOBAL(u16, acpi_gbl_obj_type_count[ACPI_TYPE_NS_NODE_MAX + 1]); +ACPI_GLOBAL(u16, acpi_gbl_node_type_count[ACPI_TYPE_NS_NODE_MAX + 1]); +ACPI_GLOBAL(u16, acpi_gbl_obj_type_count_misc); +ACPI_GLOBAL(u16, acpi_gbl_node_type_count_misc); +ACPI_GLOBAL(u32, acpi_gbl_num_nodes); +ACPI_GLOBAL(u32, acpi_gbl_num_objects); + +#endif /* ACPI_DEBUGGER */ + +/***************************************************************************** + * + * Application globals + * + ****************************************************************************/ + +#ifdef ACPI_APPLICATION + +ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL); +ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_output_file, NULL); + +/* Print buffer */ + +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_print_lock); /* For print buffer */ +ACPI_GLOBAL(char, acpi_gbl_print_buffer[1024]); + +#endif /* ACPI_APPLICATION */ + +/***************************************************************************** + * + * Info/help support + * + ****************************************************************************/ + +extern const struct ah_predefined_name asl_predefined_info[]; +extern const struct ah_device_id asl_device_ids[]; + +#endif /* __ACGLOBAL_H__ */ diff --git a/kernel/drivers/acpi/acpica/achware.h b/kernel/drivers/acpi/acpica/achware.h new file mode 100644 index 000000000..196a55244 --- /dev/null +++ b/kernel/drivers/acpi/acpica/achware.h @@ -0,0 +1,150 @@ +/****************************************************************************** + * + * Name: achware.h -- hardware specific interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACHWARE_H__ +#define __ACHWARE_H__ + +/* Values for the _SST predefined method */ + +#define ACPI_SST_INDICATOR_OFF 0 +#define ACPI_SST_WORKING 1 +#define ACPI_SST_WAKING 2 +#define ACPI_SST_SLEEPING 3 +#define ACPI_SST_SLEEP_CONTEXT 4 + +/* + * hwacpi - high level functions + */ +acpi_status acpi_hw_set_mode(u32 mode); + +u32 acpi_hw_get_mode(void); + +/* + * hwregs - ACPI Register I/O + */ +acpi_status +acpi_hw_validate_register(struct acpi_generic_address *reg, + u8 max_bit_width, u64 *address); + +acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg); + +acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg); + +struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id); + +acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control); + +acpi_status acpi_hw_register_read(u32 register_id, u32 *return_value); + +acpi_status acpi_hw_register_write(u32 register_id, u32 value); + +acpi_status acpi_hw_clear_acpi_status(void); + +/* + * hwsleep - sleep/wake support (Legacy sleep registers) + */ +acpi_status acpi_hw_legacy_sleep(u8 sleep_state); + +acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state); + +acpi_status acpi_hw_legacy_wake(u8 sleep_state); + +/* + * hwesleep - sleep/wake support (Extended FADT-V5 sleep registers) + */ +void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument); + +acpi_status acpi_hw_extended_sleep(u8 sleep_state); + +acpi_status acpi_hw_extended_wake_prep(u8 sleep_state); + +acpi_status acpi_hw_extended_wake(u8 sleep_state); + +/* + * hwvalid - Port I/O with validation + */ +acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width); + +acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width); + +/* + * hwgpe - GPE support + */ +u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action); + +acpi_status +acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + +acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + +acpi_status +acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, + acpi_event_status *event_status); + +acpi_status acpi_hw_disable_all_gpes(void); + +acpi_status acpi_hw_enable_all_runtime_gpes(void); + +acpi_status acpi_hw_enable_all_wakeup_gpes(void); + +acpi_status +acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); + +/* + * hwpci - PCI configuration support + */ +acpi_status +acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id, + acpi_handle root_pci_device, acpi_handle pci_region); + +#endif /* __ACHWARE_H__ */ diff --git a/kernel/drivers/acpi/acpica/acinterp.h b/kernel/drivers/acpi/acpica/acinterp.h new file mode 100644 index 000000000..1886bde54 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acinterp.h @@ -0,0 +1,534 @@ +/****************************************************************************** + * + * Name: acinterp.h - Interpreter subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACINTERP_H__ +#define __ACINTERP_H__ + +#define ACPI_WALK_OPERANDS (&(walk_state->operands [walk_state->num_operands -1])) + +/* Macros for tables used for debug output */ + +#define ACPI_EXD_OFFSET(f) (u8) ACPI_OFFSET (union acpi_operand_object,f) +#define ACPI_EXD_NSOFFSET(f) (u8) ACPI_OFFSET (struct acpi_namespace_node,f) +#define ACPI_EXD_TABLE_SIZE(name) (sizeof(name) / sizeof (struct acpi_exdump_info)) + +/* + * If possible, pack the following structures to byte alignment, since we + * don't care about performance for debug output. Two cases where we cannot + * pack the structures: + * + * 1) Hardware does not support misaligned memory transfers + * 2) Compiler does not support pointers within packed structures + */ +#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED)) +#pragma pack(1) +#endif + +typedef const struct acpi_exdump_info { + u8 opcode; + u8 offset; + char *name; + +} acpi_exdump_info; + +/* Values for the Opcode field above */ + +#define ACPI_EXD_INIT 0 +#define ACPI_EXD_TYPE 1 +#define ACPI_EXD_UINT8 2 +#define ACPI_EXD_UINT16 3 +#define ACPI_EXD_UINT32 4 +#define ACPI_EXD_UINT64 5 +#define ACPI_EXD_LITERAL 6 +#define ACPI_EXD_POINTER 7 +#define ACPI_EXD_ADDRESS 8 +#define ACPI_EXD_STRING 9 +#define ACPI_EXD_BUFFER 10 +#define ACPI_EXD_PACKAGE 11 +#define ACPI_EXD_FIELD 12 +#define ACPI_EXD_REFERENCE 13 +#define ACPI_EXD_LIST 14 /* Operand object list */ +#define ACPI_EXD_HDLR_LIST 15 /* Address Handler list */ +#define ACPI_EXD_RGN_LIST 16 /* Region list */ +#define ACPI_EXD_NODE 17 /* Namespace Node */ + +/* restore default alignment */ + +#pragma pack() + +/* + * exconvrt - object conversion + */ +acpi_status +acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc, u32 flags); + +acpi_status +acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc); + +acpi_status +acpi_ex_convert_to_string(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc, u32 type); + +/* Types for ->String conversion */ + +#define ACPI_EXPLICIT_BYTE_COPY 0x00000000 +#define ACPI_EXPLICIT_CONVERT_HEX 0x00000001 +#define ACPI_IMPLICIT_CONVERT_HEX 0x00000002 +#define ACPI_EXPLICIT_CONVERT_DECIMAL 0x00000003 + +acpi_status +acpi_ex_convert_to_target_type(acpi_object_type destination_type, + union acpi_operand_object *source_desc, + union acpi_operand_object **result_desc, + struct acpi_walk_state *walk_state); + +/* + * exdebug - AML debug object + */ +void +acpi_ex_do_debug_object(union acpi_operand_object *source_desc, + u32 level, u32 index); + +/* + * exfield - ACPI AML (p-code) execution - field manipulation + */ +acpi_status +acpi_ex_common_buffer_setup(union acpi_operand_object *obj_desc, + u32 buffer_length, u32 * datum_count); + +acpi_status +acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc, + u64 mask, + u64 field_value, u32 field_datum_byte_offset); + +void +acpi_ex_get_buffer_datum(u64 *datum, + void *buffer, + u32 buffer_length, + u32 byte_granularity, u32 buffer_offset); + +void +acpi_ex_set_buffer_datum(u64 merged_datum, + void *buffer, + u32 buffer_length, + u32 byte_granularity, u32 buffer_offset); + +acpi_status +acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, + union acpi_operand_object *obj_desc, + union acpi_operand_object **ret_buffer_desc); + +acpi_status +acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, + union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc); + +/* + * exfldio - low level field I/O + */ +acpi_status +acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, + void *buffer, u32 buffer_length); + +acpi_status +acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, + void *buffer, u32 buffer_length); + +acpi_status +acpi_ex_access_region(union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset, u64 *value, u32 read_write); + +/* + * exmisc - misc support routines + */ +acpi_status +acpi_ex_get_object_reference(union acpi_operand_object *obj_desc, + union acpi_operand_object **return_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_concat_template(union acpi_operand_object *obj_desc, + union acpi_operand_object *obj_desc2, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_do_concatenate(union acpi_operand_object *obj_desc, + union acpi_operand_object *obj_desc2, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_do_logical_numeric_op(u16 opcode, + u64 integer0, u64 integer1, u8 *logical_result); + +acpi_status +acpi_ex_do_logical_op(u16 opcode, + union acpi_operand_object *operand0, + union acpi_operand_object *operand1, u8 *logical_result); + +u64 acpi_ex_do_math_op(u16 opcode, u64 operand0, u64 operand1); + +acpi_status acpi_ex_create_mutex(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_processor(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_power_resource(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_create_region(u8 * aml_start, + u32 aml_length, + u8 region_space, struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_event(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_create_method(u8 * aml_start, + u32 aml_length, struct acpi_walk_state *walk_state); + +/* + * exconfig - dynamic table load/unload + */ +acpi_status +acpi_ex_load_op(union acpi_operand_object *obj_desc, + union acpi_operand_object *target, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_load_table_op(struct acpi_walk_state *walk_state, + union acpi_operand_object **return_desc); + +acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle); + +/* + * exmutex - mutex support + */ +acpi_status +acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_acquire_mutex_object(u16 timeout, + union acpi_operand_object *obj_desc, + acpi_thread_id thread_id); + +acpi_status +acpi_ex_release_mutex(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_release_mutex_object(union acpi_operand_object *obj_desc); + +void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread); + +void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc); + +/* + * exprep - ACPI AML execution - prep utilities + */ +acpi_status +acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc, + u8 field_flags, + u8 field_attribute, + u32 field_bit_position, u32 field_bit_length); + +acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info); + +/* + * exsystem - Interface to OS services + */ +acpi_status +acpi_ex_system_do_notify_op(union acpi_operand_object *value, + union acpi_operand_object *obj_desc); + +acpi_status acpi_ex_system_do_sleep(u64 time); + +acpi_status acpi_ex_system_do_stall(u32 time); + +acpi_status acpi_ex_system_signal_event(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ex_system_wait_event(union acpi_operand_object *time, + union acpi_operand_object *obj_desc); + +acpi_status acpi_ex_system_reset_event(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout); + +acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout); + +/* + * exoparg1 - ACPI AML execution, 1 operand + */ +acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_1T_0R(struct acpi_walk_state *walk_state); + +/* + * exoparg2 - ACPI AML execution, 2 operands + */ +acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state); + +/* + * exoparg3 - ACPI AML execution, 3 operands + */ +acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state); + +/* + * exoparg6 - ACPI AML execution, 6 operands + */ +acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state *walk_state); + +/* + * exresolv - Object resolution and get value functions + */ +acpi_status +acpi_ex_resolve_to_value(union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state, + union acpi_operand_object *operand, + acpi_object_type * return_type, + union acpi_operand_object **return_desc); + +/* + * exresnte - resolve namespace node + */ +acpi_status +acpi_ex_resolve_node_to_value(struct acpi_namespace_node **stack_ptr, + struct acpi_walk_state *walk_state); + +/* + * exresop - resolve operand to value + */ +acpi_status +acpi_ex_resolve_operands(u16 opcode, + union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state); + +/* + * exdump - Interpreter debug output routines + */ +void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth); + +void +acpi_ex_dump_operands(union acpi_operand_object **operands, + const char *opcode_name, u32 num_opcodes); + +#ifdef ACPI_FUTURE_USAGE +void +acpi_ex_dump_object_descriptor(union acpi_operand_object *object, u32 flags); + +void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags); +#endif /* ACPI_FUTURE_USAGE */ + +/* + * exnames - AML namestring support + */ +acpi_status +acpi_ex_get_name_string(acpi_object_type data_type, + u8 * in_aml_address, + char **out_name_string, u32 * out_name_length); + +/* + * exstore - Object store support + */ +acpi_status +acpi_ex_store(union acpi_operand_object *val_desc, + union acpi_operand_object *dest_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, + struct acpi_namespace_node *node, + struct acpi_walk_state *walk_state, + u8 implicit_conversion); + +#define ACPI_IMPLICIT_CONVERSION TRUE +#define ACPI_NO_IMPLICIT_CONVERSION FALSE + +/* + * exstoren - resolve/store object + */ +acpi_status +acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, + acpi_object_type target_type, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_store_object_to_object(union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc, + union acpi_operand_object **new_desc, + struct acpi_walk_state *walk_state); + +/* + * exstorob - store object - buffer/string + */ +acpi_status +acpi_ex_store_buffer_to_buffer(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +acpi_status +acpi_ex_store_string_to_string(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +/* + * excopy - object copy + */ +acpi_status +acpi_ex_copy_integer_to_index_field(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +acpi_status +acpi_ex_copy_integer_to_bank_field(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +acpi_status +acpi_ex_copy_data_to_named_field(union acpi_operand_object *source_desc, + struct acpi_namespace_node *node); + +acpi_status +acpi_ex_copy_integer_to_buffer_field(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +/* + * exutils - interpreter/scanner utilities + */ +void acpi_ex_enter_interpreter(void); + +void acpi_ex_exit_interpreter(void); + +u8 acpi_ex_truncate_for32bit_table(union acpi_operand_object *obj_desc); + +void acpi_ex_acquire_global_lock(u32 rule); + +void acpi_ex_release_global_lock(u32 rule); + +void acpi_ex_eisa_id_to_string(char *dest, u64 compressed_id); + +void acpi_ex_integer_to_string(char *dest, u64 value); + +u8 acpi_is_valid_space_id(u8 space_id); + +/* + * exregion - default op_region handlers + */ +acpi_status +acpi_ex_system_memory_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, + void *region_context); + +acpi_status +acpi_ex_system_io_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_pci_config_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_cmos_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_pci_bar_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_embedded_controller_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, + void *region_context); + +acpi_status +acpi_ex_sm_bus_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_data_table_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context); + +#endif /* __INTERP_H__ */ diff --git a/kernel/drivers/acpi/acpica/aclocal.h b/kernel/drivers/acpi/acpica/aclocal.h new file mode 100644 index 000000000..87b27521f --- /dev/null +++ b/kernel/drivers/acpi/acpica/aclocal.h @@ -0,0 +1,1168 @@ +/****************************************************************************** + * + * Name: aclocal.h - Internal data types used across the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACLOCAL_H__ +#define __ACLOCAL_H__ + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +#define ACPI_SERIALIZED 0xFF + +typedef u32 acpi_mutex_handle; +#define ACPI_GLOBAL_LOCK (acpi_semaphore) (-1) + +/* Total number of aml opcodes defined */ + +#define AML_NUM_OPCODES 0x82 + +/* Forward declarations */ + +struct acpi_walk_state; +struct acpi_obj_mutex; +union acpi_parse_object; + +/***************************************************************************** + * + * Mutex typedefs and structs + * + ****************************************************************************/ + +/* + * Predefined handles for the mutex objects used within the subsystem + * All mutex objects are automatically created by acpi_ut_mutex_initialize. + * + * The acquire/release ordering protocol is implied via this list. Mutexes + * with a lower value must be acquired before mutexes with a higher value. + * + * NOTE: any changes here must be reflected in the acpi_gbl_mutex_names + * table below also! + */ +#define ACPI_MTX_INTERPRETER 0 /* AML Interpreter, main lock */ +#define ACPI_MTX_NAMESPACE 1 /* ACPI Namespace */ +#define ACPI_MTX_TABLES 2 /* Data for ACPI tables */ +#define ACPI_MTX_EVENTS 3 /* Data for ACPI events */ +#define ACPI_MTX_CACHES 4 /* Internal caches, general purposes */ +#define ACPI_MTX_MEMORY 5 /* Debug memory tracking lists */ +#define ACPI_MTX_DEBUG_CMD_COMPLETE 6 /* AML debugger */ +#define ACPI_MTX_DEBUG_CMD_READY 7 /* AML debugger */ + +#define ACPI_MAX_MUTEX 7 +#define ACPI_NUM_MUTEX ACPI_MAX_MUTEX+1 + +/* Lock structure for reader/writer interfaces */ + +struct acpi_rw_lock { + acpi_mutex writer_mutex; + acpi_mutex reader_mutex; + u32 num_readers; +}; + +/* + * Predefined handles for spinlocks used within the subsystem. + * These spinlocks are created by acpi_ut_mutex_initialize + */ +#define ACPI_LOCK_GPES 0 +#define ACPI_LOCK_HARDWARE 1 + +#define ACPI_MAX_LOCK 1 +#define ACPI_NUM_LOCK ACPI_MAX_LOCK+1 + +/* This Thread ID means that the mutex is not in use (unlocked) */ + +#define ACPI_MUTEX_NOT_ACQUIRED (acpi_thread_id) 0 + +/* Table for the global mutexes */ + +struct acpi_mutex_info { + acpi_mutex mutex; + u32 use_count; + acpi_thread_id thread_id; +}; + +/* Lock flag parameter for various interfaces */ + +#define ACPI_MTX_DO_NOT_LOCK 0 +#define ACPI_MTX_LOCK 1 + +/* Field access granularities */ + +#define ACPI_FIELD_BYTE_GRANULARITY 1 +#define ACPI_FIELD_WORD_GRANULARITY 2 +#define ACPI_FIELD_DWORD_GRANULARITY 4 +#define ACPI_FIELD_QWORD_GRANULARITY 8 + +#define ACPI_ENTRY_NOT_FOUND NULL + +/***************************************************************************** + * + * Namespace typedefs and structs + * + ****************************************************************************/ + +/* Operational modes of the AML interpreter/scanner */ + +typedef enum { + ACPI_IMODE_LOAD_PASS1 = 0x01, + ACPI_IMODE_LOAD_PASS2 = 0x02, + ACPI_IMODE_EXECUTE = 0x03 +} acpi_interpreter_mode; + +/* + * The Namespace Node describes a named object that appears in the AML. + * descriptor_type is used to differentiate between internal descriptors. + * + * The node is optimized for both 32-bit and 64-bit platforms: + * 20 bytes for the 32-bit case, 32 bytes for the 64-bit case. + * + * Note: The descriptor_type and Type fields must appear in the identical + * position in both the struct acpi_namespace_node and union acpi_operand_object + * structures. + */ +struct acpi_namespace_node { + union acpi_operand_object *object; /* Interpreter object */ + u8 descriptor_type; /* Differentiate object descriptor types */ + u8 type; /* ACPI Type associated with this name */ + u8 flags; /* Miscellaneous flags */ + acpi_owner_id owner_id; /* Node creator */ + union acpi_name_union name; /* ACPI Name, always 4 chars per ACPI spec */ + struct acpi_namespace_node *parent; /* Parent node */ + struct acpi_namespace_node *child; /* First child */ + struct acpi_namespace_node *peer; /* First peer */ + + /* + * The following fields are used by the ASL compiler and disassembler only + */ +#ifdef ACPI_LARGE_NAMESPACE_NODE + union acpi_parse_object *op; + u32 value; + u32 length; +#endif +}; + +/* Namespace Node flags */ + +#define ANOBJ_RESERVED 0x01 /* Available for use */ +#define ANOBJ_TEMPORARY 0x02 /* Node is create by a method and is temporary */ +#define ANOBJ_METHOD_ARG 0x04 /* Node is a method argument */ +#define ANOBJ_METHOD_LOCAL 0x08 /* Node is a method local */ +#define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */ +#define ANOBJ_EVALUATED 0x20 /* Set on first evaluation of node */ +#define ANOBJ_ALLOCATED_BUFFER 0x40 /* Method AML buffer is dynamic (install_method) */ + +#define ANOBJ_IS_EXTERNAL 0x08 /* iASL only: This object created via External() */ +#define ANOBJ_METHOD_NO_RETVAL 0x10 /* iASL only: Method has no return value */ +#define ANOBJ_METHOD_SOME_NO_RETVAL 0x20 /* iASL only: Method has at least one return value */ +#define ANOBJ_IS_REFERENCED 0x80 /* iASL only: Object was referenced */ + +/* Internal ACPI table management - master table list */ + +struct acpi_table_list { + struct acpi_table_desc *tables; /* Table descriptor array */ + u32 current_table_count; /* Tables currently in the array */ + u32 max_table_count; /* Max tables array will hold */ + u8 flags; +}; + +/* Flags for above */ + +#define ACPI_ROOT_ORIGIN_UNKNOWN (0) /* ~ORIGIN_ALLOCATED */ +#define ACPI_ROOT_ORIGIN_ALLOCATED (1) +#define ACPI_ROOT_ALLOW_RESIZE (2) + +/* Predefined (fixed) table indexes */ + +#define ACPI_TABLE_INDEX_DSDT (0) +#define ACPI_TABLE_INDEX_FACS (1) + +struct acpi_find_context { + char *search_for; + acpi_handle *list; + u32 *count; +}; + +struct acpi_ns_search_data { + struct acpi_namespace_node *node; +}; + +/* Object types used during package copies */ + +#define ACPI_COPY_TYPE_SIMPLE 0 +#define ACPI_COPY_TYPE_PACKAGE 1 + +/* Info structure used to convert external<->internal namestrings */ + +struct acpi_namestring_info { + const char *external_name; + const char *next_external_char; + char *internal_name; + u32 length; + u32 num_segments; + u32 num_carats; + u8 fully_qualified; +}; + +/* Field creation info */ + +struct acpi_create_field_info { + struct acpi_namespace_node *region_node; + struct acpi_namespace_node *field_node; + struct acpi_namespace_node *register_node; + struct acpi_namespace_node *data_register_node; + struct acpi_namespace_node *connection_node; + u8 *resource_buffer; + u32 bank_value; + u32 field_bit_position; + u32 field_bit_length; + u16 resource_length; + u16 pin_number_index; + u8 field_flags; + u8 attribute; + u8 field_type; + u8 access_length; +}; + +typedef +acpi_status(*acpi_internal_method) (struct acpi_walk_state * walk_state); + +/* + * Bitmapped ACPI types. Used internally only + */ +#define ACPI_BTYPE_ANY 0x00000000 +#define ACPI_BTYPE_INTEGER 0x00000001 +#define ACPI_BTYPE_STRING 0x00000002 +#define ACPI_BTYPE_BUFFER 0x00000004 +#define ACPI_BTYPE_PACKAGE 0x00000008 +#define ACPI_BTYPE_FIELD_UNIT 0x00000010 +#define ACPI_BTYPE_DEVICE 0x00000020 +#define ACPI_BTYPE_EVENT 0x00000040 +#define ACPI_BTYPE_METHOD 0x00000080 +#define ACPI_BTYPE_MUTEX 0x00000100 +#define ACPI_BTYPE_REGION 0x00000200 +#define ACPI_BTYPE_POWER 0x00000400 +#define ACPI_BTYPE_PROCESSOR 0x00000800 +#define ACPI_BTYPE_THERMAL 0x00001000 +#define ACPI_BTYPE_BUFFER_FIELD 0x00002000 +#define ACPI_BTYPE_DDB_HANDLE 0x00004000 +#define ACPI_BTYPE_DEBUG_OBJECT 0x00008000 +#define ACPI_BTYPE_REFERENCE 0x00010000 +#define ACPI_BTYPE_RESOURCE 0x00020000 + +#define ACPI_BTYPE_COMPUTE_DATA (ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_BUFFER) + +#define ACPI_BTYPE_DATA (ACPI_BTYPE_COMPUTE_DATA | ACPI_BTYPE_PACKAGE) +#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE | ACPI_BTYPE_DDB_HANDLE) +#define ACPI_BTYPE_DEVICE_OBJECTS (ACPI_BTYPE_DEVICE | ACPI_BTYPE_THERMAL | ACPI_BTYPE_PROCESSOR) +#define ACPI_BTYPE_OBJECTS_AND_REFS 0x0001FFFF /* ARG or LOCAL */ +#define ACPI_BTYPE_ALL_OBJECTS 0x0000FFFF + +#pragma pack(1) + +/* + * Information structure for ACPI predefined names. + * Each entry in the table contains the following items: + * + * name - The ACPI reserved name + * param_count - Number of arguments to the method + * expected_return_btypes - Allowed type(s) for the return value + */ +struct acpi_name_info { + char name[ACPI_NAME_SIZE]; + u16 argument_list; + u8 expected_btypes; +}; + +/* + * Secondary information structures for ACPI predefined objects that return + * package objects. This structure appears as the next entry in the table + * after the NAME_INFO structure above. + * + * The reason for this is to minimize the size of the predefined name table. + */ + +/* + * Used for ACPI_PTYPE1_FIXED, ACPI_PTYPE1_VAR, ACPI_PTYPE2, + * ACPI_PTYPE2_MIN, ACPI_PTYPE2_PKG_COUNT, ACPI_PTYPE2_COUNT, + * ACPI_PTYPE2_FIX_VAR + */ +struct acpi_package_info { + u8 type; + u8 object_type1; + u8 count1; + u8 object_type2; + u8 count2; + u16 reserved; +}; + +/* Used for ACPI_PTYPE2_FIXED */ + +struct acpi_package_info2 { + u8 type; + u8 count; + u8 object_type[4]; + u8 reserved; +}; + +/* Used for ACPI_PTYPE1_OPTION */ + +struct acpi_package_info3 { + u8 type; + u8 count; + u8 object_type[2]; + u8 tail_object_type; + u16 reserved; +}; + +union acpi_predefined_info { + struct acpi_name_info info; + struct acpi_package_info ret_info; + struct acpi_package_info2 ret_info2; + struct acpi_package_info3 ret_info3; +}; + +/* Reset to default packing */ + +#pragma pack() + +/* Return object auto-repair info */ + +typedef acpi_status(*acpi_object_converter) (union acpi_operand_object + *original_object, + union acpi_operand_object + **converted_object); + +struct acpi_simple_repair_info { + char name[ACPI_NAME_SIZE]; + u32 unexpected_btypes; + u32 package_index; + acpi_object_converter object_converter; +}; + +/* + * Bitmapped return value types + * Note: the actual data types must be contiguous, a loop in nspredef.c + * depends on this. + */ +#define ACPI_RTYPE_ANY 0x00 +#define ACPI_RTYPE_NONE 0x01 +#define ACPI_RTYPE_INTEGER 0x02 +#define ACPI_RTYPE_STRING 0x04 +#define ACPI_RTYPE_BUFFER 0x08 +#define ACPI_RTYPE_PACKAGE 0x10 +#define ACPI_RTYPE_REFERENCE 0x20 +#define ACPI_RTYPE_ALL 0x3F + +#define ACPI_NUM_RTYPES 5 /* Number of actual object types */ + +/***************************************************************************** + * + * Event typedefs and structs + * + ****************************************************************************/ + +/* Dispatch info for each host-installed SCI handler */ + +struct acpi_sci_handler_info { + struct acpi_sci_handler_info *next; + acpi_sci_handler address; /* Address of handler */ + void *context; /* Context to be passed to handler */ +}; + +/* Dispatch info for each GPE -- either a method or handler, cannot be both */ + +struct acpi_gpe_handler_info { + acpi_gpe_handler address; /* Address of handler, if any */ + void *context; /* Context to be passed to handler */ + struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ + u8 original_flags; /* Original (pre-handler) GPE info */ + u8 originally_enabled; /* True if GPE was originally enabled */ +}; + +/* Notify info for implicit notify, multiple device objects */ + +struct acpi_gpe_notify_info { + struct acpi_namespace_node *device_node; /* Device to be notified */ + struct acpi_gpe_notify_info *next; +}; + +/* + * GPE dispatch info. At any time, the GPE can have at most one type + * of dispatch - Method, Handler, or Implicit Notify. + */ +union acpi_gpe_dispatch_info { + struct acpi_namespace_node *method_node; /* Method node for this GPE level */ + struct acpi_gpe_handler_info *handler; /* Installed GPE handler */ + struct acpi_gpe_notify_info *notify_list; /* List of _PRW devices for implicit notifies */ +}; + +/* + * Information about a GPE, one per each GPE in an array. + * NOTE: Important to keep this struct as small as possible. + */ +struct acpi_gpe_event_info { + union acpi_gpe_dispatch_info dispatch; /* Either Method, Handler, or notify_list */ + struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ + u8 flags; /* Misc info about this GPE */ + u8 gpe_number; /* This GPE */ + u8 runtime_count; /* References to a run GPE */ +}; + +/* Information about a GPE register pair, one per each status/enable pair in an array */ + +struct acpi_gpe_register_info { + struct acpi_generic_address status_address; /* Address of status reg */ + struct acpi_generic_address enable_address; /* Address of enable reg */ + u16 base_gpe_number; /* Base GPE number for this register */ + u8 enable_for_wake; /* GPEs to keep enabled when sleeping */ + u8 enable_for_run; /* GPEs to keep enabled when running */ + u8 enable_mask; /* Current mask of enabled GPEs */ +}; + +/* + * Information about a GPE register block, one per each installed block -- + * GPE0, GPE1, and one per each installed GPE Block Device. + */ +struct acpi_gpe_block_info { + struct acpi_namespace_node *node; + struct acpi_gpe_block_info *previous; + struct acpi_gpe_block_info *next; + struct acpi_gpe_xrupt_info *xrupt_block; /* Backpointer to interrupt block */ + struct acpi_gpe_register_info *register_info; /* One per GPE register pair */ + struct acpi_gpe_event_info *event_info; /* One for each GPE */ + u64 address; /* Base address of the block */ + u32 register_count; /* Number of register pairs in block */ + u16 gpe_count; /* Number of individual GPEs in block */ + u16 block_base_number; /* Base GPE number for this block */ + u8 space_id; + u8 initialized; /* TRUE if this block is initialized */ +}; + +/* Information about GPE interrupt handlers, one per each interrupt level used for GPEs */ + +struct acpi_gpe_xrupt_info { + struct acpi_gpe_xrupt_info *previous; + struct acpi_gpe_xrupt_info *next; + struct acpi_gpe_block_info *gpe_block_list_head; /* List of GPE blocks for this xrupt */ + u32 interrupt_number; /* System interrupt number */ +}; + +struct acpi_gpe_walk_info { + struct acpi_namespace_node *gpe_device; + struct acpi_gpe_block_info *gpe_block; + u16 count; + acpi_owner_id owner_id; + u8 execute_by_owner_id; +}; + +struct acpi_gpe_device_info { + u32 index; + u32 next_block_base_index; + acpi_status status; + struct acpi_namespace_node *gpe_device; +}; + +typedef acpi_status(*acpi_gpe_callback) (struct acpi_gpe_xrupt_info * + gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); + +/* Information about each particular fixed event */ + +struct acpi_fixed_event_handler { + acpi_event_handler handler; /* Address of handler. */ + void *context; /* Context to be passed to handler */ +}; + +struct acpi_fixed_event_info { + u8 status_register_id; + u8 enable_register_id; + u16 status_bit_mask; + u16 enable_bit_mask; +}; + +/* Information used during field processing */ + +struct acpi_field_info { + u8 skip_field; + u8 field_flag; + u32 pkg_length; +}; + +/***************************************************************************** + * + * Generic "state" object for stacks + * + ****************************************************************************/ + +#define ACPI_CONTROL_NORMAL 0xC0 +#define ACPI_CONTROL_CONDITIONAL_EXECUTING 0xC1 +#define ACPI_CONTROL_PREDICATE_EXECUTING 0xC2 +#define ACPI_CONTROL_PREDICATE_FALSE 0xC3 +#define ACPI_CONTROL_PREDICATE_TRUE 0xC4 + +#define ACPI_STATE_COMMON \ + void *next; \ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 flags; \ + u16 value; \ + u16 state; + + /* There are 2 bytes available here until the next natural alignment boundary */ + +struct acpi_common_state { +ACPI_STATE_COMMON}; + +/* + * Update state - used to traverse complex objects such as packages + */ +struct acpi_update_state { + ACPI_STATE_COMMON union acpi_operand_object *object; +}; + +/* + * Pkg state - used to traverse nested package structures + */ +struct acpi_pkg_state { + ACPI_STATE_COMMON u16 index; + union acpi_operand_object *source_object; + union acpi_operand_object *dest_object; + struct acpi_walk_state *walk_state; + void *this_target_obj; + u32 num_packages; +}; + +/* + * Control state - one per if/else and while constructs. + * Allows nesting of these constructs + */ +struct acpi_control_state { + ACPI_STATE_COMMON u16 opcode; + union acpi_parse_object *predicate_op; + u8 *aml_predicate_start; /* Start of if/while predicate */ + u8 *package_end; /* End of if/while block */ + u32 loop_count; /* While() loop counter */ +}; + +/* + * Scope state - current scope during namespace lookups + */ +struct acpi_scope_state { + ACPI_STATE_COMMON struct acpi_namespace_node *node; +}; + +struct acpi_pscope_state { + ACPI_STATE_COMMON u32 arg_count; /* Number of fixed arguments */ + union acpi_parse_object *op; /* Current op being parsed */ + u8 *arg_end; /* Current argument end */ + u8 *pkg_end; /* Current package end */ + u32 arg_list; /* Next argument to parse */ +}; + +/* + * Thread state - one per thread across multiple walk states. Multiple walk + * states are created when there are nested control methods executing. + */ +struct acpi_thread_state { + ACPI_STATE_COMMON u8 current_sync_level; /* Mutex Sync (nested acquire) level */ + struct acpi_walk_state *walk_state_list; /* Head of list of walk_states for this thread */ + union acpi_operand_object *acquired_mutex_list; /* List of all currently acquired mutexes */ + acpi_thread_id thread_id; /* Running thread ID */ +}; + +/* + * Result values - used to accumulate the results of nested + * AML arguments + */ +struct acpi_result_values { + ACPI_STATE_COMMON + union acpi_operand_object *obj_desc[ACPI_RESULTS_FRAME_OBJ_NUM]; +}; + +typedef +acpi_status(*acpi_parse_downwards) (struct acpi_walk_state * walk_state, + union acpi_parse_object ** out_op); + +typedef acpi_status(*acpi_parse_upwards) (struct acpi_walk_state * walk_state); + +/* Global handlers for AML Notifies */ + +struct acpi_global_notify_handler { + acpi_notify_handler handler; + void *context; +}; + +/* + * Notify info - used to pass info to the deferred notify + * handler/dispatcher. + */ +struct acpi_notify_info { + ACPI_STATE_COMMON u8 handler_list_id; + struct acpi_namespace_node *node; + union acpi_operand_object *handler_list_head; + struct acpi_global_notify_handler *global; +}; + +/* Generic state is union of structs above */ + +union acpi_generic_state { + struct acpi_common_state common; + struct acpi_control_state control; + struct acpi_update_state update; + struct acpi_scope_state scope; + struct acpi_pscope_state parse_scope; + struct acpi_pkg_state pkg; + struct acpi_thread_state thread; + struct acpi_result_values results; + struct acpi_notify_info notify; +}; + +/***************************************************************************** + * + * Interpreter typedefs and structs + * + ****************************************************************************/ + +typedef acpi_status(*acpi_execute_op) (struct acpi_walk_state * walk_state); + +/* Address Range info block */ + +struct acpi_address_range { + struct acpi_address_range *next; + struct acpi_namespace_node *region_node; + acpi_physical_address start_address; + acpi_physical_address end_address; +}; + +/***************************************************************************** + * + * Parser typedefs and structs + * + ****************************************************************************/ + +/* + * AML opcode, name, and argument layout + */ +struct acpi_opcode_info { +#if defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUG_OUTPUT) + char *name; /* Opcode name (disassembler/debug only) */ +#endif + u32 parse_args; /* Grammar/Parse time arguments */ + u32 runtime_args; /* Interpret time arguments */ + u16 flags; /* Misc flags */ + u8 object_type; /* Corresponding internal object type */ + u8 class; /* Opcode class */ + u8 type; /* Opcode type */ +}; + +/* Value associated with the parse object */ + +union acpi_parse_value { + u64 integer; /* Integer constant (Up to 64 bits) */ + u32 size; /* bytelist or field size */ + char *string; /* NULL terminated string */ + u8 *buffer; /* buffer or string */ + char *name; /* NULL terminated string */ + union acpi_parse_object *arg; /* arguments and contained ops */ +}; + +#ifdef ACPI_DISASSEMBLER +#define ACPI_DISASM_ONLY_MEMBERS(a) a; +#else +#define ACPI_DISASM_ONLY_MEMBERS(a) +#endif + +#define ACPI_PARSE_COMMON \ + union acpi_parse_object *parent; /* Parent op */\ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 flags; /* Type of Op */\ + u16 aml_opcode; /* AML opcode */\ + u32 aml_offset; /* Offset of declaration in AML */\ + union acpi_parse_object *next; /* Next op */\ + struct acpi_namespace_node *node; /* For use by interpreter */\ + union acpi_parse_value value; /* Value or args associated with the opcode */\ + u8 arg_list_length; /* Number of elements in the arg list */\ + ACPI_DISASM_ONLY_MEMBERS (\ + u8 disasm_flags; /* Used during AML disassembly */\ + u8 disasm_opcode; /* Subtype used for disassembly */\ + char *operator_symbol;/* Used for C-style operator name strings */\ + char aml_op_name[16]) /* Op name (debug only) */ + +/* Flags for disasm_flags field above */ + +#define ACPI_DASM_BUFFER 0x00 /* Buffer is a simple data buffer */ +#define ACPI_DASM_RESOURCE 0x01 /* Buffer is a Resource Descriptor */ +#define ACPI_DASM_STRING 0x02 /* Buffer is a ASCII string */ +#define ACPI_DASM_UNICODE 0x03 /* Buffer is a Unicode string */ +#define ACPI_DASM_PLD_METHOD 0x04 /* Buffer is a _PLD method bit-packed buffer */ +#define ACPI_DASM_UUID 0x05 /* Buffer is a UUID/GUID */ +#define ACPI_DASM_EISAID 0x06 /* Integer is an EISAID */ +#define ACPI_DASM_MATCHOP 0x07 /* Parent opcode is a Match() operator */ +#define ACPI_DASM_LNOT_PREFIX 0x08 /* Start of a Lnot_equal (etc.) pair of opcodes */ +#define ACPI_DASM_LNOT_SUFFIX 0x09 /* End of a Lnot_equal (etc.) pair of opcodes */ +#define ACPI_DASM_HID_STRING 0x0A /* String is a _HID or _CID */ +#define ACPI_DASM_IGNORE 0x0B /* Not used at this time */ + +/* + * Generic operation (for example: If, While, Store) + */ +struct acpi_parse_obj_common { +ACPI_PARSE_COMMON}; + +/* + * Extended Op for named ops (Scope, Method, etc.), deferred ops (Methods and op_regions), + * and bytelists. + */ +struct acpi_parse_obj_named { + ACPI_PARSE_COMMON u8 *path; + u8 *data; /* AML body or bytelist data */ + u32 length; /* AML length */ + u32 name; /* 4-byte name or zero if no name */ +}; + +/* This version is used by the iASL compiler only */ + +#define ACPI_MAX_PARSEOP_NAME 20 + +struct acpi_parse_obj_asl { + ACPI_PARSE_COMMON union acpi_parse_object *child; + union acpi_parse_object *parent_method; + char *filename; + char *external_name; + char *namepath; + char name_seg[4]; + u32 extra_value; + u32 column; + u32 line_number; + u32 logical_line_number; + u32 logical_byte_offset; + u32 end_line; + u32 end_logical_line; + u32 acpi_btype; + u32 aml_length; + u32 aml_subtree_length; + u32 final_aml_length; + u32 final_aml_offset; + u32 compile_flags; + u16 parse_opcode; + u8 aml_opcode_length; + u8 aml_pkg_len_bytes; + u8 extra; + char parse_op_name[ACPI_MAX_PARSEOP_NAME]; +}; + +union acpi_parse_object { + struct acpi_parse_obj_common common; + struct acpi_parse_obj_named named; + struct acpi_parse_obj_asl asl; +}; + +/* + * Parse state - one state per parser invocation and each control + * method. + */ +struct acpi_parse_state { + u8 *aml_start; /* First AML byte */ + u8 *aml; /* Next AML byte */ + u8 *aml_end; /* (last + 1) AML byte */ + u8 *pkg_start; /* Current package begin */ + u8 *pkg_end; /* Current package end */ + union acpi_parse_object *start_op; /* Root of parse tree */ + struct acpi_namespace_node *start_node; + union acpi_generic_state *scope; /* Current scope */ + union acpi_parse_object *start_scope; + u32 aml_size; +}; + +/* Parse object flags */ + +#define ACPI_PARSEOP_GENERIC 0x01 +#define ACPI_PARSEOP_NAMED 0x02 +#define ACPI_PARSEOP_DEFERRED 0x04 +#define ACPI_PARSEOP_BYTELIST 0x08 +#define ACPI_PARSEOP_IN_STACK 0x10 +#define ACPI_PARSEOP_TARGET 0x20 +#define ACPI_PARSEOP_IN_CACHE 0x80 + +/* Parse object disasm_flags */ + +#define ACPI_PARSEOP_IGNORE 0x01 +#define ACPI_PARSEOP_PARAMLIST 0x02 +#define ACPI_PARSEOP_EMPTY_TERMLIST 0x04 +#define ACPI_PARSEOP_PREDEF_CHECKED 0x08 +#define ACPI_PARSEOP_SPECIAL 0x10 +#define ACPI_PARSEOP_COMPOUND 0x20 +#define ACPI_PARSEOP_ASSIGNMENT 0x40 + +/***************************************************************************** + * + * Hardware (ACPI registers) and PNP + * + ****************************************************************************/ + +struct acpi_bit_register_info { + u8 parent_register; + u8 bit_position; + u16 access_bit_mask; +}; + +/* + * Some ACPI registers have bits that must be ignored -- meaning that they + * must be preserved. + */ +#define ACPI_PM1_STATUS_PRESERVED_BITS 0x0800 /* Bit 11 */ + +/* Write-only bits must be zeroed by software */ + +#define ACPI_PM1_CONTROL_WRITEONLY_BITS 0x2004 /* Bits 13, 2 */ + +/* For control registers, both ignored and reserved bits must be preserved */ + +/* + * For PM1 control, the SCI enable bit (bit 0, SCI_EN) is defined by the + * ACPI specification to be a "preserved" bit - "OSPM always preserves this + * bit position", section 4.7.3.2.1. However, on some machines the OS must + * write a one to this bit after resume for the machine to work properly. + * To enable this, we no longer attempt to preserve this bit. No machines + * are known to fail if the bit is not preserved. (May 2009) + */ +#define ACPI_PM1_CONTROL_IGNORED_BITS 0x0200 /* Bit 9 */ +#define ACPI_PM1_CONTROL_RESERVED_BITS 0xC1F8 /* Bits 14-15, 3-8 */ +#define ACPI_PM1_CONTROL_PRESERVED_BITS \ + (ACPI_PM1_CONTROL_IGNORED_BITS | ACPI_PM1_CONTROL_RESERVED_BITS) + +#define ACPI_PM2_CONTROL_PRESERVED_BITS 0xFFFFFFFE /* All except bit 0 */ + +/* + * Register IDs + * These are the full ACPI registers + */ +#define ACPI_REGISTER_PM1_STATUS 0x01 +#define ACPI_REGISTER_PM1_ENABLE 0x02 +#define ACPI_REGISTER_PM1_CONTROL 0x03 +#define ACPI_REGISTER_PM2_CONTROL 0x04 +#define ACPI_REGISTER_PM_TIMER 0x05 +#define ACPI_REGISTER_PROCESSOR_BLOCK 0x06 +#define ACPI_REGISTER_SMI_COMMAND_BLOCK 0x07 + +/* Masks used to access the bit_registers */ + +#define ACPI_BITMASK_TIMER_STATUS 0x0001 +#define ACPI_BITMASK_BUS_MASTER_STATUS 0x0010 +#define ACPI_BITMASK_GLOBAL_LOCK_STATUS 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_STATUS 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_STATUS 0x0200 +#define ACPI_BITMASK_RT_CLOCK_STATUS 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */ +#define ACPI_BITMASK_WAKE_STATUS 0x8000 + +#define ACPI_BITMASK_ALL_FIXED_STATUS (\ + ACPI_BITMASK_TIMER_STATUS | \ + ACPI_BITMASK_BUS_MASTER_STATUS | \ + ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ + ACPI_BITMASK_POWER_BUTTON_STATUS | \ + ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ + ACPI_BITMASK_RT_CLOCK_STATUS | \ + ACPI_BITMASK_PCIEXP_WAKE_STATUS | \ + ACPI_BITMASK_WAKE_STATUS) + +#define ACPI_BITMASK_TIMER_ENABLE 0x0001 +#define ACPI_BITMASK_GLOBAL_LOCK_ENABLE 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_ENABLE 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_ENABLE 0x0200 +#define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */ + +#define ACPI_BITMASK_SCI_ENABLE 0x0001 +#define ACPI_BITMASK_BUS_MASTER_RLD 0x0002 +#define ACPI_BITMASK_GLOBAL_LOCK_RELEASE 0x0004 +#define ACPI_BITMASK_SLEEP_TYPE 0x1C00 +#define ACPI_BITMASK_SLEEP_ENABLE 0x2000 + +#define ACPI_BITMASK_ARB_DISABLE 0x0001 + +/* Raw bit position of each bit_register */ + +#define ACPI_BITPOSITION_TIMER_STATUS 0x00 +#define ACPI_BITPOSITION_BUS_MASTER_STATUS 0x04 +#define ACPI_BITPOSITION_GLOBAL_LOCK_STATUS 0x05 +#define ACPI_BITPOSITION_POWER_BUTTON_STATUS 0x08 +#define ACPI_BITPOSITION_SLEEP_BUTTON_STATUS 0x09 +#define ACPI_BITPOSITION_RT_CLOCK_STATUS 0x0A +#define ACPI_BITPOSITION_PCIEXP_WAKE_STATUS 0x0E /* ACPI 3.0 */ +#define ACPI_BITPOSITION_WAKE_STATUS 0x0F + +#define ACPI_BITPOSITION_TIMER_ENABLE 0x00 +#define ACPI_BITPOSITION_GLOBAL_LOCK_ENABLE 0x05 +#define ACPI_BITPOSITION_POWER_BUTTON_ENABLE 0x08 +#define ACPI_BITPOSITION_SLEEP_BUTTON_ENABLE 0x09 +#define ACPI_BITPOSITION_RT_CLOCK_ENABLE 0x0A +#define ACPI_BITPOSITION_PCIEXP_WAKE_DISABLE 0x0E /* ACPI 3.0 */ + +#define ACPI_BITPOSITION_SCI_ENABLE 0x00 +#define ACPI_BITPOSITION_BUS_MASTER_RLD 0x01 +#define ACPI_BITPOSITION_GLOBAL_LOCK_RELEASE 0x02 +#define ACPI_BITPOSITION_SLEEP_TYPE 0x0A +#define ACPI_BITPOSITION_SLEEP_ENABLE 0x0D + +#define ACPI_BITPOSITION_ARB_DISABLE 0x00 + +/* Structs and definitions for _OSI support and I/O port validation */ + +#define ACPI_ALWAYS_ILLEGAL 0x00 + +struct acpi_interface_info { + char *name; + struct acpi_interface_info *next; + u8 flags; + u8 value; +}; + +#define ACPI_OSI_INVALID 0x01 +#define ACPI_OSI_DYNAMIC 0x02 +#define ACPI_OSI_FEATURE 0x04 +#define ACPI_OSI_DEFAULT_INVALID 0x08 +#define ACPI_OSI_OPTIONAL_FEATURE (ACPI_OSI_FEATURE | ACPI_OSI_DEFAULT_INVALID | ACPI_OSI_INVALID) + +struct acpi_port_info { + char *name; + u16 start; + u16 end; + u8 osi_dependency; +}; + +/***************************************************************************** + * + * Resource descriptors + * + ****************************************************************************/ + +/* resource_type values */ + +#define ACPI_ADDRESS_TYPE_MEMORY_RANGE 0 +#define ACPI_ADDRESS_TYPE_IO_RANGE 1 +#define ACPI_ADDRESS_TYPE_BUS_NUMBER_RANGE 2 + +/* Resource descriptor types and masks */ + +#define ACPI_RESOURCE_NAME_LARGE 0x80 +#define ACPI_RESOURCE_NAME_SMALL 0x00 + +#define ACPI_RESOURCE_NAME_SMALL_MASK 0x78 /* Bits 6:3 contain the type */ +#define ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK 0x07 /* Bits 2:0 contain the length */ +#define ACPI_RESOURCE_NAME_LARGE_MASK 0x7F /* Bits 6:0 contain the type */ + +/* + * Small resource descriptor "names" as defined by the ACPI specification. + * Note: Bits 2:0 are used for the descriptor length + */ +#define ACPI_RESOURCE_NAME_IRQ 0x20 +#define ACPI_RESOURCE_NAME_DMA 0x28 +#define ACPI_RESOURCE_NAME_START_DEPENDENT 0x30 +#define ACPI_RESOURCE_NAME_END_DEPENDENT 0x38 +#define ACPI_RESOURCE_NAME_IO 0x40 +#define ACPI_RESOURCE_NAME_FIXED_IO 0x48 +#define ACPI_RESOURCE_NAME_FIXED_DMA 0x50 +#define ACPI_RESOURCE_NAME_RESERVED_S2 0x58 +#define ACPI_RESOURCE_NAME_RESERVED_S3 0x60 +#define ACPI_RESOURCE_NAME_RESERVED_S4 0x68 +#define ACPI_RESOURCE_NAME_VENDOR_SMALL 0x70 +#define ACPI_RESOURCE_NAME_END_TAG 0x78 + +/* + * Large resource descriptor "names" as defined by the ACPI specification. + * Note: includes the Large Descriptor bit in bit[7] + */ +#define ACPI_RESOURCE_NAME_MEMORY24 0x81 +#define ACPI_RESOURCE_NAME_GENERIC_REGISTER 0x82 +#define ACPI_RESOURCE_NAME_RESERVED_L1 0x83 +#define ACPI_RESOURCE_NAME_VENDOR_LARGE 0x84 +#define ACPI_RESOURCE_NAME_MEMORY32 0x85 +#define ACPI_RESOURCE_NAME_FIXED_MEMORY32 0x86 +#define ACPI_RESOURCE_NAME_ADDRESS32 0x87 +#define ACPI_RESOURCE_NAME_ADDRESS16 0x88 +#define ACPI_RESOURCE_NAME_EXTENDED_IRQ 0x89 +#define ACPI_RESOURCE_NAME_ADDRESS64 0x8A +#define ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 0x8B +#define ACPI_RESOURCE_NAME_GPIO 0x8C +#define ACPI_RESOURCE_NAME_SERIAL_BUS 0x8E +#define ACPI_RESOURCE_NAME_LARGE_MAX 0x8E + +/***************************************************************************** + * + * Miscellaneous + * + ****************************************************************************/ + +#define ACPI_ASCII_ZERO 0x30 + +/***************************************************************************** + * + * Disassembler + * + ****************************************************************************/ + +struct acpi_external_list { + char *path; + char *internal_path; + struct acpi_external_list *next; + u32 value; + u16 length; + u16 flags; + u8 type; +}; + +/* Values for Flags field above */ + +#define ACPI_EXT_RESOLVED_REFERENCE 0x01 /* Object was resolved during cross ref */ +#define ACPI_EXT_ORIGIN_FROM_FILE 0x02 /* External came from a file */ +#define ACPI_EXT_INTERNAL_PATH_ALLOCATED 0x04 /* Deallocate internal path on completion */ +#define ACPI_EXT_EXTERNAL_EMITTED 0x08 /* External() statement has been emitted */ + +struct acpi_external_file { + char *path; + struct acpi_external_file *next; +}; + +/***************************************************************************** + * + * Debugger + * + ****************************************************************************/ + +struct acpi_db_method_info { + acpi_handle method; + acpi_handle main_thread_gate; + acpi_handle thread_complete_gate; + acpi_handle info_gate; + acpi_thread_id *threads; + u32 num_threads; + u32 num_created; + u32 num_completed; + + char *name; + u32 flags; + u32 num_loops; + char pathname[ACPI_DB_LINE_BUFFER_SIZE]; + char **args; + acpi_object_type *types; + + /* + * Arguments to be passed to method for the command + * Threads - + * the Number of threads, ID of current thread and + * Index of current thread inside all them created. + */ + char init_args; + char *arguments[4]; + char num_threads_str[11]; + char id_of_thread_str[11]; + char index_of_thread_str[11]; +}; + +struct acpi_integrity_info { + u32 nodes; + u32 objects; +}; + +#define ACPI_DB_DISABLE_OUTPUT 0x00 +#define ACPI_DB_REDIRECTABLE_OUTPUT 0x01 +#define ACPI_DB_CONSOLE_OUTPUT 0x02 +#define ACPI_DB_DUPLICATE_OUTPUT 0x03 + +/***************************************************************************** + * + * Debug + * + ****************************************************************************/ + +/* Entry for a memory allocation (debug only) */ + +#define ACPI_MEM_MALLOC 0 +#define ACPI_MEM_CALLOC 1 +#define ACPI_MAX_MODULE_NAME 16 + +#define ACPI_COMMON_DEBUG_MEM_HEADER \ + struct acpi_debug_mem_block *previous; \ + struct acpi_debug_mem_block *next; \ + u32 size; \ + u32 component; \ + u32 line; \ + char module[ACPI_MAX_MODULE_NAME]; \ + u8 alloc_type; + +struct acpi_debug_mem_header { +ACPI_COMMON_DEBUG_MEM_HEADER}; + +struct acpi_debug_mem_block { + ACPI_COMMON_DEBUG_MEM_HEADER u64 user_space; +}; + +#define ACPI_MEM_LIST_GLOBAL 0 +#define ACPI_MEM_LIST_NSNODE 1 +#define ACPI_MEM_LIST_MAX 1 +#define ACPI_NUM_MEM_LISTS 2 + +/***************************************************************************** + * + * Info/help support + * + ****************************************************************************/ + +struct ah_predefined_name { + char *name; + char *description; +#ifndef ACPI_ASL_COMPILER + char *action; +#endif +}; + +struct ah_device_id { + char *name; + char *description; +}; + +struct ah_uuid { + char *description; + char *string; +}; + +#endif /* __ACLOCAL_H__ */ diff --git a/kernel/drivers/acpi/acpica/acmacros.h b/kernel/drivers/acpi/acpica/acmacros.h new file mode 100644 index 000000000..c240bdf82 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acmacros.h @@ -0,0 +1,419 @@ +/****************************************************************************** + * + * Name: acmacros.h - C macros for the entire subsystem. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACMACROS_H__ +#define __ACMACROS_H__ + +/* + * Extract data using a pointer. Any more than a byte and we + * get into potential aligment issues -- see the STORE macros below. + * Use with care. + */ +#define ACPI_CAST8(ptr) ACPI_CAST_PTR (u8, (ptr)) +#define ACPI_CAST16(ptr) ACPI_CAST_PTR (u16, (ptr)) +#define ACPI_CAST32(ptr) ACPI_CAST_PTR (u32, (ptr)) +#define ACPI_CAST64(ptr) ACPI_CAST_PTR (u64, (ptr)) +#define ACPI_GET8(ptr) (*ACPI_CAST8 (ptr)) +#define ACPI_GET16(ptr) (*ACPI_CAST16 (ptr)) +#define ACPI_GET32(ptr) (*ACPI_CAST32 (ptr)) +#define ACPI_GET64(ptr) (*ACPI_CAST64 (ptr)) +#define ACPI_SET8(ptr, val) (*ACPI_CAST8 (ptr) = (u8) (val)) +#define ACPI_SET16(ptr, val) (*ACPI_CAST16 (ptr) = (u16) (val)) +#define ACPI_SET32(ptr, val) (*ACPI_CAST32 (ptr) = (u32) (val)) +#define ACPI_SET64(ptr, val) (*ACPI_CAST64 (ptr) = (u64) (val)) + +/* + * printf() format helper. This macros is a workaround for the difficulties + * with emitting 64-bit integers and 64-bit pointers with the same code + * for both 32-bit and 64-bit hosts. + */ +#define ACPI_FORMAT_UINT64(i) ACPI_HIDWORD(i), ACPI_LODWORD(i) + +/* + * Macros for moving data around to/from buffers that are possibly unaligned. + * If the hardware supports the transfer of unaligned data, just do the store. + * Otherwise, we have to move one byte at a time. + */ +#ifdef ACPI_BIG_ENDIAN +/* + * Macros for big-endian machines + */ + +/* These macros reverse the bytes during the move, converting little-endian to big endian */ + + /* Big Endian <== Little Endian */ + /* Hi...Lo Lo...Hi */ +/* 16-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_16_TO_16(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[0];} + +#define ACPI_MOVE_16_TO_32(d, s) {(*(u32 *)(void *)(d))=0;\ + ((u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[1];\ + ((u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[0];} + +#define ACPI_MOVE_16_TO_64(d, s) {(*(u64 *)(void *)(d))=0;\ + ((u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\ + ((u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];} + +/* 32-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ + +#define ACPI_MOVE_32_TO_32(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[3];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[0];} + +#define ACPI_MOVE_32_TO_64(d, s) {(*(u64 *)(void *)(d))=0;\ + ((u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[3];\ + ((u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[2];\ + ((u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\ + ((u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];} + +/* 64-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ + +#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */ + +#define ACPI_MOVE_64_TO_64(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[7];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[6];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[5];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[4];\ + (( u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[3];\ + (( u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];} +#else +/* + * Macros for little-endian machines + */ + +#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED + +/* The hardware supports unaligned transfers, just do the little-endian move */ + +/* 16-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_16_TO_16(d, s) *(u16 *)(void *)(d) = *(u16 *)(void *)(s) +#define ACPI_MOVE_16_TO_32(d, s) *(u32 *)(void *)(d) = *(u16 *)(void *)(s) +#define ACPI_MOVE_16_TO_64(d, s) *(u64 *)(void *)(d) = *(u16 *)(void *)(s) + +/* 32-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ +#define ACPI_MOVE_32_TO_32(d, s) *(u32 *)(void *)(d) = *(u32 *)(void *)(s) +#define ACPI_MOVE_32_TO_64(d, s) *(u64 *)(void *)(d) = *(u32 *)(void *)(s) + +/* 64-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ +#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */ +#define ACPI_MOVE_64_TO_64(d, s) *(u64 *)(void *)(d) = *(u64 *)(void *)(s) + +#else +/* + * The hardware does not support unaligned transfers. We must move the + * data one byte at a time. These macros work whether the source or + * the destination (or both) is/are unaligned. (Little-endian move) + */ + +/* 16-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_16_TO_16(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];} + +#define ACPI_MOVE_16_TO_32(d, s) {(*(u32 *)(void *)(d)) = 0; ACPI_MOVE_16_TO_16(d, s);} +#define ACPI_MOVE_16_TO_64(d, s) {(*(u64 *)(void *)(d)) = 0; ACPI_MOVE_16_TO_16(d, s);} + +/* 32-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ + +#define ACPI_MOVE_32_TO_32(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[3];} + +#define ACPI_MOVE_32_TO_64(d, s) {(*(u64 *)(void *)(d)) = 0; ACPI_MOVE_32_TO_32(d, s);} + +/* 64-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ +#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */ +#define ACPI_MOVE_64_TO_64(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[3];\ + (( u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[4];\ + (( u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[5];\ + (( u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[6];\ + (( u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[7];} +#endif +#endif + +/* + * Fast power-of-two math macros for non-optimized compilers + */ +#define _ACPI_DIV(value, power_of2) ((u32) ((value) >> (power_of2))) +#define _ACPI_MUL(value, power_of2) ((u32) ((value) << (power_of2))) +#define _ACPI_MOD(value, divisor) ((u32) ((value) & ((divisor) -1))) + +#define ACPI_DIV_2(a) _ACPI_DIV(a, 1) +#define ACPI_MUL_2(a) _ACPI_MUL(a, 1) +#define ACPI_MOD_2(a) _ACPI_MOD(a, 2) + +#define ACPI_DIV_4(a) _ACPI_DIV(a, 2) +#define ACPI_MUL_4(a) _ACPI_MUL(a, 2) +#define ACPI_MOD_4(a) _ACPI_MOD(a, 4) + +#define ACPI_DIV_8(a) _ACPI_DIV(a, 3) +#define ACPI_MUL_8(a) _ACPI_MUL(a, 3) +#define ACPI_MOD_8(a) _ACPI_MOD(a, 8) + +#define ACPI_DIV_16(a) _ACPI_DIV(a, 4) +#define ACPI_MUL_16(a) _ACPI_MUL(a, 4) +#define ACPI_MOD_16(a) _ACPI_MOD(a, 16) + +#define ACPI_DIV_32(a) _ACPI_DIV(a, 5) +#define ACPI_MUL_32(a) _ACPI_MUL(a, 5) +#define ACPI_MOD_32(a) _ACPI_MOD(a, 32) + +/* + * Rounding macros (Power of two boundaries only) + */ +#define ACPI_ROUND_DOWN(value, boundary) (((acpi_size)(value)) & \ + (~(((acpi_size) boundary)-1))) + +#define ACPI_ROUND_UP(value, boundary) ((((acpi_size)(value)) + \ + (((acpi_size) boundary)-1)) & \ + (~(((acpi_size) boundary)-1))) + +/* Note: sizeof(acpi_size) evaluates to either 4 or 8 (32- vs 64-bit mode) */ + +#define ACPI_ROUND_DOWN_TO_32BIT(a) ACPI_ROUND_DOWN(a, 4) +#define ACPI_ROUND_DOWN_TO_64BIT(a) ACPI_ROUND_DOWN(a, 8) +#define ACPI_ROUND_DOWN_TO_NATIVE_WORD(a) ACPI_ROUND_DOWN(a, sizeof(acpi_size)) + +#define ACPI_ROUND_UP_TO_32BIT(a) ACPI_ROUND_UP(a, 4) +#define ACPI_ROUND_UP_TO_64BIT(a) ACPI_ROUND_UP(a, 8) +#define ACPI_ROUND_UP_TO_NATIVE_WORD(a) ACPI_ROUND_UP(a, sizeof(acpi_size)) + +#define ACPI_ROUND_BITS_UP_TO_BYTES(a) ACPI_DIV_8((a) + 7) +#define ACPI_ROUND_BITS_DOWN_TO_BYTES(a) ACPI_DIV_8((a)) + +#define ACPI_ROUND_UP_TO_1K(a) (((a) + 1023) >> 10) + +/* Generic (non-power-of-two) rounding */ + +#define ACPI_ROUND_UP_TO(value, boundary) (((value) + ((boundary)-1)) / (boundary)) + +#define ACPI_IS_MISALIGNED(value) (((acpi_size) value) & (sizeof(acpi_size)-1)) + +/* + * Bitmask creation + * Bit positions start at zero. + * MASK_BITS_ABOVE creates a mask starting AT the position and above + * MASK_BITS_BELOW creates a mask starting one bit BELOW the position + */ +#define ACPI_MASK_BITS_ABOVE(position) (~((ACPI_UINT64_MAX) << ((u32) (position)))) +#define ACPI_MASK_BITS_BELOW(position) ((ACPI_UINT64_MAX) << ((u32) (position))) + +/* Bitfields within ACPI registers */ + +#define ACPI_REGISTER_PREPARE_BITS(val, pos, mask) \ + ((val << pos) & mask) + +#define ACPI_REGISTER_INSERT_VALUE(reg, pos, mask, val) \ + reg = (reg & (~(mask))) | ACPI_REGISTER_PREPARE_BITS(val, pos, mask) + +#define ACPI_INSERT_BITS(target, mask, source) \ + target = ((target & (~(mask))) | (source & mask)) + +/* Generic bitfield macros and masks */ + +#define ACPI_GET_BITS(source_ptr, position, mask) \ + ((*source_ptr >> position) & mask) + +#define ACPI_SET_BITS(target_ptr, position, mask, value) \ + (*target_ptr |= ((value & mask) << position)) + +#define ACPI_1BIT_MASK 0x00000001 +#define ACPI_2BIT_MASK 0x00000003 +#define ACPI_3BIT_MASK 0x00000007 +#define ACPI_4BIT_MASK 0x0000000F +#define ACPI_5BIT_MASK 0x0000001F +#define ACPI_6BIT_MASK 0x0000003F +#define ACPI_7BIT_MASK 0x0000007F +#define ACPI_8BIT_MASK 0x000000FF +#define ACPI_16BIT_MASK 0x0000FFFF +#define ACPI_24BIT_MASK 0x00FFFFFF + +/* Macros to extract flag bits from position zero */ + +#define ACPI_GET_1BIT_FLAG(value) ((value) & ACPI_1BIT_MASK) +#define ACPI_GET_2BIT_FLAG(value) ((value) & ACPI_2BIT_MASK) +#define ACPI_GET_3BIT_FLAG(value) ((value) & ACPI_3BIT_MASK) +#define ACPI_GET_4BIT_FLAG(value) ((value) & ACPI_4BIT_MASK) + +/* Macros to extract flag bits from position one and above */ + +#define ACPI_EXTRACT_1BIT_FLAG(field, position) (ACPI_GET_1BIT_FLAG ((field) >> position)) +#define ACPI_EXTRACT_2BIT_FLAG(field, position) (ACPI_GET_2BIT_FLAG ((field) >> position)) +#define ACPI_EXTRACT_3BIT_FLAG(field, position) (ACPI_GET_3BIT_FLAG ((field) >> position)) +#define ACPI_EXTRACT_4BIT_FLAG(field, position) (ACPI_GET_4BIT_FLAG ((field) >> position)) + +/* ACPI Pathname helpers */ + +#define ACPI_IS_ROOT_PREFIX(c) ((c) == (u8) 0x5C) /* Backslash */ +#define ACPI_IS_PARENT_PREFIX(c) ((c) == (u8) 0x5E) /* Carat */ +#define ACPI_IS_PATH_SEPARATOR(c) ((c) == (u8) 0x2E) /* Period (dot) */ + +/* + * An object of type struct acpi_namespace_node can appear in some contexts + * where a pointer to an object of type union acpi_operand_object can also + * appear. This macro is used to distinguish them. + * + * The "DescriptorType" field is the second field in both structures. + */ +#define ACPI_GET_DESCRIPTOR_PTR(d) (((union acpi_descriptor *)(void *)(d))->common.common_pointer) +#define ACPI_SET_DESCRIPTOR_PTR(d, p) (((union acpi_descriptor *)(void *)(d))->common.common_pointer = (p)) +#define ACPI_GET_DESCRIPTOR_TYPE(d) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type) +#define ACPI_SET_DESCRIPTOR_TYPE(d, t) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type = (t)) + +/* + * Macros for the master AML opcode table + */ +#if defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT) +#define ACPI_OP(name, Pargs, Iargs, obj_type, class, type, flags) \ + {name, (u32)(Pargs), (u32)(Iargs), (u32)(flags), obj_type, class, type} +#else +#define ACPI_OP(name, Pargs, Iargs, obj_type, class, type, flags) \ + {(u32)(Pargs), (u32)(Iargs), (u32)(flags), obj_type, class, type} +#endif + +#define ARG_TYPE_WIDTH 5 +#define ARG_1(x) ((u32)(x)) +#define ARG_2(x) ((u32)(x) << (1 * ARG_TYPE_WIDTH)) +#define ARG_3(x) ((u32)(x) << (2 * ARG_TYPE_WIDTH)) +#define ARG_4(x) ((u32)(x) << (3 * ARG_TYPE_WIDTH)) +#define ARG_5(x) ((u32)(x) << (4 * ARG_TYPE_WIDTH)) +#define ARG_6(x) ((u32)(x) << (5 * ARG_TYPE_WIDTH)) + +#define ARGI_LIST1(a) (ARG_1(a)) +#define ARGI_LIST2(a, b) (ARG_1(b)|ARG_2(a)) +#define ARGI_LIST3(a, b, c) (ARG_1(c)|ARG_2(b)|ARG_3(a)) +#define ARGI_LIST4(a, b, c, d) (ARG_1(d)|ARG_2(c)|ARG_3(b)|ARG_4(a)) +#define ARGI_LIST5(a, b, c, d, e) (ARG_1(e)|ARG_2(d)|ARG_3(c)|ARG_4(b)|ARG_5(a)) +#define ARGI_LIST6(a, b, c, d, e, f) (ARG_1(f)|ARG_2(e)|ARG_3(d)|ARG_4(c)|ARG_5(b)|ARG_6(a)) + +#define ARGP_LIST1(a) (ARG_1(a)) +#define ARGP_LIST2(a, b) (ARG_1(a)|ARG_2(b)) +#define ARGP_LIST3(a, b, c) (ARG_1(a)|ARG_2(b)|ARG_3(c)) +#define ARGP_LIST4(a, b, c, d) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)) +#define ARGP_LIST5(a, b, c, d, e) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e)) +#define ARGP_LIST6(a, b, c, d, e, f) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e)|ARG_6(f)) + +#define GET_CURRENT_ARG_TYPE(list) (list & ((u32) 0x1F)) +#define INCREMENT_ARG_LIST(list) (list >>= ((u32) ARG_TYPE_WIDTH)) + +/* + * Ascii error messages can be configured out + */ +#ifndef ACPI_NO_ERROR_MESSAGES +/* + * Error reporting. Callers module and line number are inserted by AE_INFO, + * the plist contains a set of parens to allow variable-length lists. + * These macros are used for both the debug and non-debug versions of the code. + */ +#define ACPI_ERROR_NAMESPACE(s, e) acpi_ut_namespace_error (AE_INFO, s, e); +#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ut_method_error (AE_INFO, s, n, p, e); +#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist +#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist +#define ACPI_BIOS_ERROR_PREDEFINED(plist) acpi_ut_predefined_bios_error plist + +#else + +/* No error messages */ + +#define ACPI_ERROR_NAMESPACE(s, e) +#define ACPI_ERROR_METHOD(s, n, p, e) +#define ACPI_WARN_PREDEFINED(plist) +#define ACPI_INFO_PREDEFINED(plist) +#define ACPI_BIOS_ERROR_PREDEFINED(plist) + +#endif /* ACPI_NO_ERROR_MESSAGES */ + +#if (!ACPI_REDUCED_HARDWARE) +#define ACPI_HW_OPTIONAL_FUNCTION(addr) addr +#else +#define ACPI_HW_OPTIONAL_FUNCTION(addr) NULL +#endif + +/* + * Some code only gets executed when the debugger is built in. + * Note that this is entirely independent of whether the + * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not. + */ +#ifdef ACPI_DEBUGGER +#define ACPI_DEBUGGER_EXEC(a) a +#else +#define ACPI_DEBUGGER_EXEC(a) +#endif + +/* + * Macros used for ACPICA utilities only + */ + +/* Generate a UUID */ + +#define ACPI_INIT_UUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ + (a) & 0xFF, ((a) >> 8) & 0xFF, ((a) >> 16) & 0xFF, ((a) >> 24) & 0xFF, \ + (b) & 0xFF, ((b) >> 8) & 0xFF, \ + (c) & 0xFF, ((c) >> 8) & 0xFF, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) + +#define ACPI_IS_OCTAL_DIGIT(d) (((char)(d) >= '0') && ((char)(d) <= '7')) + +#endif /* ACMACROS_H */ diff --git a/kernel/drivers/acpi/acpica/acnamesp.h b/kernel/drivers/acpi/acpica/acnamesp.h new file mode 100644 index 000000000..952fbe0b7 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acnamesp.h @@ -0,0 +1,407 @@ +/****************************************************************************** + * + * Name: acnamesp.h - Namespace subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACNAMESP_H__ +#define __ACNAMESP_H__ + +/* To search the entire name space, pass this as search_base */ + +#define ACPI_NS_ALL ((acpi_handle)0) + +/* + * Elements of acpi_ns_properties are bit significant + * and should be one-to-one with values of acpi_object_type + */ +#define ACPI_NS_NORMAL 0 +#define ACPI_NS_NEWSCOPE 1 /* a definition of this type opens a name scope */ +#define ACPI_NS_LOCAL 2 /* suppress search of enclosing scopes */ + +/* Flags for acpi_ns_lookup, acpi_ns_search_and_enter */ + +#define ACPI_NS_NO_UPSEARCH 0 +#define ACPI_NS_SEARCH_PARENT 0x01 +#define ACPI_NS_DONT_OPEN_SCOPE 0x02 +#define ACPI_NS_NO_PEER_SEARCH 0x04 +#define ACPI_NS_ERROR_IF_FOUND 0x08 +#define ACPI_NS_PREFIX_IS_SCOPE 0x10 +#define ACPI_NS_EXTERNAL 0x20 +#define ACPI_NS_TEMPORARY 0x40 + +/* Flags for acpi_ns_walk_namespace */ + +#define ACPI_NS_WALK_NO_UNLOCK 0 +#define ACPI_NS_WALK_UNLOCK 0x01 +#define ACPI_NS_WALK_TEMP_NODES 0x02 + +/* Object is not a package element */ + +#define ACPI_NOT_PACKAGE_ELEMENT ACPI_UINT32_MAX + +/* Always emit warning message, not dependent on node flags */ + +#define ACPI_WARN_ALWAYS 0 + +/* + * nsinit - Namespace initialization + */ +acpi_status acpi_ns_initialize_objects(void); + +acpi_status acpi_ns_initialize_devices(void); + +/* + * nsload - Namespace loading + */ +acpi_status acpi_ns_load_namespace(void); + +acpi_status +acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node); + +/* + * nswalk - walk the namespace + */ +acpi_status +acpi_ns_walk_namespace(acpi_object_type type, + acpi_handle start_object, + u32 max_depth, + u32 flags, + acpi_walk_callback descending_callback, + acpi_walk_callback ascending_callback, + void *context, void **return_value); + +struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node + *parent, + struct acpi_namespace_node + *child); + +struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type, + struct + acpi_namespace_node + *parent, + struct + acpi_namespace_node + *child); + +/* + * nsparse - table parsing + */ +acpi_status +acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node); + +acpi_status +acpi_ns_one_complete_parse(u32 pass_number, + u32 table_index, + struct acpi_namespace_node *start_node); + +/* + * nsaccess - Top-level namespace access + */ +acpi_status acpi_ns_root_initialize(void); + +acpi_status +acpi_ns_lookup(union acpi_generic_state *scope_info, + char *name, + acpi_object_type type, + acpi_interpreter_mode interpreter_mode, + u32 flags, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **ret_node); + +/* + * nsalloc - Named object allocation/deallocation + */ +struct acpi_namespace_node *acpi_ns_create_node(u32 name); + +void acpi_ns_delete_node(struct acpi_namespace_node *node); + +void acpi_ns_remove_node(struct acpi_namespace_node *node); + +void +acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_handle); + +void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id); + +void acpi_ns_detach_object(struct acpi_namespace_node *node); + +void acpi_ns_delete_children(struct acpi_namespace_node *parent); + +int acpi_ns_compare_names(char *name1, char *name2); + +/* + * nsconvert - Dynamic object conversion routines + */ +acpi_status +acpi_ns_convert_to_integer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +acpi_status +acpi_ns_convert_to_string(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +acpi_status +acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +acpi_status +acpi_ns_convert_to_unicode(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +acpi_status +acpi_ns_convert_to_resource(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +/* + * nsdump - Namespace dump/print utilities + */ +#ifdef ACPI_FUTURE_USAGE +void acpi_ns_dump_tables(acpi_handle search_base, u32 max_depth); +#endif /* ACPI_FUTURE_USAGE */ + +void acpi_ns_dump_entry(acpi_handle handle, u32 debug_level); + +void +acpi_ns_dump_pathname(acpi_handle handle, char *msg, u32 level, u32 component); + +void acpi_ns_print_pathname(u32 num_segments, char *pathname); + +acpi_status +acpi_ns_dump_one_object(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +#ifdef ACPI_FUTURE_USAGE +void +acpi_ns_dump_objects(acpi_object_type type, + u8 display_type, + u32 max_depth, + acpi_owner_id owner_id, acpi_handle start_handle); + +void +acpi_ns_dump_object_paths(acpi_object_type type, + u8 display_type, + u32 max_depth, + acpi_owner_id owner_id, acpi_handle start_handle); +#endif /* ACPI_FUTURE_USAGE */ + +/* + * nseval - Namespace evaluation functions + */ +acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info); + +void acpi_ns_exec_module_code_list(void); + +/* + * nsarguments - Argument count/type checking for predefined/reserved names + */ +void +acpi_ns_check_argument_count(char *pathname, + struct acpi_namespace_node *node, + u32 user_param_count, + const union acpi_predefined_info *info); + +void +acpi_ns_check_acpi_compliance(char *pathname, + struct acpi_namespace_node *node, + const union acpi_predefined_info *predefined); + +void acpi_ns_check_argument_types(struct acpi_evaluate_info *info); + +/* + * nspredef - Return value checking for predefined/reserved names + */ +acpi_status +acpi_ns_check_return_value(struct acpi_namespace_node *node, + struct acpi_evaluate_info *info, + u32 user_param_count, + acpi_status return_status, + union acpi_operand_object **return_object); + +acpi_status +acpi_ns_check_object_type(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr, + u32 expected_btypes, u32 package_index); + +/* + * nsprepkg - Validation of predefined name packages + */ +acpi_status +acpi_ns_check_package(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +/* + * nsnames - Name and Scope manipulation + */ +u32 acpi_ns_opens_scope(acpi_object_type type); + +acpi_status +acpi_ns_build_external_path(struct acpi_namespace_node *node, + acpi_size size, char *name_buffer); + +char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node); + +char *acpi_ns_name_of_current_scope(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ns_handle_to_pathname(acpi_handle target_handle, + struct acpi_buffer *buffer); + +u8 +acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for); + +acpi_status +acpi_ns_get_node(struct acpi_namespace_node *prefix_node, + const char *external_pathname, + u32 flags, struct acpi_namespace_node **out_node); + +acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node); + +/* + * nsobject - Object management for namespace nodes + */ +acpi_status +acpi_ns_attach_object(struct acpi_namespace_node *node, + union acpi_operand_object *object, acpi_object_type type); + +union acpi_operand_object *acpi_ns_get_attached_object(struct + acpi_namespace_node + *node); + +union acpi_operand_object *acpi_ns_get_secondary_object(union + acpi_operand_object + *obj_desc); + +acpi_status +acpi_ns_attach_data(struct acpi_namespace_node *node, + acpi_object_handler handler, void *data); + +acpi_status +acpi_ns_detach_data(struct acpi_namespace_node *node, + acpi_object_handler handler); + +acpi_status +acpi_ns_get_attached_data(struct acpi_namespace_node *node, + acpi_object_handler handler, void **data); + +/* + * nsrepair - General return object repair for all + * predefined methods/objects + */ +acpi_status +acpi_ns_simple_repair(struct acpi_evaluate_info *info, + u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr); + +acpi_status +acpi_ns_wrap_with_package(struct acpi_evaluate_info *info, + union acpi_operand_object *original_object, + union acpi_operand_object **obj_desc_ptr); + +acpi_status +acpi_ns_repair_null_element(struct acpi_evaluate_info *info, + u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr); + +void +acpi_ns_remove_null_elements(struct acpi_evaluate_info *info, + u8 package_type, + union acpi_operand_object *obj_desc); + +/* + * nsrepair2 - Return object repair for specific + * predefined methods/objects + */ +acpi_status +acpi_ns_complex_repairs(struct acpi_evaluate_info *info, + struct acpi_namespace_node *node, + acpi_status validate_status, + union acpi_operand_object **return_object_ptr); + +/* + * nssearch - Namespace searching and entry + */ +acpi_status +acpi_ns_search_and_enter(u32 entry_name, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + acpi_interpreter_mode interpreter_mode, + acpi_object_type type, + u32 flags, struct acpi_namespace_node **ret_node); + +acpi_status +acpi_ns_search_one_scope(u32 entry_name, + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_namespace_node **ret_node); + +void +acpi_ns_install_node(struct acpi_walk_state *walk_state, + struct acpi_namespace_node *parent_node, + struct acpi_namespace_node *node, acpi_object_type type); + +/* + * nsutils - Utility functions + */ +acpi_object_type acpi_ns_get_type(struct acpi_namespace_node *node); + +u32 acpi_ns_local(acpi_object_type type); + +void +acpi_ns_print_node_pathname(struct acpi_namespace_node *node, const char *msg); + +acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info); + +void acpi_ns_get_internal_name_length(struct acpi_namestring_info *info); + +acpi_status +acpi_ns_internalize_name(const char *dotted_name, char **converted_name); + +acpi_status +acpi_ns_externalize_name(u32 internal_name_length, + const char *internal_name, + u32 * converted_name_length, char **converted_name); + +struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle); + +void acpi_ns_terminate(void); + +#endif /* __ACNAMESP_H__ */ diff --git a/kernel/drivers/acpi/acpica/acobject.h b/kernel/drivers/acpi/acpica/acobject.h new file mode 100644 index 000000000..3e9720e1f --- /dev/null +++ b/kernel/drivers/acpi/acpica/acobject.h @@ -0,0 +1,463 @@ +/****************************************************************************** + * + * Name: acobject.h - Definition of union acpi_operand_object (Internal object only) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _ACOBJECT_H +#define _ACOBJECT_H + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +/* + * The union acpi_operand_object is used to pass AML operands from the dispatcher + * to the interpreter, and to keep track of the various handlers such as + * address space handlers and notify handlers. The object is a constant + * size in order to allow it to be cached and reused. + * + * Note: The object is optimized to be aligned and will not work if it is + * byte-packed. + */ +#if ACPI_MACHINE_WIDTH == 64 +#pragma pack(8) +#else +#pragma pack(4) +#endif + +/******************************************************************************* + * + * Common Descriptors + * + ******************************************************************************/ + +/* + * Common area for all objects. + * + * descriptor_type is used to differentiate between internal descriptors, and + * must be in the same place across all descriptors + * + * Note: The descriptor_type and Type fields must appear in the identical + * position in both the struct acpi_namespace_node and union acpi_operand_object + * structures. + */ +#define ACPI_OBJECT_COMMON_HEADER \ + union acpi_operand_object *next_object; /* Objects linked to parent NS node */\ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 type; /* acpi_object_type */\ + u16 reference_count; /* For object deletion management */\ + u8 flags; + /* + * Note: There are 3 bytes available here before the + * next natural alignment boundary (for both 32/64 cases) + */ + +/* Values for Flag byte above */ + +#define AOPOBJ_AML_CONSTANT 0x01 /* Integer is an AML constant */ +#define AOPOBJ_STATIC_POINTER 0x02 /* Data is part of an ACPI table, don't delete */ +#define AOPOBJ_DATA_VALID 0x04 /* Object is initialized and data is valid */ +#define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized, _REG was run */ +#define AOPOBJ_SETUP_COMPLETE 0x10 /* Region setup is complete */ +#define AOPOBJ_INVALID 0x20 /* Host OS won't allow a Region address */ + +/****************************************************************************** + * + * Basic data types + * + *****************************************************************************/ + +struct acpi_object_common { +ACPI_OBJECT_COMMON_HEADER}; + +struct acpi_object_integer { + ACPI_OBJECT_COMMON_HEADER u8 fill[3]; /* Prevent warning on some compilers */ + u64 value; +}; + +/* + * Note: The String and Buffer object must be identical through the + * pointer and length elements. There is code that depends on this. + * + * Fields common to both Strings and Buffers + */ +#define ACPI_COMMON_BUFFER_INFO(_type) \ + _type *pointer; \ + u32 length; + +struct acpi_object_string { /* Null terminated, ASCII characters only */ + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(char) /* String in AML stream or allocated string */ +}; + +struct acpi_object_buffer { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(u8) /* Buffer in AML stream or allocated buffer */ + u32 aml_length; + u8 *aml_start; + struct acpi_namespace_node *node; /* Link back to parent node */ +}; + +struct acpi_object_package { + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Link back to parent node */ + union acpi_operand_object **elements; /* Array of pointers to acpi_objects */ + u8 *aml_start; + u32 aml_length; + u32 count; /* # of elements in package */ +}; + +/****************************************************************************** + * + * Complex data types + * + *****************************************************************************/ + +struct acpi_object_event { + ACPI_OBJECT_COMMON_HEADER acpi_semaphore os_semaphore; /* Actual OS synchronization object */ +}; + +struct acpi_object_mutex { + ACPI_OBJECT_COMMON_HEADER u8 sync_level; /* 0-15, specified in Mutex() call */ + u16 acquisition_depth; /* Allow multiple Acquires, same thread */ + acpi_mutex os_mutex; /* Actual OS synchronization object */ + acpi_thread_id thread_id; /* Current owner of the mutex */ + struct acpi_thread_state *owner_thread; /* Current owner of the mutex */ + union acpi_operand_object *prev; /* Link for list of acquired mutexes */ + union acpi_operand_object *next; /* Link for list of acquired mutexes */ + struct acpi_namespace_node *node; /* Containing namespace node */ + u8 original_sync_level; /* Owner's original sync level (0-15) */ +}; + +struct acpi_object_region { + ACPI_OBJECT_COMMON_HEADER u8 space_id; + struct acpi_namespace_node *node; /* Containing namespace node */ + union acpi_operand_object *handler; /* Handler for region access */ + union acpi_operand_object *next; + acpi_physical_address address; + u32 length; +}; + +struct acpi_object_method { + ACPI_OBJECT_COMMON_HEADER u8 info_flags; + u8 param_count; + u8 sync_level; + union acpi_operand_object *mutex; + u8 *aml_start; + union { + acpi_internal_method implementation; + union acpi_operand_object *handler; + } dispatch; + + u32 aml_length; + u8 thread_count; + acpi_owner_id owner_id; +}; + +/* Flags for info_flags field above */ + +#define ACPI_METHOD_MODULE_LEVEL 0x01 /* Method is actually module-level code */ +#define ACPI_METHOD_INTERNAL_ONLY 0x02 /* Method is implemented internally (_OSI) */ +#define ACPI_METHOD_SERIALIZED 0x04 /* Method is serialized */ +#define ACPI_METHOD_SERIALIZED_PENDING 0x08 /* Method is to be marked serialized */ +#define ACPI_METHOD_IGNORE_SYNC_LEVEL 0x10 /* Method was auto-serialized at table load time */ +#define ACPI_METHOD_MODIFIED_NAMESPACE 0x20 /* Method modified the namespace */ + +/****************************************************************************** + * + * Objects that can be notified. All share a common notify_info area. + * + *****************************************************************************/ + +/* + * Common fields for objects that support ASL notifications + */ +#define ACPI_COMMON_NOTIFY_INFO \ + union acpi_operand_object *notify_list[2]; /* Handlers for system/device notifies */\ + union acpi_operand_object *handler; /* Handler for Address space */ + +struct acpi_object_notify_common { /* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */ +ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; + +struct acpi_object_device { + ACPI_OBJECT_COMMON_HEADER + ACPI_COMMON_NOTIFY_INFO struct acpi_gpe_block_info *gpe_block; +}; + +struct acpi_object_power_resource { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO u32 system_level; + u32 resource_order; +}; + +struct acpi_object_processor { + ACPI_OBJECT_COMMON_HEADER + /* The next two fields take advantage of the 3-byte space before NOTIFY_INFO */ + u8 proc_id; + u8 length; + ACPI_COMMON_NOTIFY_INFO acpi_io_address address; +}; + +struct acpi_object_thermal_zone { +ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; + +/****************************************************************************** + * + * Fields. All share a common header/info field. + * + *****************************************************************************/ + +/* + * Common bitfield for the field objects + * "Field Datum" -- a datum from the actual field object + * "Buffer Datum" -- a datum from a user buffer, read from or to be written to the field + */ +#define ACPI_COMMON_FIELD_INFO \ + u8 field_flags; /* Access, update, and lock bits */\ + u8 attribute; /* From access_as keyword */\ + u8 access_byte_width; /* Read/Write size in bytes */\ + struct acpi_namespace_node *node; /* Link back to parent node */\ + u32 bit_length; /* Length of field in bits */\ + u32 base_byte_offset; /* Byte offset within containing object */\ + u32 value; /* Value to store into the Bank or Index register */\ + u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\ + u8 access_length; /* For serial regions/fields */ + + +struct acpi_object_field_common { /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */ + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */ +}; + +struct acpi_object_region_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u16 resource_length; + union acpi_operand_object *region_obj; /* Containing op_region object */ + u8 *resource_buffer; /* resource_template for serial regions/fields */ + u16 pin_number_index; /* Index relative to previous Connection/Template */ +}; + +struct acpi_object_bank_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Containing op_region object */ + union acpi_operand_object *bank_obj; /* bank_select Register object */ +}; + +struct acpi_object_index_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO + /* + * No "RegionObj" pointer needed since the Index and Data registers + * are each field definitions unto themselves. + */ + union acpi_operand_object *index_obj; /* Index register */ + union acpi_operand_object *data_obj; /* Data register */ +}; + +/* The buffer_field is different in that it is part of a Buffer, not an op_region */ + +struct acpi_object_buffer_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *buffer_obj; /* Containing Buffer object */ +}; + +/****************************************************************************** + * + * Objects for handlers + * + *****************************************************************************/ + +struct acpi_object_notify_handler { + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */ + u32 handler_type; /* Type: Device/System/Both */ + acpi_notify_handler handler; /* Handler address */ + void *context; + union acpi_operand_object *next[2]; /* Device and System handler lists */ +}; + +struct acpi_object_addr_handler { + ACPI_OBJECT_COMMON_HEADER u8 space_id; + u8 handler_flags; + acpi_adr_space_handler handler; + struct acpi_namespace_node *node; /* Parent device */ + void *context; + acpi_adr_space_setup setup; + union acpi_operand_object *region_list; /* Regions using this handler */ + union acpi_operand_object *next; +}; + +/* Flags for address handler (handler_flags) */ + +#define ACPI_ADDR_HANDLER_DEFAULT_INSTALLED 0x01 + +/****************************************************************************** + * + * Special internal objects + * + *****************************************************************************/ + +/* + * The Reference object is used for these opcodes: + * Arg[0-6], Local[0-7], index_op, name_op, ref_of_op, load_op, load_table_op, debug_op + * The Reference.Class differentiates these types. + */ +struct acpi_object_reference { + ACPI_OBJECT_COMMON_HEADER u8 class; /* Reference Class */ + u8 target_type; /* Used for Index Op */ + u8 reserved; + void *object; /* name_op=>HANDLE to obj, index_op=>union acpi_operand_object */ + struct acpi_namespace_node *node; /* ref_of or Namepath */ + union acpi_operand_object **where; /* Target of Index */ + u32 value; /* Used for Local/Arg/Index/ddb_handle */ +}; + +/* Values for Reference.Class above */ + +typedef enum { + ACPI_REFCLASS_LOCAL = 0, /* Method local */ + ACPI_REFCLASS_ARG = 1, /* Method argument */ + ACPI_REFCLASS_REFOF = 2, /* Result of ref_of() TBD: Split to Ref/Node and Ref/operand_obj? */ + ACPI_REFCLASS_INDEX = 3, /* Result of Index() */ + ACPI_REFCLASS_TABLE = 4, /* ddb_handle - Load(), load_table() */ + ACPI_REFCLASS_NAME = 5, /* Reference to a named object */ + ACPI_REFCLASS_DEBUG = 6, /* Debug object */ + + ACPI_REFCLASS_MAX = 6 +} ACPI_REFERENCE_CLASSES; + +/* + * Extra object is used as additional storage for types that + * have AML code in their declarations (term_args) that must be + * evaluated at run time. + * + * Currently: Region and field_unit types + */ +struct acpi_object_extra { + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */ + struct acpi_namespace_node *scope_node; + void *region_context; /* Region-specific data */ + u8 *aml_start; + u32 aml_length; +}; + +/* Additional data that can be attached to namespace nodes */ + +struct acpi_object_data { + ACPI_OBJECT_COMMON_HEADER acpi_object_handler handler; + void *pointer; +}; + +/* Structure used when objects are cached for reuse */ + +struct acpi_object_cache_list { + ACPI_OBJECT_COMMON_HEADER union acpi_operand_object *next; /* Link for object cache and internal lists */ +}; + +/****************************************************************************** + * + * union acpi_operand_object descriptor - a giant union of all of the above + * + *****************************************************************************/ + +union acpi_operand_object { + struct acpi_object_common common; + struct acpi_object_integer integer; + struct acpi_object_string string; + struct acpi_object_buffer buffer; + struct acpi_object_package package; + struct acpi_object_event event; + struct acpi_object_method method; + struct acpi_object_mutex mutex; + struct acpi_object_region region; + struct acpi_object_notify_common common_notify; + struct acpi_object_device device; + struct acpi_object_power_resource power_resource; + struct acpi_object_processor processor; + struct acpi_object_thermal_zone thermal_zone; + struct acpi_object_field_common common_field; + struct acpi_object_region_field field; + struct acpi_object_buffer_field buffer_field; + struct acpi_object_bank_field bank_field; + struct acpi_object_index_field index_field; + struct acpi_object_notify_handler notify; + struct acpi_object_addr_handler address_space; + struct acpi_object_reference reference; + struct acpi_object_extra extra; + struct acpi_object_data data; + struct acpi_object_cache_list cache; + + /* + * Add namespace node to union in order to simplify code that accepts both + * ACPI_OPERAND_OBJECTs and ACPI_NAMESPACE_NODEs. The structures share + * a common descriptor_type field in order to differentiate them. + */ + struct acpi_namespace_node node; +}; + +/****************************************************************************** + * + * union acpi_descriptor - objects that share a common descriptor identifier + * + *****************************************************************************/ + +/* Object descriptor types */ + +#define ACPI_DESC_TYPE_CACHED 0x01 /* Used only when object is cached */ +#define ACPI_DESC_TYPE_STATE 0x02 +#define ACPI_DESC_TYPE_STATE_UPDATE 0x03 +#define ACPI_DESC_TYPE_STATE_PACKAGE 0x04 +#define ACPI_DESC_TYPE_STATE_CONTROL 0x05 +#define ACPI_DESC_TYPE_STATE_RPSCOPE 0x06 +#define ACPI_DESC_TYPE_STATE_PSCOPE 0x07 +#define ACPI_DESC_TYPE_STATE_WSCOPE 0x08 +#define ACPI_DESC_TYPE_STATE_RESULT 0x09 +#define ACPI_DESC_TYPE_STATE_NOTIFY 0x0A +#define ACPI_DESC_TYPE_STATE_THREAD 0x0B +#define ACPI_DESC_TYPE_WALK 0x0C +#define ACPI_DESC_TYPE_PARSER 0x0D +#define ACPI_DESC_TYPE_OPERAND 0x0E +#define ACPI_DESC_TYPE_NAMED 0x0F +#define ACPI_DESC_TYPE_MAX 0x0F + +struct acpi_common_descriptor { + void *common_pointer; + u8 descriptor_type; /* To differentiate various internal objs */ +}; + +union acpi_descriptor { + struct acpi_common_descriptor common; + union acpi_operand_object object; + struct acpi_namespace_node node; + union acpi_parse_object op; +}; + +#pragma pack() + +#endif /* _ACOBJECT_H */ diff --git a/kernel/drivers/acpi/acpica/acopcode.h b/kernel/drivers/acpi/acpica/acopcode.h new file mode 100644 index 000000000..fd85ad05a --- /dev/null +++ b/kernel/drivers/acpi/acpica/acopcode.h @@ -0,0 +1,329 @@ +/****************************************************************************** + * + * Name: acopcode.h - AML opcode information for the AML parser and interpreter + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACOPCODE_H__ +#define __ACOPCODE_H__ + +#define MAX_EXTENDED_OPCODE 0x88 +#define NUM_EXTENDED_OPCODE (MAX_EXTENDED_OPCODE + 1) +#define MAX_INTERNAL_OPCODE +#define NUM_INTERNAL_OPCODE (MAX_INTERNAL_OPCODE + 1) + +/* Used for non-assigned opcodes */ + +#define _UNK 0x6B + +/* + * Reserved ASCII characters. Do not use any of these for + * internal opcodes, since they are used to differentiate + * name strings from AML opcodes + */ +#define _ASC 0x6C +#define _NAM 0x6C +#define _PFX 0x6D + +/* + * All AML opcodes and the parse-time arguments for each. Used by the AML + * parser Each list is compressed into a 32-bit number and stored in the + * master opcode table (in psopcode.c). + */ +#define ARGP_ACCESSFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_ACQUIRE_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_WORDDATA) +#define ARGP_ADD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_ALIAS_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_NAME) +#define ARGP_ARG0 ARG_NONE +#define ARGP_ARG1 ARG_NONE +#define ARGP_ARG2 ARG_NONE +#define ARGP_ARG3 ARG_NONE +#define ARGP_ARG4 ARG_NONE +#define ARGP_ARG5 ARG_NONE +#define ARGP_ARG6 ARG_NONE +#define ARGP_BANK_FIELD_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_TERMARG, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_BIT_AND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NAND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_OR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_XOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BREAK_OP ARG_NONE +#define ARGP_BREAK_POINT_OP ARG_NONE +#define ARGP_BUFFER_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_BYTELIST) +#define ARGP_BYTE_OP ARGP_LIST1 (ARGP_BYTEDATA) +#define ARGP_BYTELIST_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME) +#define ARGP_CONNECTFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_CONTINUE_OP ARG_NONE +#define ARGP_COPY_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SIMPLENAME) +#define ARGP_CREATE_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_BYTE_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_DWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_FIELD_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_QWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_WORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_DATA_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_DEBUG_OP ARG_NONE +#define ARGP_DECREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_DEREF_OF_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_DEVICE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_DIVIDE_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET, ARGP_TARGET) +#define ARGP_DWORD_OP ARGP_LIST1 (ARGP_DWORDDATA) +#define ARGP_ELSE_OP ARGP_LIST2 (ARGP_PKGLENGTH, ARGP_TERMLIST) +#define ARGP_EVENT_OP ARGP_LIST1 (ARGP_NAME) +#define ARGP_EXTERNAL_OP ARGP_LIST3 (ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_BYTEDATA) +#define ARGP_FATAL_OP ARGP_LIST3 (ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_TERMARG) +#define ARGP_FIELD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_FIND_SET_LEFT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FIND_SET_RIGHT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FROM_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_IF_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_INCREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_INDEX_FIELD_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_INDEX_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_LAND_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATEREQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESS_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESSEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LNOT_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_LNOTEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LOAD_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_SUPERNAME) +#define ARGP_LOAD_TABLE_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LOCAL0 ARG_NONE +#define ARGP_LOCAL1 ARG_NONE +#define ARGP_LOCAL2 ARG_NONE +#define ARGP_LOCAL3 ARG_NONE +#define ARGP_LOCAL4 ARG_NONE +#define ARGP_LOCAL5 ARG_NONE +#define ARGP_LOCAL6 ARG_NONE +#define ARGP_LOCAL7 ARG_NONE +#define ARGP_LOR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_MATCH_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_METHOD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMLIST) +#define ARGP_METHODCALL_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_MID_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MOD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MULTIPLY_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MUTEX_OP ARGP_LIST2 (ARGP_NAME, ARGP_BYTEDATA) +#define ARGP_NAME_OP ARGP_LIST2 (ARGP_NAME, ARGP_DATAOBJ) +#define ARGP_NAMEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_NAMEPATH_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_NOOP_OP ARG_NONE +#define ARGP_NOTIFY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_ONE_OP ARG_NONE +#define ARGP_ONES_OP ARG_NONE +#define ARGP_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_BYTEDATA, ARGP_DATAOBJLIST) +#define ARGP_POWER_RES_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_WORDDATA, ARGP_OBJLIST) +#define ARGP_PROCESSOR_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_BYTEDATA, ARGP_OBJLIST) +#define ARGP_QWORD_OP ARGP_LIST1 (ARGP_QWORDDATA) +#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_RELEASE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_RESERVEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_RESET_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_RETURN_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_REVISION_OP ARG_NONE +#define ARGP_SCOPE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_TERMLIST) +#define ARGP_SERIALFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_SHIFT_LEFT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SHIFT_RIGHT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SIGNAL_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_SIZE_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_SLEEP_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_STALL_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_STATICSTRING_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_STORE_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SUPERNAME) +#define ARGP_STRING_OP ARGP_LIST1 (ARGP_CHARLIST) +#define ARGP_SUBTRACT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_THERMAL_ZONE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_TIMER_OP ARG_NONE +#define ARGP_TO_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_BUFFER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_DEC_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_HEX_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_INTEGER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_STRING_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TYPE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_UNLOAD_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_VAR_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_DATAOBJLIST) +#define ARGP_WAIT_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_WHILE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_WORD_OP ARGP_LIST1 (ARGP_WORDDATA) +#define ARGP_ZERO_OP ARG_NONE + +/* + * All AML opcodes and the runtime arguments for each. Used by the AML + * interpreter Each list is compressed into a 32-bit number and stored + * in the master opcode table (in psopcode.c). + * + * (Used by prep_operands procedure and the ASL Compiler) + */ +#define ARGI_ACCESSFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_ACQUIRE_OP ARGI_LIST2 (ARGI_MUTEX, ARGI_INTEGER) +#define ARGI_ADD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_ALIAS_OP ARGI_INVALID_OPCODE +#define ARGI_ARG0 ARG_NONE +#define ARGI_ARG1 ARG_NONE +#define ARGI_ARG2 ARG_NONE +#define ARGI_ARG3 ARG_NONE +#define ARGI_ARG4 ARG_NONE +#define ARGI_ARG5 ARG_NONE +#define ARGI_ARG6 ARG_NONE +#define ARGI_BANK_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NOT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_OR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_XOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BREAK_OP ARG_NONE +#define ARGI_BREAK_POINT_OP ARG_NONE +#define ARGI_BUFFER_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_BYTE_OP ARGI_INVALID_OPCODE +#define ARGI_BYTELIST_OP ARGI_INVALID_OPCODE +#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA, ARGI_TARGETREF) +#define ARGI_CONCAT_RES_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_BUFFER, ARGI_TARGETREF) +#define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_OBJECT_REF, ARGI_TARGETREF) +#define ARGI_CONNECTFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_CONTINUE_OP ARGI_INVALID_OPCODE +#define ARGI_COPY_OP ARGI_LIST2 (ARGI_ANYTYPE, ARGI_SIMPLE_TARGET) +#define ARGI_CREATE_BIT_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_BYTE_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_DWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_FIELD_OP ARGI_LIST4 (ARGI_BUFFER, ARGI_INTEGER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_QWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_WORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_DATA_REGION_OP ARGI_LIST3 (ARGI_STRING, ARGI_STRING, ARGI_STRING) +#define ARGI_DEBUG_OP ARG_NONE +#define ARGI_DECREMENT_OP ARGI_LIST1 (ARGI_TARGETREF) +#define ARGI_DEREF_OF_OP ARGI_LIST1 (ARGI_REF_OR_STRING) +#define ARGI_DEVICE_OP ARGI_INVALID_OPCODE +#define ARGI_DIVIDE_OP ARGI_LIST4 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF, ARGI_TARGETREF) +#define ARGI_DWORD_OP ARGI_INVALID_OPCODE +#define ARGI_ELSE_OP ARGI_INVALID_OPCODE +#define ARGI_EVENT_OP ARGI_INVALID_OPCODE +#define ARGI_EXTERNAL_OP ARGI_LIST3 (ARGI_STRING, ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_FATAL_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_FIND_SET_RIGHT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_FROM_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_IF_OP ARGI_INVALID_OPCODE +#define ARGI_INCREMENT_OP ARGI_LIST1 (ARGI_TARGETREF) +#define ARGI_INDEX_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_INDEX_OP ARGI_LIST3 (ARGI_COMPLEXOBJ, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_LAND_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_LEQUAL_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LGREATER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LGREATEREQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LLESS_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LLESSEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LNOT_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_LNOTEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LOAD_OP ARGI_LIST2 (ARGI_REGION_OR_BUFFER,ARGI_TARGETREF) +#define ARGI_LOAD_TABLE_OP ARGI_LIST6 (ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_ANYTYPE) +#define ARGI_LOCAL0 ARG_NONE +#define ARGI_LOCAL1 ARG_NONE +#define ARGI_LOCAL2 ARG_NONE +#define ARGI_LOCAL3 ARG_NONE +#define ARGI_LOCAL4 ARG_NONE +#define ARGI_LOCAL5 ARG_NONE +#define ARGI_LOCAL6 ARG_NONE +#define ARGI_LOCAL7 ARG_NONE +#define ARGI_LOR_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_MATCH_OP ARGI_LIST6 (ARGI_PACKAGE, ARGI_INTEGER, ARGI_COMPUTEDATA, ARGI_INTEGER,ARGI_COMPUTEDATA,ARGI_INTEGER) +#define ARGI_METHOD_OP ARGI_INVALID_OPCODE +#define ARGI_METHODCALL_OP ARGI_INVALID_OPCODE +#define ARGI_MID_OP ARGI_LIST4 (ARGI_BUFFER_OR_STRING,ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MOD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MULTIPLY_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MUTEX_OP ARGI_INVALID_OPCODE +#define ARGI_NAME_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEPATH_OP ARGI_INVALID_OPCODE +#define ARGI_NOOP_OP ARG_NONE +#define ARGI_NOTIFY_OP ARGI_LIST2 (ARGI_DEVICE_REF, ARGI_INTEGER) +#define ARGI_ONE_OP ARG_NONE +#define ARGI_ONES_OP ARG_NONE +#define ARGI_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_POWER_RES_OP ARGI_INVALID_OPCODE +#define ARGI_PROCESSOR_OP ARGI_INVALID_OPCODE +#define ARGI_QWORD_OP ARGI_INVALID_OPCODE +#define ARGI_REF_OF_OP ARGI_LIST1 (ARGI_OBJECT_REF) +#define ARGI_REGION_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_RELEASE_OP ARGI_LIST1 (ARGI_MUTEX) +#define ARGI_RESERVEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_RESET_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_RETURN_OP ARGI_INVALID_OPCODE +#define ARGI_REVISION_OP ARG_NONE +#define ARGI_SCOPE_OP ARGI_INVALID_OPCODE +#define ARGI_SERIALFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_SHIFT_LEFT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_SHIFT_RIGHT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_SIGNAL_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_SIZE_OF_OP ARGI_LIST1 (ARGI_DATAOBJECT) +#define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_STALL_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE +#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_TARGETREF) +#define ARGI_STRING_OP ARGI_INVALID_OPCODE +#define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE +#define ARGI_TIMER_OP ARG_NONE +#define ARGI_TO_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_TO_BUFFER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_DEC_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE) +#define ARGI_UNLOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE) +#define ARGI_VAR_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_INTEGER) +#define ARGI_WHILE_OP ARGI_INVALID_OPCODE +#define ARGI_WORD_OP ARGI_INVALID_OPCODE +#define ARGI_ZERO_OP ARG_NONE + +#endif /* __ACOPCODE_H__ */ diff --git a/kernel/drivers/acpi/acpica/acparser.h b/kernel/drivers/acpi/acpica/acparser.h new file mode 100644 index 000000000..74a390c6d --- /dev/null +++ b/kernel/drivers/acpi/acpica/acparser.h @@ -0,0 +1,254 @@ +/****************************************************************************** + * + * Module Name: acparser.h - AML Parser subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACPARSER_H__ +#define __ACPARSER_H__ + +#define OP_HAS_RETURN_VALUE 1 + +/* Variable number of arguments. This field must be 32 bits */ + +#define ACPI_VAR_ARGS ACPI_UINT32_MAX + +#define ACPI_PARSE_DELETE_TREE 0x0001 +#define ACPI_PARSE_NO_TREE_DELETE 0x0000 +#define ACPI_PARSE_TREE_MASK 0x0001 + +#define ACPI_PARSE_LOAD_PASS1 0x0010 +#define ACPI_PARSE_LOAD_PASS2 0x0020 +#define ACPI_PARSE_EXECUTE 0x0030 +#define ACPI_PARSE_MODE_MASK 0x0030 + +#define ACPI_PARSE_DEFERRED_OP 0x0100 +#define ACPI_PARSE_DISASSEMBLE 0x0200 + +#define ACPI_PARSE_MODULE_LEVEL 0x0400 + +/****************************************************************************** + * + * Parser interfaces + * + *****************************************************************************/ + +/* + * psxface - Parser external interfaces + */ +acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info); + +/* + * psargs - Parse AML opcode arguments + */ +u8 *acpi_ps_get_next_package_end(struct acpi_parse_state *parser_state); + +char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state); + +void +acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, + u32 arg_type, union acpi_parse_object *arg); + +acpi_status +acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + union acpi_parse_object *arg, u8 method_call); + +acpi_status +acpi_ps_get_next_arg(struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + u32 arg_type, union acpi_parse_object **return_arg); + +/* + * psfind + */ +union acpi_parse_object *acpi_ps_find_name(union acpi_parse_object *scope, + u32 name, u32 opcode); + +union acpi_parse_object *acpi_ps_get_parent(union acpi_parse_object *op); + +/* + * psobject - support for parse object processing + */ +acpi_status +acpi_ps_build_named_op(struct acpi_walk_state *walk_state, + u8 *aml_op_start, + union acpi_parse_object *unnamed_op, + union acpi_parse_object **op); + +acpi_status +acpi_ps_create_op(struct acpi_walk_state *walk_state, + u8 *aml_op_start, union acpi_parse_object **new_op); + +acpi_status +acpi_ps_complete_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **op, acpi_status status); + +acpi_status +acpi_ps_complete_final_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, acpi_status status); + +/* + * psopinfo - AML Opcode information + */ +const struct acpi_opcode_info *acpi_ps_get_opcode_info(u16 opcode); + +char *acpi_ps_get_opcode_name(u16 opcode); + +u8 acpi_ps_get_argument_count(u32 op_type); + +/* + * psparse - top level parsing routines + */ +acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state); + +u32 acpi_ps_get_opcode_size(u32 opcode); + +u16 acpi_ps_peek_opcode(struct acpi_parse_state *state); + +acpi_status +acpi_ps_complete_this_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ps_next_parse_state(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + acpi_status callback_status); + +/* + * psloop - main parse loop + */ +acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state); + +/* + * psscope - Scope stack management routines + */ +acpi_status +acpi_ps_init_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object *root); + +union acpi_parse_object *acpi_ps_get_parent_scope(struct acpi_parse_state + *state); + +u8 acpi_ps_has_completed_scope(struct acpi_parse_state *parser_state); + +void +acpi_ps_pop_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object **op, u32 *arg_list, u32 *arg_count); + +acpi_status +acpi_ps_push_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object *op, + u32 remaining_args, u32 arg_count); + +void acpi_ps_cleanup_scope(struct acpi_parse_state *state); + +/* + * pstree - parse tree manipulation routines + */ +void +acpi_ps_append_arg(union acpi_parse_object *op, union acpi_parse_object *arg); + +union acpi_parse_object *acpi_ps_find(union acpi_parse_object *scope, + char *path, u16 opcode, u32 create); + +union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn); + +#ifdef ACPI_FUTURE_USAGE +union acpi_parse_object *acpi_ps_get_depth_next(union acpi_parse_object *origin, + union acpi_parse_object *op); +#endif /* ACPI_FUTURE_USAGE */ + +/* + * pswalk - parse tree walk routines + */ +acpi_status +acpi_ps_walk_parsed_aml(union acpi_parse_object *start_op, + union acpi_parse_object *end_op, + union acpi_operand_object *mth_desc, + struct acpi_namespace_node *start_node, + union acpi_operand_object **params, + union acpi_operand_object **caller_return_desc, + acpi_owner_id owner_id, + acpi_parse_downwards descending_callback, + acpi_parse_upwards ascending_callback); + +acpi_status +acpi_ps_get_next_walk_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + acpi_parse_upwards ascending_callback); + +acpi_status acpi_ps_delete_completed_op(struct acpi_walk_state *walk_state); + +void acpi_ps_delete_parse_tree(union acpi_parse_object *root); + +/* + * psutils - parser utilities + */ +union acpi_parse_object *acpi_ps_create_scope_op(void); + +void acpi_ps_init_op(union acpi_parse_object *op, u16 opcode); + +union acpi_parse_object *acpi_ps_alloc_op(u16 opcode); + +void acpi_ps_free_op(union acpi_parse_object *op); + +u8 acpi_ps_is_leading_char(u32 c); + +#ifdef ACPI_FUTURE_USAGE +u32 acpi_ps_get_name(union acpi_parse_object *op); +#endif /* ACPI_FUTURE_USAGE */ + +void acpi_ps_set_name(union acpi_parse_object *op, u32 name); + +/* + * psdump - display parser tree + */ +u32 +acpi_ps_sprint_path(char *buffer_start, + u32 buffer_size, union acpi_parse_object *op); + +u32 +acpi_ps_sprint_op(char *buffer_start, + u32 buffer_size, union acpi_parse_object *op); + +void acpi_ps_show(union acpi_parse_object *op); + +#endif /* __ACPARSER_H__ */ diff --git a/kernel/drivers/acpi/acpica/acpredef.h b/kernel/drivers/acpi/acpica/acpredef.h new file mode 100644 index 000000000..a972d11c9 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acpredef.h @@ -0,0 +1,1101 @@ +/****************************************************************************** + * + * Name: acpredef - Information table for ACPI predefined methods and objects + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACPREDEF_H__ +#define __ACPREDEF_H__ + +/****************************************************************************** + * + * Return Package types + * + * 1) PTYPE1 packages do not contain subpackages. + * + * ACPI_PTYPE1_FIXED: Fixed-length length, 1 or 2 object types: + * object type + * count + * object type + * count + * + * ACPI_PTYPE1_VAR: Variable-length length. Zero-length package is allowed: + * object type (Int/Buf/Ref) + * + * ACPI_PTYPE1_OPTION: Package has some required and some optional elements + * (Used for _PRW) + * + * + * 2) PTYPE2 packages contain a Variable-length number of subpackages. Each + * of the different types describe the contents of each of the subpackages. + * + * ACPI_PTYPE2: Each subpackage contains 1 or 2 object types. Zero-length + * parent package is allowed: + * object type + * count + * object type + * count + * (Used for _ALR,_MLS,_PSS,_TRT,_TSS) + * + * ACPI_PTYPE2_COUNT: Each subpackage has a count as first element. + * Zero-length parent package is allowed: + * object type + * (Used for _CSD,_PSD,_TSD) + * + * ACPI_PTYPE2_PKG_COUNT: Count of subpackages at start, 1 or 2 object types: + * object type + * count + * object type + * count + * (Used for _CST) + * + * ACPI_PTYPE2_FIXED: Each subpackage is of Fixed-length. Zero-length + * parent package is allowed. + * (Used for _PRT) + * + * ACPI_PTYPE2_MIN: Each subpackage has a Variable-length but minimum length. + * Zero-length parent package is allowed: + * (Used for _HPX) + * + * ACPI_PTYPE2_REV_FIXED: Revision at start, each subpackage is Fixed-length + * (Used for _ART, _FPS) + * + * ACPI_PTYPE2_FIX_VAR: Each subpackage consists of some fixed-length elements + * followed by an optional element. Zero-length parent package is allowed. + * object type + * count + * object type + * count = 0 (optional) + * (Used for _DLM) + * + * ACPI_PTYPE2_UUID_PAIR: Each subpackage is preceded by a UUID Buffer. The UUID + * defines the format of the package. Zero-length parent package is + * allowed. + * (Used for _DSD) + * + *****************************************************************************/ + +enum acpi_return_package_types { + ACPI_PTYPE1_FIXED = 1, + ACPI_PTYPE1_VAR = 2, + ACPI_PTYPE1_OPTION = 3, + ACPI_PTYPE2 = 4, + ACPI_PTYPE2_COUNT = 5, + ACPI_PTYPE2_PKG_COUNT = 6, + ACPI_PTYPE2_FIXED = 7, + ACPI_PTYPE2_MIN = 8, + ACPI_PTYPE2_REV_FIXED = 9, + ACPI_PTYPE2_FIX_VAR = 10, + ACPI_PTYPE2_UUID_PAIR = 11 +}; + +/* Support macros for users of the predefined info table */ + +#define METHOD_PREDEF_ARGS_MAX 4 +#define METHOD_ARG_BIT_WIDTH 3 +#define METHOD_ARG_MASK 0x0007 +#define ARG_COUNT_IS_MINIMUM 0x8000 +#define METHOD_MAX_ARG_TYPE ACPI_TYPE_PACKAGE + +#define METHOD_GET_ARG_COUNT(arg_list) ((arg_list) & METHOD_ARG_MASK) +#define METHOD_GET_NEXT_TYPE(arg_list) (((arg_list) >>= METHOD_ARG_BIT_WIDTH) & METHOD_ARG_MASK) + +/* Macros used to build the predefined info table */ + +#define METHOD_0ARGS 0 +#define METHOD_1ARGS(a1) (1 | (a1 << 3)) +#define METHOD_2ARGS(a1,a2) (2 | (a1 << 3) | (a2 << 6)) +#define METHOD_3ARGS(a1,a2,a3) (3 | (a1 << 3) | (a2 << 6) | (a3 << 9)) +#define METHOD_4ARGS(a1,a2,a3,a4) (4 | (a1 << 3) | (a2 << 6) | (a3 << 9) | (a4 << 12)) + +#define METHOD_RETURNS(type) (type) +#define METHOD_NO_RETURN_VALUE 0 + +#define PACKAGE_INFO(a,b,c,d,e,f) {{{(a),(b),(c),(d)}, ((((u16)(f)) << 8) | (e)), 0}} + +/* Support macros for the resource descriptor info table */ + +#define WIDTH_1 0x0001 +#define WIDTH_2 0x0002 +#define WIDTH_3 0x0004 +#define WIDTH_8 0x0008 +#define WIDTH_16 0x0010 +#define WIDTH_32 0x0020 +#define WIDTH_64 0x0040 +#define VARIABLE_DATA 0x0080 +#define NUM_RESOURCE_WIDTHS 8 + +#define WIDTH_ADDRESS WIDTH_16 | WIDTH_32 | WIDTH_64 + +#ifdef ACPI_CREATE_PREDEFINED_TABLE +/****************************************************************************** + * + * Predefined method/object information table. + * + * These are the names that can actually be evaluated via acpi_evaluate_object. + * Not present in this table are the following: + * + * 1) Predefined/Reserved names that are never evaluated via + * acpi_evaluate_object: + * _Lxx and _Exx GPE methods + * _Qxx EC methods + * _T_x compiler temporary variables + * _Wxx wake events + * + * 2) Predefined names that never actually exist within the AML code: + * Predefined resource descriptor field names + * + * 3) Predefined names that are implemented within ACPICA: + * _OSI + * + * The main entries in the table each contain the following items: + * + * name - The ACPI reserved name + * argument_list - Contains (in 16 bits), the number of required + * arguments to the method (3 bits), and a 3-bit type + * field for each argument (up to 4 arguments). The + * METHOD_?ARGS macros generate the correct packed data. + * expected_btypes - Allowed type(s) for the return value. + * 0 means that no return value is expected. + * + * For methods that return packages, the next entry in the table contains + * information about the expected structure of the package. This information + * is saved here (rather than in a separate table) in order to minimize the + * overall size of the stored data. + * + * Note: The additional braces are intended to promote portability. + * + * Note2: Table is used by the kernel-resident subsystem, the iASL compiler, + * and the acpi_help utility. + * + * TBD: _PRT - currently ignore reversed entries. Attempt to fix in nsrepair. + * Possibly fixing package elements like _BIF, etc. + * + *****************************************************************************/ + +const union acpi_predefined_info acpi_gbl_predefined_methods[] = { + {{"_AC0", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC1", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC2", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC3", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC4", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC5", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC6", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC7", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC8", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AC9", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_ADR", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_AEI", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_AL0", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL1", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL2", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL3", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL4", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL5", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL6", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL7", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL8", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_AL9", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_ALC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_ALI", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_ALP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_ALR", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 2 (Ints) */ + PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 2, 0, 0, 0), + + {{"_ALT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_ART", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Ref/11 Int) */ + PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_REFERENCE, 2, + ACPI_RTYPE_INTEGER, 11, 0), + + {{"_BBN", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_BCL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0), + + {{"_BCM", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_BCT", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_BDN", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_BFS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_BIF", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (9 Int),(4 Str) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 9, + ACPI_RTYPE_STRING, 4, 0), + + {{"_BIX", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (16 Int),(4 Str) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, + ACPI_RTYPE_STRING, 4, 0), + + {{"_BLT", + METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_BMA", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_BMC", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_BMD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (5 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5, 0, 0, 0), + + {{"_BMS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_BQC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_BST", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0), + + {{"_BTM", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_BTP", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_CBA", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See PCI firmware spec 3.0 */ + + {{"_CCA", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* ACPI 5.1 */ + + {{"_CDM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_CID", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints/Strs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0, + 0, 0, 0), + + {{"_CLS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0, 0, 0), + + {{"_CPC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints/Bufs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER, 0, + 0, 0, 0), + + {{"_CRS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_CRT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_CSD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(n), n-1 Int) */ + PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0), + + {{"_CST", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(n), n Pkg (1 Buf/3 Int) */ + PACKAGE_INFO(ACPI_PTYPE2_PKG_COUNT, ACPI_RTYPE_BUFFER, 1, + ACPI_RTYPE_INTEGER, 3, 0), + + {{"_CWS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_DCK", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_DCS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_DDC", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER)}}, + + {{"_DDN", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_STRING)}}, + + {{"_DEP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_DGS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_DIS", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_DLM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (1 Ref, 0/1 Optional Buf/Ref) */ + PACKAGE_INFO(ACPI_PTYPE2_FIX_VAR, ACPI_RTYPE_REFERENCE, 1, + ACPI_RTYPE_REFERENCE | ACPI_RTYPE_BUFFER, 0, 0), + + {{"_DMA", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_DOD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0), + + {{"_DOS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_DSD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Buf, 1 Pkg */ + PACKAGE_INFO(ACPI_PTYPE2_UUID_PAIR, ACPI_RTYPE_BUFFER, 1, + ACPI_RTYPE_PACKAGE, 1, 0), + + {{"_DSM", + METHOD_4ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, + ACPI_TYPE_PACKAGE), + METHOD_RETURNS(ACPI_RTYPE_ALL)}}, /* Must return a value, but it can be of any type */ + + {{"_DSS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_DSW", + METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_DTI", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_EC_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_EDL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_EJ0", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_EJ1", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_EJ2", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_EJ3", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_EJ4", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_EJD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_STRING)}}, + + {{"_ERR", + METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_STRING, ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* Internal use only, used by ACPICA test suites */ + + {{"_EVT", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_FDE", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_FDI", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (16 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, 0, 0, 0), + + {{"_FDM", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_FIF", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0), + + {{"_FIX", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Ints) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0), + + {{"_FPS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (5 Int) */ + PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 5, 0, 0, 0), + + {{"_FSL", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_FST", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, 0, 0, 0), + + {{"_GAI", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_GCP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_GHL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_GLK", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_GPD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_GPE", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* _GPE method, not _GPE scope */ + + {{"_GRT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_GSB", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_GTF", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_GTM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_GTS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_GWS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_HID", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING)}}, + + {{"_HOT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_HPP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0), + + /* + * For _HPX, a single package is returned, containing a variable-length number + * of subpackages. Each subpackage contains a PCI record setting. + * There are several different type of record settings, of different + * lengths, but all elements of all settings are Integers. + */ + {{"_HPX", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (var Ints) */ + PACKAGE_INFO(ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5, 0, 0, 0), + + {{"_HRV", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_IFT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See IPMI spec */ + + {{"_INI", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_IRC", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_LCK", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_LID", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_LPD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Int) */ + PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0), + + {{"_MAT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_MBM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (8 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 8, 0, 0, 0), + + {{"_MLS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (1 Str/1 Buf) */ + PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_STRING, 1, ACPI_RTYPE_BUFFER, 1, + 0), + + {{"_MSG", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_MSM", + METHOD_4ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, + ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_NTT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_OFF", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_ON_", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_OS_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_STRING)}}, + + {{"_OSC", + METHOD_4ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, + ACPI_TYPE_BUFFER), + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_OST", + METHOD_3ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER, ACPI_TYPE_BUFFER), + METHOD_NO_RETURN_VALUE}}, + + {{"_PAI", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PCL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PCT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Buf) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0), + + {{"_PDC", METHOD_1ARGS(ACPI_TYPE_BUFFER), + METHOD_NO_RETURN_VALUE}}, + + {{"_PDL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PIC", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_PIF", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (3 Int),(3 Str) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 3, + ACPI_RTYPE_STRING, 3, 0), + + {{"_PLD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Bufs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_BUFFER, 0, 0, 0, 0), + + {{"_PMC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (11 Int),(3 Str) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 11, + ACPI_RTYPE_STRING, 3, 0), + + {{"_PMD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PMM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PPC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PPE", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See dig64 spec */ + + {{"_PR0", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PR1", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PR2", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PR3", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PRE", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PRL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PRS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + /* + * For _PRT, many BIOSs reverse the 3rd and 4th Package elements (Source + * and source_index). This bug is so prevalent that there is code in the + * ACPICA Resource Manager to detect this and switch them back. For now, + * do not allow and issue a warning. To allow this and eliminate the + * warning, add the ACPI_RTYPE_REFERENCE type to the 4th element (index 3) + * in the statement below. + */ + {{"_PRT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (4): Int,Int,Int/Ref,Int */ + PACKAGE_INFO(ACPI_PTYPE2_FIXED, 4, ACPI_RTYPE_INTEGER, + ACPI_RTYPE_INTEGER, + ACPI_RTYPE_INTEGER | ACPI_RTYPE_REFERENCE, + ACPI_RTYPE_INTEGER), + + {{"_PRW", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: Pkg/Int,Int,[Variable-length Refs] (Pkg is Ref/Int) */ + PACKAGE_INFO(ACPI_PTYPE1_OPTION, 2, + ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE, + ACPI_RTYPE_INTEGER, ACPI_RTYPE_REFERENCE, 0), + + {{"_PS0", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_PS1", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_PS2", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_PS3", METHOD_0ARGS, + METHOD_NO_RETURN_VALUE}}, + + {{"_PSC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PSD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (5 Int) with count */ + PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0), + + {{"_PSE", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_PSL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_PSR", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PSS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (6 Int) */ + PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 6, 0, 0, 0), + + {{"_PSV", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PSW", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_PTC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Buf) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0), + + {{"_PTP", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_PTS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_PUR", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (2 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0), + + {{"_PXM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_REG", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_REV", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_RMV", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_ROM", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_RTV", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + /* + * For _S0_ through _S5_, the ACPI spec defines a return Package + * containing 1 Integer, but most DSDTs have it wrong - 2,3, or 4 integers. + * Allow this by making the objects "Variable-length length", but all elements + * must be Integers. + */ + {{"_S0_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0), + + {{"_S1_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0), + + {{"_S2_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0), + + {{"_S3_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0), + + {{"_S4_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0), + + {{"_S5_", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0), + + {{"_S1D", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S2D", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S3D", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S4D", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S0W", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S1W", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S2W", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S3W", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_S4W", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SBS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SCP", METHOD_1ARGS(ACPI_TYPE_INTEGER) | ARG_COUNT_IS_MINIMUM, + METHOD_NO_RETURN_VALUE}}, /* Acpi 1.0 allowed 1 integer arg. Acpi 3.0 expanded to 3 args. Allow both. */ + + {{"_SDD", METHOD_1ARGS(ACPI_TYPE_BUFFER), + METHOD_NO_RETURN_VALUE}}, + + {{"_SEG", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SHL", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SLI", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_SPD", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SRS", METHOD_1ARGS(ACPI_TYPE_BUFFER), + METHOD_NO_RETURN_VALUE}}, + + {{"_SRT", METHOD_1ARGS(ACPI_TYPE_BUFFER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SRV", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, /* See IPMI spec */ + + {{"_SST", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_STA", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_STM", + METHOD_3ARGS(ACPI_TYPE_BUFFER, ACPI_TYPE_BUFFER, ACPI_TYPE_BUFFER), + METHOD_NO_RETURN_VALUE}}, + + {{"_STP", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_STR", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_STV", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SUB", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_STRING)}}, + + {{"_SUN", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_SWS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TC1", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TC2", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TDL", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TIP", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TIV", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TMP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TPC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TPT", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_TRT", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 2 Ref/6 Int */ + PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_REFERENCE, 2, ACPI_RTYPE_INTEGER, + 6, 0), + + {{"_TSD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int with count */ + PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 5, 0, 0, 0), + + {{"_TSP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TSS", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int */ + PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 5, 0, 0, 0), + + {{"_TST", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_TTS", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_NO_RETURN_VALUE}}, + + {{"_TZD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ + PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + + {{"_TZM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_REFERENCE)}}, + + {{"_TZP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_UID", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING)}}, + + {{"_UPC", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0), + + {{"_UPD", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_UPP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + {{"_VPO", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + + /* Acpi 1.0 defined _WAK with no return value. Later, it was changed to return a package */ + + {{"_WAK", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_NONE | ACPI_RTYPE_INTEGER | + ACPI_RTYPE_PACKAGE)}}, + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0), /* Fixed-length (2 Int), but is optional */ + + /* _WDG/_WED are MS extensions defined by "Windows Instrumentation" */ + + {{"_WDG", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + + {{"_WED", METHOD_1ARGS(ACPI_TYPE_INTEGER), + METHOD_RETURNS(ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | + ACPI_RTYPE_BUFFER)}}, + + PACKAGE_INFO(0, 0, 0, 0, 0, 0) /* Table terminator */ +}; +#else +extern const union acpi_predefined_info acpi_gbl_predefined_methods[]; +#endif + +#if (defined ACPI_CREATE_RESOURCE_TABLE && defined ACPI_APPLICATION) +/****************************************************************************** + * + * Predefined names for use in Resource Descriptors. These names do not + * appear in the global Predefined Name table (since these names never + * appear in actual AML byte code, only in the original ASL) + * + * Note: Used by iASL compiler and acpi_help utility only. + * + *****************************************************************************/ + +const union acpi_predefined_info acpi_gbl_resource_names[] = { + {{"_ADR", WIDTH_16 | WIDTH_64, 0}}, + {{"_ALN", WIDTH_8 | WIDTH_16 | WIDTH_32, 0}}, + {{"_ASI", WIDTH_8, 0}}, + {{"_ASZ", WIDTH_8, 0}}, + {{"_ATT", WIDTH_64, 0}}, + {{"_BAS", WIDTH_16 | WIDTH_32, 0}}, + {{"_BM_", WIDTH_1, 0}}, + {{"_DBT", WIDTH_16, 0}}, /* Acpi 5.0 */ + {{"_DEC", WIDTH_1, 0}}, + {{"_DMA", WIDTH_8, 0}}, + {{"_DPL", WIDTH_1, 0}}, /* Acpi 5.0 */ + {{"_DRS", WIDTH_16, 0}}, /* Acpi 5.0 */ + {{"_END", WIDTH_1, 0}}, /* Acpi 5.0 */ + {{"_FLC", WIDTH_2, 0}}, /* Acpi 5.0 */ + {{"_GRA", WIDTH_ADDRESS, 0}}, + {{"_HE_", WIDTH_1, 0}}, + {{"_INT", WIDTH_16 | WIDTH_32, 0}}, + {{"_IOR", WIDTH_2, 0}}, /* Acpi 5.0 */ + {{"_LEN", WIDTH_8 | WIDTH_ADDRESS, 0}}, + {{"_LIN", WIDTH_8, 0}}, /* Acpi 5.0 */ + {{"_LL_", WIDTH_1, 0}}, + {{"_MAF", WIDTH_1, 0}}, + {{"_MAX", WIDTH_ADDRESS, 0}}, + {{"_MEM", WIDTH_2, 0}}, + {{"_MIF", WIDTH_1, 0}}, + {{"_MIN", WIDTH_ADDRESS, 0}}, + {{"_MOD", WIDTH_1, 0}}, /* Acpi 5.0 */ + {{"_MTP", WIDTH_2, 0}}, + {{"_PAR", WIDTH_8, 0}}, /* Acpi 5.0 */ + {{"_PHA", WIDTH_1, 0}}, /* Acpi 5.0 */ + {{"_PIN", WIDTH_16, 0}}, /* Acpi 5.0 */ + {{"_PPI", WIDTH_8, 0}}, /* Acpi 5.0 */ + {{"_POL", WIDTH_1 | WIDTH_2, 0}}, /* Acpi 5.0 */ + {{"_RBO", WIDTH_8, 0}}, + {{"_RBW", WIDTH_8, 0}}, + {{"_RNG", WIDTH_1, 0}}, + {{"_RT_", WIDTH_8, 0}}, /* Acpi 3.0 */ + {{"_RW_", WIDTH_1, 0}}, + {{"_RXL", WIDTH_16, 0}}, /* Acpi 5.0 */ + {{"_SHR", WIDTH_2, 0}}, + {{"_SIZ", WIDTH_2, 0}}, + {{"_SLV", WIDTH_1, 0}}, /* Acpi 5.0 */ + {{"_SPE", WIDTH_32, 0}}, /* Acpi 5.0 */ + {{"_STB", WIDTH_2, 0}}, /* Acpi 5.0 */ + {{"_TRA", WIDTH_ADDRESS, 0}}, + {{"_TRS", WIDTH_1, 0}}, + {{"_TSF", WIDTH_8, 0}}, /* Acpi 3.0 */ + {{"_TTP", WIDTH_1, 0}}, + {{"_TXL", WIDTH_16, 0}}, /* Acpi 5.0 */ + {{"_TYP", WIDTH_2 | WIDTH_16, 0}}, + {{"_VEN", VARIABLE_DATA, 0}}, /* Acpi 5.0 */ + PACKAGE_INFO(0, 0, 0, 0, 0, 0) /* Table terminator */ +}; + +static const union acpi_predefined_info acpi_gbl_scope_names[] = { + {{"_GPE", 0, 0}}, + {{"_PR_", 0, 0}}, + {{"_SB_", 0, 0}}, + {{"_SI_", 0, 0}}, + {{"_TZ_", 0, 0}}, + PACKAGE_INFO(0, 0, 0, 0, 0, 0) /* Table terminator */ +}; +#else +extern const union acpi_predefined_info acpi_gbl_resource_names[]; +#endif + +#endif diff --git a/kernel/drivers/acpi/acpica/acresrc.h b/kernel/drivers/acpi/acpica/acresrc.h new file mode 100644 index 000000000..6357efb01 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acresrc.h @@ -0,0 +1,383 @@ +/****************************************************************************** + * + * Name: acresrc.h - Resource Manager function prototypes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACRESRC_H__ +#define __ACRESRC_H__ + +/* Need the AML resource descriptor structs */ + +#include "amlresrc.h" + +/* + * If possible, pack the following structures to byte alignment, since we + * don't care about performance for debug output. Two cases where we cannot + * pack the structures: + * + * 1) Hardware does not support misaligned memory transfers + * 2) Compiler does not support pointers within packed structures + */ +#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED)) +#pragma pack(1) +#endif + +/* + * Individual entry for the resource conversion tables + */ +typedef const struct acpi_rsconvert_info { + u8 opcode; + u8 resource_offset; + u8 aml_offset; + u8 value; + +} acpi_rsconvert_info; + +/* Resource conversion opcodes */ + +typedef enum { + ACPI_RSC_INITGET = 0, + ACPI_RSC_INITSET, + ACPI_RSC_FLAGINIT, + ACPI_RSC_1BITFLAG, + ACPI_RSC_2BITFLAG, + ACPI_RSC_3BITFLAG, + ACPI_RSC_ADDRESS, + ACPI_RSC_BITMASK, + ACPI_RSC_BITMASK16, + ACPI_RSC_COUNT, + ACPI_RSC_COUNT16, + ACPI_RSC_COUNT_GPIO_PIN, + ACPI_RSC_COUNT_GPIO_RES, + ACPI_RSC_COUNT_GPIO_VEN, + ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RSC_DATA8, + ACPI_RSC_EXIT_EQ, + ACPI_RSC_EXIT_LE, + ACPI_RSC_EXIT_NE, + ACPI_RSC_LENGTH, + ACPI_RSC_MOVE_GPIO_PIN, + ACPI_RSC_MOVE_GPIO_RES, + ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RSC_MOVE8, + ACPI_RSC_MOVE16, + ACPI_RSC_MOVE32, + ACPI_RSC_MOVE64, + ACPI_RSC_SET8, + ACPI_RSC_SOURCE, + ACPI_RSC_SOURCEX +} ACPI_RSCONVERT_OPCODES; + +/* Resource Conversion sub-opcodes */ + +#define ACPI_RSC_COMPARE_AML_LENGTH 0 +#define ACPI_RSC_COMPARE_VALUE 1 + +#define ACPI_RSC_TABLE_SIZE(d) (sizeof (d) / sizeof (struct acpi_rsconvert_info)) + +#define ACPI_RS_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_resource,f) +#define AML_OFFSET(f) (u8) ACPI_OFFSET (union aml_resource,f) + +/* + * Individual entry for the resource dump tables + */ +typedef const struct acpi_rsdump_info { + u8 opcode; + u8 offset; + char *name; + const char **pointer; + +} acpi_rsdump_info; + +/* Values for the Opcode field above */ + +typedef enum { + ACPI_RSD_TITLE = 0, + ACPI_RSD_1BITFLAG, + ACPI_RSD_2BITFLAG, + ACPI_RSD_3BITFLAG, + ACPI_RSD_ADDRESS, + ACPI_RSD_DWORDLIST, + ACPI_RSD_LITERAL, + ACPI_RSD_LONGLIST, + ACPI_RSD_SHORTLIST, + ACPI_RSD_SHORTLISTX, + ACPI_RSD_SOURCE, + ACPI_RSD_STRING, + ACPI_RSD_UINT8, + ACPI_RSD_UINT16, + ACPI_RSD_UINT32, + ACPI_RSD_UINT64, + ACPI_RSD_WORDLIST +} ACPI_RSDUMP_OPCODES; + +/* restore default alignment */ + +#pragma pack() + +/* Resource tables indexed by internal resource type */ + +extern const u8 acpi_gbl_aml_resource_sizes[]; +extern const u8 acpi_gbl_aml_resource_serial_bus_sizes[]; +extern struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[]; + +/* Resource tables indexed by raw AML resource descriptor type */ + +extern const u8 acpi_gbl_resource_struct_sizes[]; +extern const u8 acpi_gbl_resource_struct_serial_bus_sizes[]; +extern struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[]; + +extern struct acpi_rsconvert_info + *acpi_gbl_convert_resource_serial_bus_dispatch[]; + +struct acpi_vendor_walk_info { + struct acpi_vendor_uuid *uuid; + struct acpi_buffer *buffer; + acpi_status status; +}; + +/* + * rscreate + */ +acpi_status +acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer, + struct acpi_buffer *output_buffer); + +acpi_status +acpi_rs_create_aml_resources(struct acpi_buffer *resource_list, + struct acpi_buffer *output_buffer); + +acpi_status +acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, + struct acpi_buffer *output_buffer); + +/* + * rsutils + */ + +acpi_status +acpi_rs_get_prt_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_get_crs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_get_method_data(acpi_handle handle, + char *path, struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_set_srs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_get_aei_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +/* + * rscalc + */ +acpi_status +acpi_rs_get_list_length(u8 * aml_buffer, + u32 aml_buffer_length, acpi_size * size_needed); + +acpi_status +acpi_rs_get_aml_length(struct acpi_resource *resource_list, + acpi_size resource_list_size, acpi_size * size_needed); + +acpi_status +acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, + acpi_size * buffer_size_needed); + +acpi_status +acpi_rs_convert_aml_to_resources(u8 * aml, + u32 length, + u32 offset, u8 resource_index, void **context); + +acpi_status +acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, + acpi_size aml_size_needed, u8 * output_buffer); + +/* + * rsaddr + */ +void +acpi_rs_set_address_common(union aml_resource *aml, + struct acpi_resource *resource); + +u8 +acpi_rs_get_address_common(struct acpi_resource *resource, + union aml_resource *aml); + +/* + * rsmisc + */ +acpi_status +acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, + union aml_resource *aml, + struct acpi_rsconvert_info *info); + +acpi_status +acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, + union aml_resource *aml, + struct acpi_rsconvert_info *info); + +/* + * rsutils + */ +void +acpi_rs_move_data(void *destination, + void *source, u16 item_count, u8 move_type); + +u8 acpi_rs_decode_bitmask(u16 mask, u8 * list); + +u16 acpi_rs_encode_bitmask(u8 * list, u8 count); + +acpi_rs_length +acpi_rs_get_resource_source(acpi_rs_length resource_length, + acpi_rs_length minimum_length, + struct acpi_resource_source *resource_source, + union aml_resource *aml, char *string_ptr); + +acpi_rsdesc_size +acpi_rs_set_resource_source(union aml_resource *aml, + acpi_rs_length minimum_length, + struct acpi_resource_source *resource_source); + +void +acpi_rs_set_resource_header(u8 descriptor_type, + acpi_rsdesc_size total_length, + union aml_resource *aml); + +void +acpi_rs_set_resource_length(acpi_rsdesc_size total_length, + union aml_resource *aml); + +/* + * rsdump - Debugger support + */ +#ifdef ACPI_DEBUGGER +void acpi_rs_dump_resource_list(struct acpi_resource *resource); + +void acpi_rs_dump_irq_list(u8 *route_table); +#endif + +/* + * Resource conversion tables + */ +extern struct acpi_rsconvert_info acpi_rs_convert_dma[]; +extern struct acpi_rsconvert_info acpi_rs_convert_end_dpf[]; +extern struct acpi_rsconvert_info acpi_rs_convert_io[]; +extern struct acpi_rsconvert_info acpi_rs_convert_fixed_io[]; +extern struct acpi_rsconvert_info acpi_rs_convert_end_tag[]; +extern struct acpi_rsconvert_info acpi_rs_convert_memory24[]; +extern struct acpi_rsconvert_info acpi_rs_convert_generic_reg[]; +extern struct acpi_rsconvert_info acpi_rs_convert_memory32[]; +extern struct acpi_rsconvert_info acpi_rs_convert_fixed_memory32[]; +extern struct acpi_rsconvert_info acpi_rs_convert_address32[]; +extern struct acpi_rsconvert_info acpi_rs_convert_address16[]; +extern struct acpi_rsconvert_info acpi_rs_convert_ext_irq[]; +extern struct acpi_rsconvert_info acpi_rs_convert_address64[]; +extern struct acpi_rsconvert_info acpi_rs_convert_ext_address64[]; +extern struct acpi_rsconvert_info acpi_rs_convert_gpio[]; +extern struct acpi_rsconvert_info acpi_rs_convert_fixed_dma[]; +extern struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[]; +extern struct acpi_rsconvert_info acpi_rs_convert_spi_serial_bus[]; +extern struct acpi_rsconvert_info acpi_rs_convert_uart_serial_bus[]; + +/* These resources require separate get/set tables */ + +extern struct acpi_rsconvert_info acpi_rs_get_irq[]; +extern struct acpi_rsconvert_info acpi_rs_get_start_dpf[]; +extern struct acpi_rsconvert_info acpi_rs_get_vendor_small[]; +extern struct acpi_rsconvert_info acpi_rs_get_vendor_large[]; + +extern struct acpi_rsconvert_info acpi_rs_set_irq[]; +extern struct acpi_rsconvert_info acpi_rs_set_start_dpf[]; +extern struct acpi_rsconvert_info acpi_rs_set_vendor[]; + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +/* + * rsinfo + */ +extern struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[]; +extern struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[]; + +/* + * rsdumpinfo + */ +extern struct acpi_rsdump_info acpi_rs_dump_irq[]; +extern struct acpi_rsdump_info acpi_rs_dump_prt[]; +extern struct acpi_rsdump_info acpi_rs_dump_dma[]; +extern struct acpi_rsdump_info acpi_rs_dump_start_dpf[]; +extern struct acpi_rsdump_info acpi_rs_dump_end_dpf[]; +extern struct acpi_rsdump_info acpi_rs_dump_io[]; +extern struct acpi_rsdump_info acpi_rs_dump_io_flags[]; +extern struct acpi_rsdump_info acpi_rs_dump_fixed_io[]; +extern struct acpi_rsdump_info acpi_rs_dump_vendor[]; +extern struct acpi_rsdump_info acpi_rs_dump_end_tag[]; +extern struct acpi_rsdump_info acpi_rs_dump_memory24[]; +extern struct acpi_rsdump_info acpi_rs_dump_memory32[]; +extern struct acpi_rsdump_info acpi_rs_dump_memory_flags[]; +extern struct acpi_rsdump_info acpi_rs_dump_fixed_memory32[]; +extern struct acpi_rsdump_info acpi_rs_dump_address16[]; +extern struct acpi_rsdump_info acpi_rs_dump_address32[]; +extern struct acpi_rsdump_info acpi_rs_dump_address64[]; +extern struct acpi_rsdump_info acpi_rs_dump_ext_address64[]; +extern struct acpi_rsdump_info acpi_rs_dump_ext_irq[]; +extern struct acpi_rsdump_info acpi_rs_dump_generic_reg[]; +extern struct acpi_rsdump_info acpi_rs_dump_gpio[]; +extern struct acpi_rsdump_info acpi_rs_dump_fixed_dma[]; +extern struct acpi_rsdump_info acpi_rs_dump_common_serial_bus[]; +extern struct acpi_rsdump_info acpi_rs_dump_i2c_serial_bus[]; +extern struct acpi_rsdump_info acpi_rs_dump_spi_serial_bus[]; +extern struct acpi_rsdump_info acpi_rs_dump_uart_serial_bus[]; +extern struct acpi_rsdump_info acpi_rs_dump_general_flags[]; +#endif + +#endif /* __ACRESRC_H__ */ diff --git a/kernel/drivers/acpi/acpica/acstruct.h b/kernel/drivers/acpi/acpica/acstruct.h new file mode 100644 index 000000000..87c7860b3 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acstruct.h @@ -0,0 +1,242 @@ +/****************************************************************************** + * + * Name: acstruct.h - Internal structs + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACSTRUCT_H__ +#define __ACSTRUCT_H__ + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +/***************************************************************************** + * + * Tree walking typedefs and structs + * + ****************************************************************************/ + +/* + * Walk state - current state of a parse tree walk. Used for both a leisurely + * stroll through the tree (for whatever reason), and for control method + * execution. + */ +#define ACPI_NEXT_OP_DOWNWARD 1 +#define ACPI_NEXT_OP_UPWARD 2 + +/* + * Groups of definitions for walk_type used for different implementations of + * walkers (never simultaneously) - flags for interpreter: + */ +#define ACPI_WALK_NON_METHOD 0 +#define ACPI_WALK_METHOD 0x01 +#define ACPI_WALK_METHOD_RESTART 0x02 + +struct acpi_walk_state { + struct acpi_walk_state *next; /* Next walk_state in list */ + u8 descriptor_type; /* To differentiate various internal objs */ + u8 walk_type; + u16 opcode; /* Current AML opcode */ + u8 next_op_info; /* Info about next_op */ + u8 num_operands; /* Stack pointer for Operands[] array */ + u8 operand_index; /* Index into operand stack, to be used by acpi_ds_obj_stack_push */ + acpi_owner_id owner_id; /* Owner of objects created during the walk */ + u8 last_predicate; /* Result of last predicate */ + u8 current_result; + u8 return_used; + u8 scope_depth; + u8 pass_number; /* Parse pass during table load */ + u8 result_size; /* Total elements for the result stack */ + u8 result_count; /* Current number of occupied elements of result stack */ + u32 aml_offset; + u32 arg_types; + u32 method_breakpoint; /* For single stepping */ + u32 user_breakpoint; /* User AML breakpoint */ + u32 parse_flags; + + struct acpi_parse_state parser_state; /* Current state of parser */ + u32 prev_arg_types; + u32 arg_count; /* push for fixed or var args */ + + struct acpi_namespace_node arguments[ACPI_METHOD_NUM_ARGS]; /* Control method arguments */ + struct acpi_namespace_node local_variables[ACPI_METHOD_NUM_LOCALS]; /* Control method locals */ + union acpi_operand_object *operands[ACPI_OBJ_NUM_OPERANDS + 1]; /* Operands passed to the interpreter (+1 for NULL terminator) */ + union acpi_operand_object **params; + + u8 *aml_last_while; + union acpi_operand_object **caller_return_desc; + union acpi_generic_state *control_state; /* List of control states (nested IFs) */ + struct acpi_namespace_node *deferred_node; /* Used when executing deferred opcodes */ + union acpi_operand_object *implicit_return_obj; + struct acpi_namespace_node *method_call_node; /* Called method Node */ + union acpi_parse_object *method_call_op; /* method_call Op if running a method */ + union acpi_operand_object *method_desc; /* Method descriptor if running a method */ + struct acpi_namespace_node *method_node; /* Method node if running a method. */ + union acpi_parse_object *op; /* Current parser op */ + const struct acpi_opcode_info *op_info; /* Info on current opcode */ + union acpi_parse_object *origin; /* Start of walk [Obsolete] */ + union acpi_operand_object *result_obj; + union acpi_generic_state *results; /* Stack of accumulated results */ + union acpi_operand_object *return_desc; /* Return object, if any */ + union acpi_generic_state *scope_info; /* Stack of nested scopes */ + union acpi_parse_object *prev_op; /* Last op that was processed */ + union acpi_parse_object *next_op; /* next op to be processed */ + struct acpi_thread_state *thread; + acpi_parse_downwards descending_callback; + acpi_parse_upwards ascending_callback; +}; + +/* Info used by acpi_ns_initialize_objects and acpi_ds_initialize_objects */ + +struct acpi_init_walk_info { + u32 table_index; + u32 object_count; + u32 method_count; + u32 serial_method_count; + u32 non_serial_method_count; + u32 serialized_method_count; + u32 device_count; + u32 op_region_count; + u32 field_count; + u32 buffer_count; + u32 package_count; + u32 op_region_init; + u32 field_init; + u32 buffer_init; + u32 package_init; + acpi_owner_id owner_id; +}; + +struct acpi_get_devices_info { + acpi_walk_callback user_function; + void *context; + const char *hid; +}; + +union acpi_aml_operands { + union acpi_operand_object *operands[7]; + + struct { + struct acpi_object_integer *type; + struct acpi_object_integer *code; + struct acpi_object_integer *argument; + + } fatal; + + struct { + union acpi_operand_object *source; + struct acpi_object_integer *index; + union acpi_operand_object *target; + + } index; + + struct { + union acpi_operand_object *source; + struct acpi_object_integer *index; + struct acpi_object_integer *length; + union acpi_operand_object *target; + + } mid; +}; + +/* + * Structure used to pass object evaluation information and parameters. + * Purpose is to reduce CPU stack use. + */ +struct acpi_evaluate_info { + /* The first 3 elements are passed by the caller to acpi_ns_evaluate */ + + struct acpi_namespace_node *prefix_node; /* Input: starting node */ + char *relative_pathname; /* Input: path relative to prefix_node */ + union acpi_operand_object **parameters; /* Input: argument list */ + + struct acpi_namespace_node *node; /* Resolved node (prefix_node:relative_pathname) */ + union acpi_operand_object *obj_desc; /* Object attached to the resolved node */ + char *full_pathname; /* Full pathname of the resolved node */ + + const union acpi_predefined_info *predefined; /* Used if Node is a predefined name */ + union acpi_operand_object *return_object; /* Object returned from the evaluation */ + union acpi_operand_object *parent_package; /* Used if return object is a Package */ + + u32 return_flags; /* Used for return value analysis */ + u32 return_btype; /* Bitmapped type of the returned object */ + u16 param_count; /* Count of the input argument list */ + u8 pass_number; /* Parser pass number */ + u8 return_object_type; /* Object type of the returned object */ + u8 node_flags; /* Same as Node->Flags */ + u8 flags; /* General flags */ +}; + +/* Values for Flags above */ + +#define ACPI_IGNORE_RETURN_VALUE 1 + +/* Defines for return_flags field above */ + +#define ACPI_OBJECT_REPAIRED 1 +#define ACPI_OBJECT_WRAPPED 2 + +/* Info used by acpi_ns_initialize_devices */ + +struct acpi_device_walk_info { + struct acpi_table_desc *table_desc; + struct acpi_evaluate_info *evaluate_info; + u32 device_count; + u32 num_STA; + u32 num_INI; +}; + +/* TBD: [Restructure] Merge with struct above */ + +struct acpi_walk_info { + u32 debug_level; + u32 count; + acpi_owner_id owner_id; + u8 display_type; +}; + +/* Display Types */ + +#define ACPI_DISPLAY_SUMMARY (u8) 0 +#define ACPI_DISPLAY_OBJECTS (u8) 1 +#define ACPI_DISPLAY_MASK (u8) 1 + +#define ACPI_DISPLAY_SHORT (u8) 2 + +#endif diff --git a/kernel/drivers/acpi/acpica/actables.h b/kernel/drivers/acpi/acpica/actables.h new file mode 100644 index 000000000..7e0b6f1be --- /dev/null +++ b/kernel/drivers/acpi/acpica/actables.h @@ -0,0 +1,167 @@ +/****************************************************************************** + * + * Name: actables.h - ACPI table management + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACTABLES_H__ +#define __ACTABLES_H__ + +acpi_status acpi_allocate_root_table(u32 initial_table_count); + +/* + * tbxfroot - Root pointer utilities + */ +u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp); + +acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp); + +u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length); + +/* + * tbdata - table data structure management + */ +acpi_status +acpi_tb_get_next_table_descriptor(u32 *table_index, + struct acpi_table_desc **table_desc); + +void +acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc, + acpi_physical_address address, + u8 flags, struct acpi_table_header *table); + +acpi_status +acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc, + acpi_physical_address address, u8 flags); + +void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc); + +acpi_status acpi_tb_validate_temp_table(struct acpi_table_desc *table_desc); + +acpi_status +acpi_tb_verify_temp_table(struct acpi_table_desc *table_desc, char *signature); + +u8 acpi_tb_is_table_loaded(u32 table_index); + +void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded); + +/* + * tbfadt - FADT parse/convert/validate + */ +void acpi_tb_parse_fadt(u32 table_index); + +void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length); + +/* + * tbfind - find ACPI table + */ +acpi_status +acpi_tb_find_table(char *signature, + char *oem_id, char *oem_table_id, u32 *table_index); + +/* + * tbinstal - Table removal and deletion + */ +acpi_status acpi_tb_resize_root_table_list(void); + +acpi_status acpi_tb_validate_table(struct acpi_table_desc *table_desc); + +void acpi_tb_invalidate_table(struct acpi_table_desc *table_desc); + +void acpi_tb_override_table(struct acpi_table_desc *old_table_desc); + +acpi_status +acpi_tb_acquire_table(struct acpi_table_desc *table_desc, + struct acpi_table_header **table_ptr, + u32 *table_length, u8 *table_flags); + +void +acpi_tb_release_table(struct acpi_table_header *table, + u32 table_length, u8 table_flags); + +acpi_status +acpi_tb_install_standard_table(acpi_physical_address address, + u8 flags, + u8 reload, u8 override, u32 *table_index); + +void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc); + +void acpi_tb_terminate(void); + +acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index); + +acpi_status acpi_tb_allocate_owner_id(u32 table_index); + +acpi_status acpi_tb_release_owner_id(u32 table_index); + +acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id *owner_id); + +/* + * tbutils - table manager utilities + */ +acpi_status acpi_tb_initialize_facs(void); + +u8 acpi_tb_tables_loaded(void); + +void +acpi_tb_print_table_header(acpi_physical_address address, + struct acpi_table_header *header); + +u8 acpi_tb_checksum(u8 *buffer, u32 length); + +acpi_status +acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length); + +void acpi_tb_check_dsdt_header(void); + +struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index); + +void +acpi_tb_install_table_with_override(u32 table_index, + struct acpi_table_desc *new_table_desc, + u8 override); + +acpi_status +acpi_tb_install_fixed_table(acpi_physical_address address, + char *signature, u32 table_index); + +acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address); + +#endif /* __ACTABLES_H__ */ diff --git a/kernel/drivers/acpi/acpica/acutils.h b/kernel/drivers/acpi/acpica/acutils.h new file mode 100644 index 000000000..2b3c5bd22 --- /dev/null +++ b/kernel/drivers/acpi/acpica/acutils.h @@ -0,0 +1,792 @@ +/****************************************************************************** + * + * Name: acutils.h -- prototypes for the common (subsystem-wide) procedures + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _ACUTILS_H +#define _ACUTILS_H + +extern const u8 acpi_gbl_resource_aml_sizes[]; +extern const u8 acpi_gbl_resource_aml_serial_bus_sizes[]; + +/* Strings used by the disassembler and debugger resource dump routines */ + +#if defined(ACPI_DEBUG_OUTPUT) || defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) + +extern const char *acpi_gbl_bm_decode[]; +extern const char *acpi_gbl_config_decode[]; +extern const char *acpi_gbl_consume_decode[]; +extern const char *acpi_gbl_dec_decode[]; +extern const char *acpi_gbl_he_decode[]; +extern const char *acpi_gbl_io_decode[]; +extern const char *acpi_gbl_ll_decode[]; +extern const char *acpi_gbl_max_decode[]; +extern const char *acpi_gbl_mem_decode[]; +extern const char *acpi_gbl_min_decode[]; +extern const char *acpi_gbl_mtp_decode[]; +extern const char *acpi_gbl_rng_decode[]; +extern const char *acpi_gbl_rw_decode[]; +extern const char *acpi_gbl_shr_decode[]; +extern const char *acpi_gbl_siz_decode[]; +extern const char *acpi_gbl_trs_decode[]; +extern const char *acpi_gbl_ttp_decode[]; +extern const char *acpi_gbl_typ_decode[]; +extern const char *acpi_gbl_ppc_decode[]; +extern const char *acpi_gbl_ior_decode[]; +extern const char *acpi_gbl_dts_decode[]; +extern const char *acpi_gbl_ct_decode[]; +extern const char *acpi_gbl_sbt_decode[]; +extern const char *acpi_gbl_am_decode[]; +extern const char *acpi_gbl_sm_decode[]; +extern const char *acpi_gbl_wm_decode[]; +extern const char *acpi_gbl_cph_decode[]; +extern const char *acpi_gbl_cpo_decode[]; +extern const char *acpi_gbl_dp_decode[]; +extern const char *acpi_gbl_ed_decode[]; +extern const char *acpi_gbl_bpb_decode[]; +extern const char *acpi_gbl_sb_decode[]; +extern const char *acpi_gbl_fc_decode[]; +extern const char *acpi_gbl_pt_decode[]; +#endif + +/* + * For the iASL compiler case, the output is redirected to stderr so that + * any of the various ACPI errors and warnings do not appear in the output + * files, for either the compiler or disassembler portions of the tool. + */ +#ifdef ACPI_ASL_COMPILER + +#include + +#define ACPI_MSG_REDIRECT_BEGIN \ + FILE *output_file = acpi_gbl_output_file; \ + acpi_os_redirect_output (stderr); + +#define ACPI_MSG_REDIRECT_END \ + acpi_os_redirect_output (output_file); + +#else +/* + * non-iASL case - no redirection, nothing to do + */ +#define ACPI_MSG_REDIRECT_BEGIN +#define ACPI_MSG_REDIRECT_END +#endif + +/* + * Common error message prefixes + */ +#define ACPI_MSG_ERROR "ACPI Error: " +#define ACPI_MSG_EXCEPTION "ACPI Exception: " +#define ACPI_MSG_WARNING "ACPI Warning: " +#define ACPI_MSG_INFO "ACPI: " + +#define ACPI_MSG_BIOS_ERROR "ACPI BIOS Error (bug): " +#define ACPI_MSG_BIOS_WARNING "ACPI BIOS Warning (bug): " + +/* + * Common message suffix + */ +#define ACPI_MSG_SUFFIX \ + acpi_os_printf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number) + +/* Types for Resource descriptor entries */ + +#define ACPI_INVALID_RESOURCE 0 +#define ACPI_FIXED_LENGTH 1 +#define ACPI_VARIABLE_LENGTH 2 +#define ACPI_SMALL_VARIABLE_LENGTH 3 + +typedef +acpi_status(*acpi_walk_aml_callback) (u8 *aml, + u32 length, + u32 offset, + u8 resource_index, void **context); + +typedef +acpi_status(*acpi_pkg_callback) (u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state * state, + void *context); + +struct acpi_pkg_info { + u8 *free_space; + acpi_size length; + u32 object_space; + u32 num_packages; +}; + +/* Object reference counts */ + +#define REF_INCREMENT (u16) 0 +#define REF_DECREMENT (u16) 1 + +/* acpi_ut_dump_buffer */ + +#define DB_BYTE_DISPLAY 1 +#define DB_WORD_DISPLAY 2 +#define DB_DWORD_DISPLAY 4 +#define DB_QWORD_DISPLAY 8 + +/* + * utglobal - Global data structures and procedures + */ +acpi_status acpi_ut_init_globals(void); + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +char *acpi_ut_get_mutex_name(u32 mutex_id); + +const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type); +#endif + +char *acpi_ut_get_type_name(acpi_object_type type); + +char *acpi_ut_get_node_name(void *object); + +char *acpi_ut_get_descriptor_name(void *object); + +const char *acpi_ut_get_reference_name(union acpi_operand_object *object); + +char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc); + +char *acpi_ut_get_region_name(u8 space_id); + +char *acpi_ut_get_event_name(u32 event_id); + +char acpi_ut_hex_to_ascii_char(u64 integer, u32 position); + +u8 acpi_ut_ascii_char_to_hex(int hex_char); + +u8 acpi_ut_valid_object_type(acpi_object_type type); + +/* + * utinit - miscellaneous initialization and shutdown + */ +acpi_status acpi_ut_hardware_initialize(void); + +void acpi_ut_subsystem_shutdown(void); + +/* + * utclib - Local implementations of C library functions + */ +#ifndef ACPI_USE_SYSTEM_CLIBRARY + +acpi_size acpi_ut_strlen(const char *string); + +char *acpi_ut_strchr(const char *string, int ch); + +char *acpi_ut_strcpy(char *dst_string, const char *src_string); + +char *acpi_ut_strncpy(char *dst_string, + const char *src_string, acpi_size count); + +int acpi_ut_memcmp(const char *buffer1, const char *buffer2, acpi_size count); + +int acpi_ut_strncmp(const char *string1, const char *string2, acpi_size count); + +int acpi_ut_strcmp(const char *string1, const char *string2); + +char *acpi_ut_strcat(char *dst_string, const char *src_string); + +char *acpi_ut_strncat(char *dst_string, + const char *src_string, acpi_size count); + +u32 acpi_ut_strtoul(const char *string, char **terminator, u32 base); + +char *acpi_ut_strstr(char *string1, char *string2); + +void *acpi_ut_memcpy(void *dest, const void *src, acpi_size count); + +void *acpi_ut_memset(void *dest, u8 value, acpi_size count); + +int acpi_ut_to_upper(int c); + +int acpi_ut_to_lower(int c); + +extern const u8 _acpi_ctype[]; + +#define _ACPI_XA 0x00 /* extra alphabetic - not supported */ +#define _ACPI_XS 0x40 /* extra space */ +#define _ACPI_BB 0x00 /* BEL, BS, etc. - not supported */ +#define _ACPI_CN 0x20 /* CR, FF, HT, NL, VT */ +#define _ACPI_DI 0x04 /* '0'-'9' */ +#define _ACPI_LO 0x02 /* 'a'-'z' */ +#define _ACPI_PU 0x10 /* punctuation */ +#define _ACPI_SP 0x08 /* space */ +#define _ACPI_UP 0x01 /* 'A'-'Z' */ +#define _ACPI_XD 0x80 /* '0'-'9', 'A'-'F', 'a'-'f' */ + +#define ACPI_IS_DIGIT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_DI)) +#define ACPI_IS_SPACE(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_SP)) +#define ACPI_IS_XDIGIT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_XD)) +#define ACPI_IS_UPPER(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_UP)) +#define ACPI_IS_LOWER(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO)) +#define ACPI_IS_PRINT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP | _ACPI_DI | _ACPI_XS | _ACPI_PU)) +#define ACPI_IS_ALPHA(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP)) + +#endif /* !ACPI_USE_SYSTEM_CLIBRARY */ + +#define ACPI_IS_ASCII(c) ((c) < 0x80) + +/* + * utcopy - Object construction and conversion interfaces + */ +acpi_status +acpi_ut_build_simple_object(union acpi_operand_object *obj, + union acpi_object *user_obj, + u8 *data_space, u32 *buffer_space_used); + +acpi_status +acpi_ut_build_package_object(union acpi_operand_object *obj, + u8 *buffer, u32 *space_used); + +acpi_status +acpi_ut_copy_iobject_to_eobject(union acpi_operand_object *obj, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_ut_copy_eobject_to_iobject(union acpi_object *obj, + union acpi_operand_object **internal_obj); + +acpi_status +acpi_ut_copy_isimple_to_isimple(union acpi_operand_object *source_obj, + union acpi_operand_object *dest_obj); + +acpi_status +acpi_ut_copy_iobject_to_iobject(union acpi_operand_object *source_desc, + union acpi_operand_object **dest_desc, + struct acpi_walk_state *walk_state); + +/* + * utcreate - Object creation + */ +acpi_status +acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action); + +/* + * utdebug - Debug interfaces + */ +void acpi_ut_init_stack_ptr_trace(void); + +void acpi_ut_track_stack_ptr(void); + +void +acpi_ut_trace(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id); + +void +acpi_ut_trace_ptr(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, void *pointer); + +void +acpi_ut_trace_u32(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u32 integer); + +void +acpi_ut_trace_str(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, char *string); + +void +acpi_ut_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id); + +void +acpi_ut_status_exit(u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, acpi_status status); + +void +acpi_ut_value_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u64 value); + +void +acpi_ut_ptr_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u8 *ptr); + +void +acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id); + +void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 offset); + +#ifdef ACPI_APPLICATION +void +acpi_ut_dump_buffer_to_file(ACPI_FILE file, + u8 *buffer, + u32 count, u32 display, u32 base_offset); +#endif + +void acpi_ut_report_error(char *module_name, u32 line_number); + +void acpi_ut_report_info(char *module_name, u32 line_number); + +void acpi_ut_report_warning(char *module_name, u32 line_number); + +/* + * utdelete - Object deletion and reference counts + */ +void acpi_ut_add_reference(union acpi_operand_object *object); + +void acpi_ut_remove_reference(union acpi_operand_object *object); + +void acpi_ut_delete_internal_package_object(union acpi_operand_object *object); + +void acpi_ut_delete_internal_simple_object(union acpi_operand_object *object); + +void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list); + +/* + * uteval - object evaluation + */ +acpi_status +acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, + char *path, + u32 expected_return_btypes, + union acpi_operand_object **return_desc); + +acpi_status +acpi_ut_evaluate_numeric_object(char *object_name, + struct acpi_namespace_node *device_node, + u64 *value); + +acpi_status +acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 *status_flags); + +acpi_status +acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node, + const char **method_names, + u8 method_count, u8 *out_values); + +/* + * utfileio - file operations + */ +#ifdef ACPI_APPLICATION +acpi_status +acpi_ut_read_table_from_file(char *filename, struct acpi_table_header **table); +#endif + +/* + * utids - device ID support + */ +acpi_status +acpi_ut_execute_HID(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id ** return_id); + +acpi_status +acpi_ut_execute_UID(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id ** return_id); + +acpi_status +acpi_ut_execute_SUB(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id **return_id); + +acpi_status +acpi_ut_execute_CID(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id_list ** return_cid_list); + +/* + * utlock - reader/writer locks + */ +acpi_status acpi_ut_create_rw_lock(struct acpi_rw_lock *lock); + +void acpi_ut_delete_rw_lock(struct acpi_rw_lock *lock); + +acpi_status acpi_ut_acquire_read_lock(struct acpi_rw_lock *lock); + +acpi_status acpi_ut_release_read_lock(struct acpi_rw_lock *lock); + +acpi_status acpi_ut_acquire_write_lock(struct acpi_rw_lock *lock); + +void acpi_ut_release_write_lock(struct acpi_rw_lock *lock); + +/* + * utobject - internal object create/delete/cache routines + */ +union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char + *module_name, + u32 line_number, + u32 component_id, + acpi_object_type + type); + +void *acpi_ut_allocate_object_desc_dbg(const char *module_name, + u32 line_number, u32 component_id); + +#define acpi_ut_create_internal_object(t) acpi_ut_create_internal_object_dbg (_acpi_module_name,__LINE__,_COMPONENT,t) +#define acpi_ut_allocate_object_desc() acpi_ut_allocate_object_desc_dbg (_acpi_module_name,__LINE__,_COMPONENT) + +void acpi_ut_delete_object_desc(union acpi_operand_object *object); + +u8 acpi_ut_valid_internal_object(void *object); + +union acpi_operand_object *acpi_ut_create_package_object(u32 count); + +union acpi_operand_object *acpi_ut_create_integer_object(u64 value); + +union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size); + +union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size); + +acpi_status +acpi_ut_get_object_size(union acpi_operand_object *obj, acpi_size * obj_length); + +/* + * utosi - Support for the _OSI predefined control method + */ +acpi_status acpi_ut_initialize_interfaces(void); + +acpi_status acpi_ut_interface_terminate(void); + +acpi_status acpi_ut_install_interface(acpi_string interface_name); + +acpi_status acpi_ut_remove_interface(acpi_string interface_name); + +acpi_status acpi_ut_update_interfaces(u8 action); + +struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name); + +acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state); + +/* + * utpredef - support for predefined names + */ +const union acpi_predefined_info *acpi_ut_get_next_predefined_method(const union + acpi_predefined_info + *this_name); + +const union acpi_predefined_info *acpi_ut_match_predefined_method(char *name); + +void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes); + +#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP) +const union acpi_predefined_info *acpi_ut_match_resource_name(char *name); + +void +acpi_ut_display_predefined_method(char *buffer, + const union acpi_predefined_info *this_name, + u8 multi_line); + +u32 acpi_ut_get_resource_bit_width(char *buffer, u16 types); +#endif + +/* + * utstate - Generic state creation/cache routines + */ +void +acpi_ut_push_generic_state(union acpi_generic_state **list_head, + union acpi_generic_state *state); + +union acpi_generic_state *acpi_ut_pop_generic_state(union acpi_generic_state + **list_head); + +union acpi_generic_state *acpi_ut_create_generic_state(void); + +struct acpi_thread_state *acpi_ut_create_thread_state(void); + +union acpi_generic_state *acpi_ut_create_update_state(union acpi_operand_object + *object, u16 action); + +union acpi_generic_state *acpi_ut_create_pkg_state(void *internal_object, + void *external_object, + u16 index); + +acpi_status +acpi_ut_create_update_state_and_push(union acpi_operand_object *object, + u16 action, + union acpi_generic_state **state_list); + +union acpi_generic_state *acpi_ut_create_control_state(void); + +void acpi_ut_delete_generic_state(union acpi_generic_state *state); + +/* + * utmath + */ +acpi_status +acpi_ut_divide(u64 in_dividend, + u64 in_divisor, u64 *out_quotient, u64 *out_remainder); + +acpi_status +acpi_ut_short_divide(u64 in_dividend, + u32 divisor, u64 *out_quotient, u32 *out_remainder); + +/* + * utmisc + */ +const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status + status); + +u8 acpi_ut_is_pci_root_bridge(char *id); + +#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP) +u8 acpi_ut_is_aml_table(struct acpi_table_header *table); +#endif + +acpi_status +acpi_ut_walk_package_tree(union acpi_operand_object *source_object, + void *target_object, + acpi_pkg_callback walk_callback, void *context); + +/* Values for Base above (16=Hex, 10=Decimal) */ + +#define ACPI_ANY_BASE 0 + +u32 acpi_ut_dword_byte_swap(u32 value); + +void acpi_ut_set_integer_width(u8 revision); + +#ifdef ACPI_DEBUG_OUTPUT +void +acpi_ut_display_init_pathname(u8 type, + struct acpi_namespace_node *obj_handle, + char *path); +#endif + +/* + * utownerid - Support for Table/Method Owner IDs + */ +acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id); + +void acpi_ut_release_owner_id(acpi_owner_id * owner_id); + +/* + * utresrc + */ +acpi_status +acpi_ut_walk_aml_resources(struct acpi_walk_state *walk_state, + u8 *aml, + acpi_size aml_length, + acpi_walk_aml_callback user_function, + void **context); + +acpi_status +acpi_ut_validate_resource(struct acpi_walk_state *walk_state, + void *aml, u8 *return_index); + +u32 acpi_ut_get_descriptor_length(void *aml); + +u16 acpi_ut_get_resource_length(void *aml); + +u8 acpi_ut_get_resource_header_length(void *aml); + +u8 acpi_ut_get_resource_type(void *aml); + +acpi_status +acpi_ut_get_resource_end_tag(union acpi_operand_object *obj_desc, u8 **end_tag); + +/* + * utstring - String and character utilities + */ +void acpi_ut_strupr(char *src_string); + +#ifdef ACPI_ASL_COMPILER +void acpi_ut_strlwr(char *src_string); + +int acpi_ut_stricmp(char *string1, char *string2); +#endif + +acpi_status acpi_ut_strtoul64(char *string, u32 base, u64 *ret_integer); + +void acpi_ut_print_string(char *string, u16 max_length); + +#if defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP +void ut_convert_backslashes(char *pathname); +#endif + +u8 acpi_ut_valid_acpi_name(char *name); + +u8 acpi_ut_valid_acpi_char(char character, u32 position); + +void acpi_ut_repair_name(char *name); + +#if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION) +u8 acpi_ut_safe_strcpy(char *dest, acpi_size dest_size, char *source); + +u8 acpi_ut_safe_strcat(char *dest, acpi_size dest_size, char *source); + +u8 +acpi_ut_safe_strncat(char *dest, + acpi_size dest_size, + char *source, acpi_size max_transfer_length); +#endif + +/* + * utmutex - mutex support + */ +acpi_status acpi_ut_mutex_initialize(void); + +void acpi_ut_mutex_terminate(void); + +acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id); + +acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id); + +/* + * utalloc - memory allocation and object caching + */ +acpi_status acpi_ut_create_caches(void); + +acpi_status acpi_ut_delete_caches(void); + +acpi_status acpi_ut_validate_buffer(struct acpi_buffer *buffer); + +acpi_status +acpi_ut_initialize_buffer(struct acpi_buffer *buffer, + acpi_size required_length); + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS +void *acpi_ut_allocate_and_track(acpi_size size, + u32 component, const char *module, u32 line); + +void *acpi_ut_allocate_zeroed_and_track(acpi_size size, + u32 component, + const char *module, u32 line); + +void +acpi_ut_free_and_track(void *address, + u32 component, const char *module, u32 line); + +#ifdef ACPI_FUTURE_USAGE +void acpi_ut_dump_allocation_info(void); +#endif /* ACPI_FUTURE_USAGE */ + +void acpi_ut_dump_allocations(u32 component, const char *module); + +acpi_status +acpi_ut_create_list(char *list_name, + u16 object_size, struct acpi_memory_list **return_cache); + +#endif /* ACPI_DBG_TRACK_ALLOCATIONS */ + +/* + * utaddress - address range check + */ +acpi_status +acpi_ut_add_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, + u32 length, struct acpi_namespace_node *region_node); + +void +acpi_ut_remove_address_range(acpi_adr_space_type space_id, + struct acpi_namespace_node *region_node); + +u32 +acpi_ut_check_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, u32 length, u8 warn); + +void acpi_ut_delete_address_lists(void); + +/* + * utxferror - various error/warning output functions + */ +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_warning(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...); + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_info(const char *module_name, + u32 line_number, + char *pathname, u8 node_flags, const char *format, ...); + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_bios_error(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...); + +void +acpi_ut_namespace_error(const char *module_name, + u32 line_number, + const char *internal_name, acpi_status lookup_status); + +void +acpi_ut_method_error(const char *module_name, + u32 line_number, + const char *message, + struct acpi_namespace_node *node, + const char *path, acpi_status lookup_status); + +/* + * Utility functions for ACPI names and IDs + */ +const struct ah_predefined_name *acpi_ah_match_predefined_name(char *nameseg); + +const struct ah_device_id *acpi_ah_match_hardware_id(char *hid); + +const char *acpi_ah_match_uuid(u8 *data); + +/* + * utprint - printf/vprintf output functions + */ +const char *acpi_ut_scan_number(const char *string, u64 *number_ptr); + +const char *acpi_ut_print_number(char *string, u64 number); + +int +acpi_ut_vsnprintf(char *string, + acpi_size size, const char *format, va_list args); + +int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...); + +#ifdef ACPI_APPLICATION +int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args); + +int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...); +#endif + +/* + * utuuid -- UUID support functions + */ +#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP) +void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer); +#endif + +#endif /* _ACUTILS_H */ diff --git a/kernel/drivers/acpi/acpica/amlcode.h b/kernel/drivers/acpi/acpica/amlcode.h new file mode 100644 index 000000000..be9fd009c --- /dev/null +++ b/kernel/drivers/acpi/acpica/amlcode.h @@ -0,0 +1,487 @@ +/****************************************************************************** + * + * Name: amlcode.h - Definitions for AML, as included in "definition blocks" + * Declarations and definitions contained herein are derived + * directly from the ACPI specification. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __AMLCODE_H__ +#define __AMLCODE_H__ + +/* primary opcodes */ + +#define AML_NULL_CHAR (u16) 0x00 + +#define AML_ZERO_OP (u16) 0x00 +#define AML_ONE_OP (u16) 0x01 +#define AML_UNASSIGNED (u16) 0x02 +#define AML_ALIAS_OP (u16) 0x06 +#define AML_NAME_OP (u16) 0x08 +#define AML_BYTE_OP (u16) 0x0a +#define AML_WORD_OP (u16) 0x0b +#define AML_DWORD_OP (u16) 0x0c +#define AML_STRING_OP (u16) 0x0d +#define AML_QWORD_OP (u16) 0x0e /* ACPI 2.0 */ +#define AML_SCOPE_OP (u16) 0x10 +#define AML_BUFFER_OP (u16) 0x11 +#define AML_PACKAGE_OP (u16) 0x12 +#define AML_VAR_PACKAGE_OP (u16) 0x13 /* ACPI 2.0 */ +#define AML_METHOD_OP (u16) 0x14 +#define AML_EXTERNAL_OP (u16) 0x15 /* ACPI 6.0 */ +#define AML_DUAL_NAME_PREFIX (u16) 0x2e +#define AML_MULTI_NAME_PREFIX_OP (u16) 0x2f +#define AML_NAME_CHAR_SUBSEQ (u16) 0x30 +#define AML_NAME_CHAR_FIRST (u16) 0x41 +#define AML_EXTENDED_OP_PREFIX (u16) 0x5b +#define AML_ROOT_PREFIX (u16) 0x5c +#define AML_PARENT_PREFIX (u16) 0x5e +#define AML_LOCAL_OP (u16) 0x60 +#define AML_LOCAL0 (u16) 0x60 +#define AML_LOCAL1 (u16) 0x61 +#define AML_LOCAL2 (u16) 0x62 +#define AML_LOCAL3 (u16) 0x63 +#define AML_LOCAL4 (u16) 0x64 +#define AML_LOCAL5 (u16) 0x65 +#define AML_LOCAL6 (u16) 0x66 +#define AML_LOCAL7 (u16) 0x67 +#define AML_ARG_OP (u16) 0x68 +#define AML_ARG0 (u16) 0x68 +#define AML_ARG1 (u16) 0x69 +#define AML_ARG2 (u16) 0x6a +#define AML_ARG3 (u16) 0x6b +#define AML_ARG4 (u16) 0x6c +#define AML_ARG5 (u16) 0x6d +#define AML_ARG6 (u16) 0x6e +#define AML_STORE_OP (u16) 0x70 +#define AML_REF_OF_OP (u16) 0x71 +#define AML_ADD_OP (u16) 0x72 +#define AML_CONCAT_OP (u16) 0x73 +#define AML_SUBTRACT_OP (u16) 0x74 +#define AML_INCREMENT_OP (u16) 0x75 +#define AML_DECREMENT_OP (u16) 0x76 +#define AML_MULTIPLY_OP (u16) 0x77 +#define AML_DIVIDE_OP (u16) 0x78 +#define AML_SHIFT_LEFT_OP (u16) 0x79 +#define AML_SHIFT_RIGHT_OP (u16) 0x7a +#define AML_BIT_AND_OP (u16) 0x7b +#define AML_BIT_NAND_OP (u16) 0x7c +#define AML_BIT_OR_OP (u16) 0x7d +#define AML_BIT_NOR_OP (u16) 0x7e +#define AML_BIT_XOR_OP (u16) 0x7f +#define AML_BIT_NOT_OP (u16) 0x80 +#define AML_FIND_SET_LEFT_BIT_OP (u16) 0x81 +#define AML_FIND_SET_RIGHT_BIT_OP (u16) 0x82 +#define AML_DEREF_OF_OP (u16) 0x83 +#define AML_CONCAT_RES_OP (u16) 0x84 /* ACPI 2.0 */ +#define AML_MOD_OP (u16) 0x85 /* ACPI 2.0 */ +#define AML_NOTIFY_OP (u16) 0x86 +#define AML_SIZE_OF_OP (u16) 0x87 +#define AML_INDEX_OP (u16) 0x88 +#define AML_MATCH_OP (u16) 0x89 +#define AML_CREATE_DWORD_FIELD_OP (u16) 0x8a +#define AML_CREATE_WORD_FIELD_OP (u16) 0x8b +#define AML_CREATE_BYTE_FIELD_OP (u16) 0x8c +#define AML_CREATE_BIT_FIELD_OP (u16) 0x8d +#define AML_TYPE_OP (u16) 0x8e +#define AML_CREATE_QWORD_FIELD_OP (u16) 0x8f /* ACPI 2.0 */ +#define AML_LAND_OP (u16) 0x90 +#define AML_LOR_OP (u16) 0x91 +#define AML_LNOT_OP (u16) 0x92 +#define AML_LEQUAL_OP (u16) 0x93 +#define AML_LGREATER_OP (u16) 0x94 +#define AML_LLESS_OP (u16) 0x95 +#define AML_TO_BUFFER_OP (u16) 0x96 /* ACPI 2.0 */ +#define AML_TO_DECSTRING_OP (u16) 0x97 /* ACPI 2.0 */ +#define AML_TO_HEXSTRING_OP (u16) 0x98 /* ACPI 2.0 */ +#define AML_TO_INTEGER_OP (u16) 0x99 /* ACPI 2.0 */ +#define AML_TO_STRING_OP (u16) 0x9c /* ACPI 2.0 */ +#define AML_COPY_OP (u16) 0x9d /* ACPI 2.0 */ +#define AML_MID_OP (u16) 0x9e /* ACPI 2.0 */ +#define AML_CONTINUE_OP (u16) 0x9f /* ACPI 2.0 */ +#define AML_IF_OP (u16) 0xa0 +#define AML_ELSE_OP (u16) 0xa1 +#define AML_WHILE_OP (u16) 0xa2 +#define AML_NOOP_OP (u16) 0xa3 +#define AML_RETURN_OP (u16) 0xa4 +#define AML_BREAK_OP (u16) 0xa5 +#define AML_BREAK_POINT_OP (u16) 0xcc +#define AML_ONES_OP (u16) 0xff + +/* prefixed opcodes */ + +#define AML_EXTENDED_OPCODE (u16) 0x5b00 /* prefix for 2-byte opcodes */ + +#define AML_MUTEX_OP (u16) 0x5b01 +#define AML_EVENT_OP (u16) 0x5b02 +#define AML_SHIFT_RIGHT_BIT_OP (u16) 0x5b10 +#define AML_SHIFT_LEFT_BIT_OP (u16) 0x5b11 +#define AML_COND_REF_OF_OP (u16) 0x5b12 +#define AML_CREATE_FIELD_OP (u16) 0x5b13 +#define AML_LOAD_TABLE_OP (u16) 0x5b1f /* ACPI 2.0 */ +#define AML_LOAD_OP (u16) 0x5b20 +#define AML_STALL_OP (u16) 0x5b21 +#define AML_SLEEP_OP (u16) 0x5b22 +#define AML_ACQUIRE_OP (u16) 0x5b23 +#define AML_SIGNAL_OP (u16) 0x5b24 +#define AML_WAIT_OP (u16) 0x5b25 +#define AML_RESET_OP (u16) 0x5b26 +#define AML_RELEASE_OP (u16) 0x5b27 +#define AML_FROM_BCD_OP (u16) 0x5b28 +#define AML_TO_BCD_OP (u16) 0x5b29 +#define AML_UNLOAD_OP (u16) 0x5b2a +#define AML_REVISION_OP (u16) 0x5b30 +#define AML_DEBUG_OP (u16) 0x5b31 +#define AML_FATAL_OP (u16) 0x5b32 +#define AML_TIMER_OP (u16) 0x5b33 /* ACPI 3.0 */ +#define AML_REGION_OP (u16) 0x5b80 +#define AML_FIELD_OP (u16) 0x5b81 +#define AML_DEVICE_OP (u16) 0x5b82 +#define AML_PROCESSOR_OP (u16) 0x5b83 +#define AML_POWER_RES_OP (u16) 0x5b84 +#define AML_THERMAL_ZONE_OP (u16) 0x5b85 +#define AML_INDEX_FIELD_OP (u16) 0x5b86 +#define AML_BANK_FIELD_OP (u16) 0x5b87 +#define AML_DATA_REGION_OP (u16) 0x5b88 /* ACPI 2.0 */ + +/* + * Combination opcodes (actually two one-byte opcodes) + * Used by the disassembler and iASL compiler + */ +#define AML_LGREATEREQUAL_OP (u16) 0x9295 +#define AML_LLESSEQUAL_OP (u16) 0x9294 +#define AML_LNOTEQUAL_OP (u16) 0x9293 + +/* + * Opcodes for "Field" operators + */ +#define AML_FIELD_OFFSET_OP (u8) 0x00 +#define AML_FIELD_ACCESS_OP (u8) 0x01 +#define AML_FIELD_CONNECTION_OP (u8) 0x02 /* ACPI 5.0 */ +#define AML_FIELD_EXT_ACCESS_OP (u8) 0x03 /* ACPI 5.0 */ + +/* + * Internal opcodes + * Use only "Unknown" AML opcodes, don't attempt to use + * any valid ACPI ASCII values (A-Z, 0-9, '-') + */ +#define AML_INT_NAMEPATH_OP (u16) 0x002d +#define AML_INT_NAMEDFIELD_OP (u16) 0x0030 +#define AML_INT_RESERVEDFIELD_OP (u16) 0x0031 +#define AML_INT_ACCESSFIELD_OP (u16) 0x0032 +#define AML_INT_BYTELIST_OP (u16) 0x0033 +#define AML_INT_METHODCALL_OP (u16) 0x0035 +#define AML_INT_RETURN_VALUE_OP (u16) 0x0036 +#define AML_INT_EVAL_SUBTREE_OP (u16) 0x0037 +#define AML_INT_CONNECTION_OP (u16) 0x0038 +#define AML_INT_EXTACCESSFIELD_OP (u16) 0x0039 + +#define ARG_NONE 0x0 + +/* + * Argument types for the AML Parser + * Each field in the arg_types u32 is 5 bits, allowing for a maximum of 6 arguments. + * There can be up to 31 unique argument types + * Zero is reserved as end-of-list indicator + */ +#define ARGP_BYTEDATA 0x01 +#define ARGP_BYTELIST 0x02 +#define ARGP_CHARLIST 0x03 +#define ARGP_DATAOBJ 0x04 +#define ARGP_DATAOBJLIST 0x05 +#define ARGP_DWORDDATA 0x06 +#define ARGP_FIELDLIST 0x07 +#define ARGP_NAME 0x08 +#define ARGP_NAMESTRING 0x09 +#define ARGP_OBJLIST 0x0A +#define ARGP_PKGLENGTH 0x0B +#define ARGP_SUPERNAME 0x0C +#define ARGP_TARGET 0x0D +#define ARGP_TERMARG 0x0E +#define ARGP_TERMLIST 0x0F +#define ARGP_WORDDATA 0x10 +#define ARGP_QWORDDATA 0x11 +#define ARGP_SIMPLENAME 0x12 + +/* + * Resolved argument types for the AML Interpreter + * Each field in the arg_types u32 is 5 bits, allowing for a maximum of 6 arguments. + * There can be up to 31 unique argument types (0 is end-of-arg-list indicator) + * + * Note1: These values are completely independent from the ACPI_TYPEs + * i.e., ARGI_INTEGER != ACPI_TYPE_INTEGER + * + * Note2: If and when 5 bits becomes insufficient, it would probably be best + * to convert to a 6-byte array of argument types, allowing 8 bits per argument. + */ + +/* Single, simple types */ + +#define ARGI_ANYTYPE 0x01 /* Don't care */ +#define ARGI_PACKAGE 0x02 +#define ARGI_EVENT 0x03 +#define ARGI_MUTEX 0x04 +#define ARGI_DDBHANDLE 0x05 + +/* Interchangeable types (via implicit conversion) */ + +#define ARGI_INTEGER 0x06 +#define ARGI_STRING 0x07 +#define ARGI_BUFFER 0x08 +#define ARGI_BUFFER_OR_STRING 0x09 /* Used by MID op only */ +#define ARGI_COMPUTEDATA 0x0A /* Buffer, String, or Integer */ + +/* Reference objects */ + +#define ARGI_INTEGER_REF 0x0B +#define ARGI_OBJECT_REF 0x0C +#define ARGI_DEVICE_REF 0x0D +#define ARGI_REFERENCE 0x0E +#define ARGI_TARGETREF 0x0F /* Target, subject to implicit conversion */ +#define ARGI_FIXED_TARGET 0x10 /* Target, no implicit conversion */ +#define ARGI_SIMPLE_TARGET 0x11 /* Name, Local, Arg -- no implicit conversion */ + +/* Multiple/complex types */ + +#define ARGI_DATAOBJECT 0x12 /* Buffer, String, package or reference to a node - Used only by size_of operator */ +#define ARGI_COMPLEXOBJ 0x13 /* Buffer, String, or package (Used by INDEX op only) */ +#define ARGI_REF_OR_STRING 0x14 /* Reference or String (Used by DEREFOF op only) */ +#define ARGI_REGION_OR_BUFFER 0x15 /* Used by LOAD op only */ +#define ARGI_DATAREFOBJ 0x16 + +/* Note: types above can expand to 0x1F maximum */ + +#define ARGI_INVALID_OPCODE 0xFFFFFFFF + +/* + * hash offsets + */ +#define AML_EXTOP_HASH_OFFSET 22 +#define AML_LNOT_HASH_OFFSET 19 + +/* + * opcode groups and types + */ +#define OPGRP_NAMED 0x01 +#define OPGRP_FIELD 0x02 +#define OPGRP_BYTELIST 0x04 + +/* + * Opcode information + */ + +/* Opcode flags */ + +#define AML_LOGICAL 0x0001 +#define AML_LOGICAL_NUMERIC 0x0002 +#define AML_MATH 0x0004 +#define AML_CREATE 0x0008 +#define AML_FIELD 0x0010 +#define AML_DEFER 0x0020 +#define AML_NAMED 0x0040 +#define AML_NSNODE 0x0080 +#define AML_NSOPCODE 0x0100 +#define AML_NSOBJECT 0x0200 +#define AML_HAS_RETVAL 0x0400 +#define AML_HAS_TARGET 0x0800 +#define AML_HAS_ARGS 0x1000 +#define AML_CONSTANT 0x2000 +#define AML_NO_OPERAND_RESOLVE 0x4000 + +/* Convenient flag groupings */ + +#define AML_FLAGS_EXEC_0A_0T_1R AML_HAS_RETVAL +#define AML_FLAGS_EXEC_1A_0T_0R AML_HAS_ARGS /* Monadic1 */ +#define AML_FLAGS_EXEC_1A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL /* Monadic2 */ +#define AML_FLAGS_EXEC_1A_1T_0R AML_HAS_ARGS | AML_HAS_TARGET +#define AML_FLAGS_EXEC_1A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL /* monadic2_r */ +#define AML_FLAGS_EXEC_2A_0T_0R AML_HAS_ARGS /* Dyadic1 */ +#define AML_FLAGS_EXEC_2A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL /* Dyadic2 */ +#define AML_FLAGS_EXEC_2A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL /* dyadic2_r */ +#define AML_FLAGS_EXEC_2A_2T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL +#define AML_FLAGS_EXEC_3A_0T_0R AML_HAS_ARGS +#define AML_FLAGS_EXEC_3A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL +#define AML_FLAGS_EXEC_6A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL + +/* + * The opcode Type is used in a dispatch table, do not change + * without updating the table. + */ +#define AML_TYPE_EXEC_0A_0T_1R 0x00 +#define AML_TYPE_EXEC_1A_0T_0R 0x01 /* Monadic1 */ +#define AML_TYPE_EXEC_1A_0T_1R 0x02 /* Monadic2 */ +#define AML_TYPE_EXEC_1A_1T_0R 0x03 +#define AML_TYPE_EXEC_1A_1T_1R 0x04 /* monadic2_r */ +#define AML_TYPE_EXEC_2A_0T_0R 0x05 /* Dyadic1 */ +#define AML_TYPE_EXEC_2A_0T_1R 0x06 /* Dyadic2 */ +#define AML_TYPE_EXEC_2A_1T_1R 0x07 /* dyadic2_r */ +#define AML_TYPE_EXEC_2A_2T_1R 0x08 +#define AML_TYPE_EXEC_3A_0T_0R 0x09 +#define AML_TYPE_EXEC_3A_1T_1R 0x0A +#define AML_TYPE_EXEC_6A_0T_1R 0x0B +/* End of types used in dispatch table */ + +#define AML_TYPE_LITERAL 0x0B +#define AML_TYPE_CONSTANT 0x0C +#define AML_TYPE_METHOD_ARGUMENT 0x0D +#define AML_TYPE_LOCAL_VARIABLE 0x0E +#define AML_TYPE_DATA_TERM 0x0F + +/* Generic for an op that returns a value */ + +#define AML_TYPE_METHOD_CALL 0x10 + +/* Misc */ + +#define AML_TYPE_CREATE_FIELD 0x11 +#define AML_TYPE_CREATE_OBJECT 0x12 +#define AML_TYPE_CONTROL 0x13 +#define AML_TYPE_NAMED_NO_OBJ 0x14 +#define AML_TYPE_NAMED_FIELD 0x15 +#define AML_TYPE_NAMED_SIMPLE 0x16 +#define AML_TYPE_NAMED_COMPLEX 0x17 +#define AML_TYPE_RETURN 0x18 + +#define AML_TYPE_UNDEFINED 0x19 +#define AML_TYPE_BOGUS 0x1A + +/* AML Package Length encodings */ + +#define ACPI_AML_PACKAGE_TYPE1 0x40 +#define ACPI_AML_PACKAGE_TYPE2 0x4000 +#define ACPI_AML_PACKAGE_TYPE3 0x400000 +#define ACPI_AML_PACKAGE_TYPE4 0x40000000 + +/* + * Opcode classes + */ +#define AML_CLASS_EXECUTE 0x00 +#define AML_CLASS_CREATE 0x01 +#define AML_CLASS_ARGUMENT 0x02 +#define AML_CLASS_NAMED_OBJECT 0x03 +#define AML_CLASS_CONTROL 0x04 +#define AML_CLASS_ASCII 0x05 +#define AML_CLASS_PREFIX 0x06 +#define AML_CLASS_INTERNAL 0x07 +#define AML_CLASS_RETURN_VALUE 0x08 +#define AML_CLASS_METHOD_CALL 0x09 +#define AML_CLASS_UNKNOWN 0x0A + +/* Comparison operation codes for match_op operator */ + +typedef enum { + MATCH_MTR = 0, + MATCH_MEQ = 1, + MATCH_MLE = 2, + MATCH_MLT = 3, + MATCH_MGE = 4, + MATCH_MGT = 5 +} AML_MATCH_OPERATOR; + +#define MAX_MATCH_OPERATOR 5 + +/* + * field_flags + * + * This byte is extracted from the AML and includes three separate + * pieces of information about the field: + * 1) The field access type + * 2) The field update rule + * 3) The lock rule for the field + * + * Bits 00 - 03 : access_type (any_acc, byte_acc, etc.) + * 04 : lock_rule (1 == Lock) + * 05 - 06 : update_rule + */ +#define AML_FIELD_ACCESS_TYPE_MASK 0x0F +#define AML_FIELD_LOCK_RULE_MASK 0x10 +#define AML_FIELD_UPDATE_RULE_MASK 0x60 + +/* 1) Field Access Types */ + +typedef enum { + AML_FIELD_ACCESS_ANY = 0x00, + AML_FIELD_ACCESS_BYTE = 0x01, + AML_FIELD_ACCESS_WORD = 0x02, + AML_FIELD_ACCESS_DWORD = 0x03, + AML_FIELD_ACCESS_QWORD = 0x04, /* ACPI 2.0 */ + AML_FIELD_ACCESS_BUFFER = 0x05 /* ACPI 2.0 */ +} AML_ACCESS_TYPE; + +/* 2) Field Lock Rules */ + +typedef enum { + AML_FIELD_LOCK_NEVER = 0x00, + AML_FIELD_LOCK_ALWAYS = 0x10 +} AML_LOCK_RULE; + +/* 3) Field Update Rules */ + +typedef enum { + AML_FIELD_UPDATE_PRESERVE = 0x00, + AML_FIELD_UPDATE_WRITE_AS_ONES = 0x20, + AML_FIELD_UPDATE_WRITE_AS_ZEROS = 0x40 +} AML_UPDATE_RULE; + +/* + * Field Access Attributes. + * This byte is extracted from the AML via the + * access_as keyword + */ +typedef enum { + AML_FIELD_ATTRIB_QUICK = 0x02, + AML_FIELD_ATTRIB_SEND_RCV = 0x04, + AML_FIELD_ATTRIB_BYTE = 0x06, + AML_FIELD_ATTRIB_WORD = 0x08, + AML_FIELD_ATTRIB_BLOCK = 0x0A, + AML_FIELD_ATTRIB_MULTIBYTE = 0x0B, + AML_FIELD_ATTRIB_WORD_CALL = 0x0C, + AML_FIELD_ATTRIB_BLOCK_CALL = 0x0D, + AML_FIELD_ATTRIB_RAW_BYTES = 0x0E, + AML_FIELD_ATTRIB_RAW_PROCESS = 0x0F +} AML_ACCESS_ATTRIBUTE; + +/* Bit fields in the AML method_flags byte */ + +#define AML_METHOD_ARG_COUNT 0x07 +#define AML_METHOD_SERIALIZED 0x08 +#define AML_METHOD_SYNC_LEVEL 0xF0 + +#endif /* __AMLCODE_H__ */ diff --git a/kernel/drivers/acpi/acpica/amlresrc.h b/kernel/drivers/acpi/acpica/amlresrc.h new file mode 100644 index 000000000..ee0cdd60b --- /dev/null +++ b/kernel/drivers/acpi/acpica/amlresrc.h @@ -0,0 +1,486 @@ +/****************************************************************************** + * + * Module Name: amlresrc.h - AML resource descriptors + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +#ifndef __AMLRESRC_H +#define __AMLRESRC_H + +/* + * Resource descriptor tags, as defined in the ACPI specification. + * Used to symbolically reference fields within a descriptor. + */ +#define ACPI_RESTAG_ADDRESS "_ADR" +#define ACPI_RESTAG_ALIGNMENT "_ALN" +#define ACPI_RESTAG_ADDRESSSPACE "_ASI" +#define ACPI_RESTAG_ACCESSSIZE "_ASZ" +#define ACPI_RESTAG_TYPESPECIFICATTRIBUTES "_ATT" +#define ACPI_RESTAG_BASEADDRESS "_BAS" +#define ACPI_RESTAG_BUSMASTER "_BM_" /* Master(1), Slave(0) */ +#define ACPI_RESTAG_DEBOUNCETIME "_DBT" +#define ACPI_RESTAG_DECODE "_DEC" +#define ACPI_RESTAG_DEVICEPOLARITY "_DPL" +#define ACPI_RESTAG_DMA "_DMA" +#define ACPI_RESTAG_DMATYPE "_TYP" /* Compatible(0), A(1), B(2), F(3) */ +#define ACPI_RESTAG_DRIVESTRENGTH "_DRS" +#define ACPI_RESTAG_ENDIANNESS "_END" +#define ACPI_RESTAG_FLOWCONTROL "_FLC" +#define ACPI_RESTAG_GRANULARITY "_GRA" +#define ACPI_RESTAG_INTERRUPT "_INT" +#define ACPI_RESTAG_INTERRUPTLEVEL "_LL_" /* active_lo(1), active_hi(0) */ +#define ACPI_RESTAG_INTERRUPTSHARE "_SHR" /* Shareable(1), no_share(0) */ +#define ACPI_RESTAG_INTERRUPTTYPE "_HE_" /* Edge(1), Level(0) */ +#define ACPI_RESTAG_IORESTRICTION "_IOR" +#define ACPI_RESTAG_LENGTH "_LEN" +#define ACPI_RESTAG_LINE "_LIN" +#define ACPI_RESTAG_MEMATTRIBUTES "_MTP" /* Memory(0), Reserved(1), ACPI(2), NVS(3) */ +#define ACPI_RESTAG_MEMTYPE "_MEM" /* non_cache(0), Cacheable(1) Cache+combine(2), Cache+prefetch(3) */ +#define ACPI_RESTAG_MAXADDR "_MAX" +#define ACPI_RESTAG_MINADDR "_MIN" +#define ACPI_RESTAG_MAXTYPE "_MAF" +#define ACPI_RESTAG_MINTYPE "_MIF" +#define ACPI_RESTAG_MODE "_MOD" +#define ACPI_RESTAG_PARITY "_PAR" +#define ACPI_RESTAG_PHASE "_PHA" +#define ACPI_RESTAG_PIN "_PIN" +#define ACPI_RESTAG_PINCONFIG "_PPI" +#define ACPI_RESTAG_POLARITY "_POL" +#define ACPI_RESTAG_REGISTERBITOFFSET "_RBO" +#define ACPI_RESTAG_REGISTERBITWIDTH "_RBW" +#define ACPI_RESTAG_RANGETYPE "_RNG" +#define ACPI_RESTAG_READWRITETYPE "_RW_" /* read_only(0), Writeable (1) */ +#define ACPI_RESTAG_LENGTH_RX "_RXL" +#define ACPI_RESTAG_LENGTH_TX "_TXL" +#define ACPI_RESTAG_SLAVEMODE "_SLV" +#define ACPI_RESTAG_SPEED "_SPE" +#define ACPI_RESTAG_STOPBITS "_STB" +#define ACPI_RESTAG_TRANSLATION "_TRA" +#define ACPI_RESTAG_TRANSTYPE "_TRS" /* Sparse(1), Dense(0) */ +#define ACPI_RESTAG_TYPE "_TTP" /* Translation(1), Static (0) */ +#define ACPI_RESTAG_XFERTYPE "_SIZ" /* 8(0), 8And16(1), 16(2) */ +#define ACPI_RESTAG_VENDORDATA "_VEN" + +/* Default sizes for "small" resource descriptors */ + +#define ASL_RDESC_IRQ_SIZE 0x02 +#define ASL_RDESC_DMA_SIZE 0x02 +#define ASL_RDESC_ST_DEPEND_SIZE 0x00 +#define ASL_RDESC_END_DEPEND_SIZE 0x00 +#define ASL_RDESC_IO_SIZE 0x07 +#define ASL_RDESC_FIXED_IO_SIZE 0x03 +#define ASL_RDESC_FIXED_DMA_SIZE 0x05 +#define ASL_RDESC_END_TAG_SIZE 0x01 + +struct asl_resource_node { + u32 buffer_length; + void *buffer; + struct asl_resource_node *next; +}; + +struct asl_resource_info { + union acpi_parse_object *descriptor_type_op; /* Resource descriptor parse node */ + union acpi_parse_object *mapping_op; /* Used for mapfile support */ + u32 current_byte_offset; /* Offset in resource template */ +}; + +/* Macros used to generate AML resource length fields */ + +#define ACPI_AML_SIZE_LARGE(r) (sizeof (r) - sizeof (struct aml_resource_large_header)) +#define ACPI_AML_SIZE_SMALL(r) (sizeof (r) - sizeof (struct aml_resource_small_header)) + +/* + * Resource descriptors defined in the ACPI specification. + * + * Packing/alignment must be BYTE because these descriptors + * are used to overlay the raw AML byte stream. + */ +#pragma pack(1) + +/* + * SMALL descriptors + */ +#define AML_RESOURCE_SMALL_HEADER_COMMON \ + u8 descriptor_type; + +struct aml_resource_small_header { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_irq { + AML_RESOURCE_SMALL_HEADER_COMMON u16 irq_mask; + u8 flags; +}; + +struct aml_resource_irq_noflags { + AML_RESOURCE_SMALL_HEADER_COMMON u16 irq_mask; +}; + +struct aml_resource_dma { + AML_RESOURCE_SMALL_HEADER_COMMON u8 dma_channel_mask; + u8 flags; +}; + +struct aml_resource_start_dependent { + AML_RESOURCE_SMALL_HEADER_COMMON u8 flags; +}; + +struct aml_resource_start_dependent_noprio { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_end_dependent { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_io { + AML_RESOURCE_SMALL_HEADER_COMMON u8 flags; + u16 minimum; + u16 maximum; + u8 alignment; + u8 address_length; +}; + +struct aml_resource_fixed_io { + AML_RESOURCE_SMALL_HEADER_COMMON u16 address; + u8 address_length; +}; + +struct aml_resource_vendor_small { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_end_tag { + AML_RESOURCE_SMALL_HEADER_COMMON u8 checksum; +}; + +struct aml_resource_fixed_dma { + AML_RESOURCE_SMALL_HEADER_COMMON u16 request_lines; + u16 channels; + u8 width; +}; + +/* + * LARGE descriptors + */ +#define AML_RESOURCE_LARGE_HEADER_COMMON \ + u8 descriptor_type;\ + u16 resource_length; + +struct aml_resource_large_header { +AML_RESOURCE_LARGE_HEADER_COMMON}; + +/* General Flags for address space resource descriptors */ + +#define ACPI_RESOURCE_FLAG_DEC 2 +#define ACPI_RESOURCE_FLAG_MIF 4 +#define ACPI_RESOURCE_FLAG_MAF 8 + +struct aml_resource_memory24 { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u16 minimum; + u16 maximum; + u16 alignment; + u16 address_length; +}; + +struct aml_resource_vendor_large { +AML_RESOURCE_LARGE_HEADER_COMMON}; + +struct aml_resource_memory32 { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u32 minimum; + u32 maximum; + u32 alignment; + u32 address_length; +}; + +struct aml_resource_fixed_memory32 { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u32 address; + u32 address_length; +}; + +#define AML_RESOURCE_ADDRESS_COMMON \ + u8 resource_type; \ + u8 flags; \ + u8 specific_flags; + +struct aml_resource_address { +AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_ADDRESS_COMMON}; + +struct aml_resource_extended_address64 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u8 revision_ID; + u8 reserved; + u64 granularity; + u64 minimum; + u64 maximum; + u64 translation_offset; + u64 address_length; + u64 type_specific; +}; + +#define AML_RESOURCE_EXTENDED_ADDRESS_REVISION 1 /* ACPI 3.0 */ + +struct aml_resource_address64 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u64 granularity; + u64 minimum; + u64 maximum; + u64 translation_offset; + u64 address_length; +}; + +struct aml_resource_address32 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u32 granularity; + u32 minimum; + u32 maximum; + u32 translation_offset; + u32 address_length; +}; + +struct aml_resource_address16 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u16 granularity; + u16 minimum; + u16 maximum; + u16 translation_offset; + u16 address_length; +}; + +struct aml_resource_extended_irq { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u8 interrupt_count; + u32 interrupts[1]; + /* res_source_index, res_source optional fields follow */ +}; + +struct aml_resource_generic_register { + AML_RESOURCE_LARGE_HEADER_COMMON u8 address_space_id; + u8 bit_width; + u8 bit_offset; + u8 access_size; /* ACPI 3.0, was previously Reserved */ + u64 address; +}; + +/* Common descriptor for gpio_int and gpio_io (ACPI 5.0) */ + +struct aml_resource_gpio { + AML_RESOURCE_LARGE_HEADER_COMMON u8 revision_id; + u8 connection_type; + u16 flags; + u16 int_flags; + u8 pin_config; + u16 drive_strength; + u16 debounce_timeout; + u16 pin_table_offset; + u8 res_source_index; + u16 res_source_offset; + u16 vendor_offset; + u16 vendor_length; + /* + * Optional fields follow immediately: + * 1) PIN list (Words) + * 2) Resource Source String + * 3) Vendor Data bytes + */ +}; + +#define AML_RESOURCE_GPIO_REVISION 1 /* ACPI 5.0 */ + +/* Values for connection_type above */ + +#define AML_RESOURCE_GPIO_TYPE_INT 0 +#define AML_RESOURCE_GPIO_TYPE_IO 1 +#define AML_RESOURCE_MAX_GPIOTYPE 1 + +/* Common preamble for all serial descriptors (ACPI 5.0) */ + +#define AML_RESOURCE_SERIAL_COMMON \ + u8 revision_id; \ + u8 res_source_index; \ + u8 type; \ + u8 flags; \ + u16 type_specific_flags; \ + u8 type_revision_id; \ + u16 type_data_length; \ + +/* Values for the type field above */ + +#define AML_RESOURCE_I2C_SERIALBUSTYPE 1 +#define AML_RESOURCE_SPI_SERIALBUSTYPE 2 +#define AML_RESOURCE_UART_SERIALBUSTYPE 3 +#define AML_RESOURCE_MAX_SERIALBUSTYPE 3 +#define AML_RESOURCE_VENDOR_SERIALBUSTYPE 192 /* Vendor defined is 0xC0-0xFF (NOT SUPPORTED) */ + +struct aml_resource_common_serialbus { +AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_SERIAL_COMMON}; + +struct aml_resource_i2c_serialbus { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_SERIAL_COMMON u32 connection_speed; + u16 slave_address; + /* + * Optional fields follow immediately: + * 1) Vendor Data bytes + * 2) Resource Source String + */ +}; + +#define AML_RESOURCE_I2C_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_I2C_TYPE_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_I2C_MIN_DATA_LEN 6 + +struct aml_resource_spi_serialbus { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_SERIAL_COMMON u32 connection_speed; + u8 data_bit_length; + u8 clock_phase; + u8 clock_polarity; + u16 device_selection; + /* + * Optional fields follow immediately: + * 1) Vendor Data bytes + * 2) Resource Source String + */ +}; + +#define AML_RESOURCE_SPI_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_SPI_TYPE_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_SPI_MIN_DATA_LEN 9 + +struct aml_resource_uart_serialbus { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_SERIAL_COMMON u32 default_baud_rate; + u16 rx_fifo_size; + u16 tx_fifo_size; + u8 parity; + u8 lines_enabled; + /* + * Optional fields follow immediately: + * 1) Vendor Data bytes + * 2) Resource Source String + */ +}; + +#define AML_RESOURCE_UART_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_UART_TYPE_REVISION 1 /* ACPI 5.0 */ +#define AML_RESOURCE_UART_MIN_DATA_LEN 10 + +/* restore default alignment */ + +#pragma pack() + +/* Union of all resource descriptors, so we can allocate the worst case */ + +union aml_resource { + /* Descriptor headers */ + + u8 descriptor_type; + struct aml_resource_small_header small_header; + struct aml_resource_large_header large_header; + + /* Small resource descriptors */ + + struct aml_resource_irq irq; + struct aml_resource_dma dma; + struct aml_resource_start_dependent start_dpf; + struct aml_resource_end_dependent end_dpf; + struct aml_resource_io io; + struct aml_resource_fixed_io fixed_io; + struct aml_resource_fixed_dma fixed_dma; + struct aml_resource_vendor_small vendor_small; + struct aml_resource_end_tag end_tag; + + /* Large resource descriptors */ + + struct aml_resource_memory24 memory24; + struct aml_resource_generic_register generic_reg; + struct aml_resource_vendor_large vendor_large; + struct aml_resource_memory32 memory32; + struct aml_resource_fixed_memory32 fixed_memory32; + struct aml_resource_address16 address16; + struct aml_resource_address32 address32; + struct aml_resource_address64 address64; + struct aml_resource_extended_address64 ext_address64; + struct aml_resource_extended_irq extended_irq; + struct aml_resource_gpio gpio; + struct aml_resource_i2c_serialbus i2c_serial_bus; + struct aml_resource_spi_serialbus spi_serial_bus; + struct aml_resource_uart_serialbus uart_serial_bus; + struct aml_resource_common_serialbus common_serial_bus; + + /* Utility overlays */ + + struct aml_resource_address address; + u32 dword_item; + u16 word_item; + u8 byte_item; +}; + +/* Interfaces used by both the disassembler and compiler */ + +void +mp_save_gpio_info(union acpi_parse_object *op, + union aml_resource *resource, + u32 pin_count, u16 *pin_list, char *device_name); + +void +mp_save_serial_info(union acpi_parse_object *op, + union aml_resource *resource, char *device_name); + +char *mp_get_hid_from_parse_tree(struct acpi_namespace_node *hid_node); + +char *mp_get_hid_via_namestring(char *device_name); + +char *mp_get_connection_info(union acpi_parse_object *op, + u32 pin_index, + struct acpi_namespace_node **target_node, + char **target_name); + +char *mp_get_parent_device_hid(union acpi_parse_object *op, + struct acpi_namespace_node **target_node, + char **parent_device_name); + +char *mp_get_ddn_value(char *device_name); + +char *mp_get_hid_value(struct acpi_namespace_node *device_node); + +#endif diff --git a/kernel/drivers/acpi/acpica/dsargs.c b/kernel/drivers/acpi/acpica/dsargs.c new file mode 100644 index 000000000..3e6989738 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsargs.c @@ -0,0 +1,405 @@ +/****************************************************************************** + * + * Module Name: dsargs - Support for execution of dynamic arguments for static + * objects (regions, fields, buffer fields, etc.) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsargs") + +/* Local prototypes */ +static acpi_status +acpi_ds_execute_arguments(struct acpi_namespace_node *node, + struct acpi_namespace_node *scope_node, + u32 aml_length, u8 *aml_start); + +/******************************************************************************* + * + * FUNCTION: acpi_ds_execute_arguments + * + * PARAMETERS: node - Object NS node + * scope_node - Parent NS node + * aml_length - Length of executable AML + * aml_start - Pointer to the AML + * + * RETURN: Status. + * + * DESCRIPTION: Late (deferred) execution of region or field arguments + * + ******************************************************************************/ + +static acpi_status +acpi_ds_execute_arguments(struct acpi_namespace_node *node, + struct acpi_namespace_node *scope_node, + u32 aml_length, u8 *aml_start) +{ + acpi_status status; + union acpi_parse_object *op; + struct acpi_walk_state *walk_state; + + ACPI_FUNCTION_TRACE(ds_execute_arguments); + + /* Allocate a new parser op to be the root of the parsed tree */ + + op = acpi_ps_alloc_op(AML_INT_EVAL_SUBTREE_OP); + if (!op) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Save the Node for use in acpi_ps_parse_aml */ + + op->common.node = scope_node; + + /* Create and initialize a new parser state */ + + walk_state = acpi_ds_create_walk_state(0, NULL, NULL, NULL); + if (!walk_state) { + status = AE_NO_MEMORY; + goto cleanup; + } + + status = acpi_ds_init_aml_walk(walk_state, op, NULL, aml_start, + aml_length, NULL, ACPI_IMODE_LOAD_PASS1); + if (ACPI_FAILURE(status)) { + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + + /* Mark this parse as a deferred opcode */ + + walk_state->parse_flags = ACPI_PARSE_DEFERRED_OP; + walk_state->deferred_node = node; + + /* Pass1: Parse the entire declaration */ + + status = acpi_ps_parse_aml(walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Get and init the Op created above */ + + op->common.node = node; + acpi_ps_delete_parse_tree(op); + + /* Evaluate the deferred arguments */ + + op = acpi_ps_alloc_op(AML_INT_EVAL_SUBTREE_OP); + if (!op) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + op->common.node = scope_node; + + /* Create and initialize a new parser state */ + + walk_state = acpi_ds_create_walk_state(0, NULL, NULL, NULL); + if (!walk_state) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Execute the opcode and arguments */ + + status = acpi_ds_init_aml_walk(walk_state, op, NULL, aml_start, + aml_length, NULL, ACPI_IMODE_EXECUTE); + if (ACPI_FAILURE(status)) { + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + + /* Mark this execution as a deferred opcode */ + + walk_state->deferred_node = node; + status = acpi_ps_parse_aml(walk_state); + +cleanup: + acpi_ps_delete_parse_tree(op); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_buffer_field_arguments + * + * PARAMETERS: obj_desc - A valid buffer_field object + * + * RETURN: Status. + * + * DESCRIPTION: Get buffer_field Buffer and Index. This implements the late + * evaluation of these field attributes. + * + ******************************************************************************/ + +acpi_status +acpi_ds_get_buffer_field_arguments(union acpi_operand_object *obj_desc) +{ + union acpi_operand_object *extra_desc; + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ds_get_buffer_field_arguments, obj_desc); + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS(AE_OK); + } + + /* Get the AML pointer (method object) and buffer_field node */ + + extra_desc = acpi_ns_get_secondary_object(obj_desc); + node = obj_desc->buffer_field.node; + + ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname(ACPI_TYPE_BUFFER_FIELD, + node, NULL)); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "[%4.4s] BufferField Arg Init\n", + acpi_ut_get_node_name(node))); + + /* Execute the AML code for the term_arg arguments */ + + status = acpi_ds_execute_arguments(node, node->parent, + extra_desc->extra.aml_length, + extra_desc->extra.aml_start); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_bank_field_arguments + * + * PARAMETERS: obj_desc - A valid bank_field object + * + * RETURN: Status. + * + * DESCRIPTION: Get bank_field bank_value. This implements the late + * evaluation of these field attributes. + * + ******************************************************************************/ + +acpi_status +acpi_ds_get_bank_field_arguments(union acpi_operand_object *obj_desc) +{ + union acpi_operand_object *extra_desc; + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ds_get_bank_field_arguments, obj_desc); + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS(AE_OK); + } + + /* Get the AML pointer (method object) and bank_field node */ + + extra_desc = acpi_ns_get_secondary_object(obj_desc); + node = obj_desc->bank_field.node; + + ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname + (ACPI_TYPE_LOCAL_BANK_FIELD, node, NULL)); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "[%4.4s] BankField Arg Init\n", + acpi_ut_get_node_name(node))); + + /* Execute the AML code for the term_arg arguments */ + + status = acpi_ds_execute_arguments(node, node->parent, + extra_desc->extra.aml_length, + extra_desc->extra.aml_start); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ut_add_address_range(obj_desc->region.space_id, + obj_desc->region.address, + obj_desc->region.length, node); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_buffer_arguments + * + * PARAMETERS: obj_desc - A valid Buffer object + * + * RETURN: Status. + * + * DESCRIPTION: Get Buffer length and initializer byte list. This implements + * the late evaluation of these attributes. + * + ******************************************************************************/ + +acpi_status acpi_ds_get_buffer_arguments(union acpi_operand_object *obj_desc) +{ + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ds_get_buffer_arguments, obj_desc); + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS(AE_OK); + } + + /* Get the Buffer node */ + + node = obj_desc->buffer.node; + if (!node) { + ACPI_ERROR((AE_INFO, + "No pointer back to namespace node in buffer object %p", + obj_desc)); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Buffer Arg Init\n")); + + /* Execute the AML code for the term_arg arguments */ + + status = acpi_ds_execute_arguments(node, node, + obj_desc->buffer.aml_length, + obj_desc->buffer.aml_start); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_package_arguments + * + * PARAMETERS: obj_desc - A valid Package object + * + * RETURN: Status. + * + * DESCRIPTION: Get Package length and initializer byte list. This implements + * the late evaluation of these attributes. + * + ******************************************************************************/ + +acpi_status acpi_ds_get_package_arguments(union acpi_operand_object *obj_desc) +{ + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ds_get_package_arguments, obj_desc); + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS(AE_OK); + } + + /* Get the Package node */ + + node = obj_desc->package.node; + if (!node) { + ACPI_ERROR((AE_INFO, + "No pointer back to namespace node in package %p", + obj_desc)); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Package Arg Init\n")); + + /* Execute the AML code for the term_arg arguments */ + + status = acpi_ds_execute_arguments(node, node, + obj_desc->package.aml_length, + obj_desc->package.aml_start); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_region_arguments + * + * PARAMETERS: obj_desc - A valid region object + * + * RETURN: Status. + * + * DESCRIPTION: Get region address and length. This implements the late + * evaluation of these region attributes. + * + ******************************************************************************/ + +acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc) +{ + struct acpi_namespace_node *node; + acpi_status status; + union acpi_operand_object *extra_desc; + + ACPI_FUNCTION_TRACE_PTR(ds_get_region_arguments, obj_desc); + + if (obj_desc->region.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS(AE_OK); + } + + extra_desc = acpi_ns_get_secondary_object(obj_desc); + if (!extra_desc) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Get the Region node */ + + node = obj_desc->region.node; + + ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname + (ACPI_TYPE_REGION, node, NULL)); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "[%4.4s] OpRegion Arg Init at AML %p\n", + acpi_ut_get_node_name(node), + extra_desc->extra.aml_start)); + + /* Execute the argument AML */ + + status = acpi_ds_execute_arguments(node, extra_desc->extra.scope_node, + extra_desc->extra.aml_length, + extra_desc->extra.aml_start); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ut_add_address_range(obj_desc->region.space_id, + obj_desc->region.address, + obj_desc->region.length, node); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dscontrol.c b/kernel/drivers/acpi/acpica/dscontrol.c new file mode 100644 index 000000000..39da9da62 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dscontrol.c @@ -0,0 +1,410 @@ +/****************************************************************************** + * + * Module Name: dscontrol - Support for execution control opcodes - + * if/else/while/return + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dscontrol") + +/******************************************************************************* + * + * FUNCTION: acpi_ds_exec_begin_control_op + * + * PARAMETERS: walk_list - The list that owns the walk stack + * op - The control Op + * + * RETURN: Status + * + * DESCRIPTION: Handles all control ops encountered during control method + * execution. + * + ******************************************************************************/ +acpi_status +acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status = AE_OK; + union acpi_generic_state *control_state; + + ACPI_FUNCTION_NAME(ds_exec_begin_control_op); + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n", + op, op->common.aml_opcode, walk_state)); + + switch (op->common.aml_opcode) { + case AML_WHILE_OP: + /* + * If this is an additional iteration of a while loop, continue. + * There is no need to allocate a new control state. + */ + if (walk_state->control_state) { + if (walk_state->control_state->control. + aml_predicate_start == + (walk_state->parser_state.aml - 1)) { + + /* Reset the state to start-of-loop */ + + walk_state->control_state->common.state = + ACPI_CONTROL_CONDITIONAL_EXECUTING; + break; + } + } + + /*lint -fallthrough */ + + case AML_IF_OP: + /* + * IF/WHILE: Create a new control state to manage these + * constructs. We need to manage these as a stack, in order + * to handle nesting. + */ + control_state = acpi_ut_create_control_state(); + if (!control_state) { + status = AE_NO_MEMORY; + break; + } + /* + * Save a pointer to the predicate for multiple executions + * of a loop + */ + control_state->control.aml_predicate_start = + walk_state->parser_state.aml - 1; + control_state->control.package_end = + walk_state->parser_state.pkg_end; + control_state->control.opcode = op->common.aml_opcode; + + /* Push the control state on this walk's control stack */ + + acpi_ut_push_generic_state(&walk_state->control_state, + control_state); + break; + + case AML_ELSE_OP: + + /* Predicate is in the state object */ + /* If predicate is true, the IF was executed, ignore ELSE part */ + + if (walk_state->last_predicate) { + status = AE_CTRL_TRUE; + } + + break; + + case AML_RETURN_OP: + + break; + + default: + + break; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_exec_end_control_op + * + * PARAMETERS: walk_list - The list that owns the walk stack + * op - The control Op + * + * RETURN: Status + * + * DESCRIPTION: Handles all control ops encountered during control method + * execution. + * + ******************************************************************************/ + +acpi_status +acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, + union acpi_parse_object * op) +{ + acpi_status status = AE_OK; + union acpi_generic_state *control_state; + + ACPI_FUNCTION_NAME(ds_exec_end_control_op); + + switch (op->common.aml_opcode) { + case AML_IF_OP: + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", op)); + + /* + * Save the result of the predicate in case there is an + * ELSE to come + */ + walk_state->last_predicate = + (u8)walk_state->control_state->common.value; + + /* + * Pop the control state that was created at the start + * of the IF and free it + */ + control_state = + acpi_ut_pop_generic_state(&walk_state->control_state); + acpi_ut_delete_generic_state(control_state); + break; + + case AML_ELSE_OP: + + break; + + case AML_WHILE_OP: + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", op)); + + control_state = walk_state->control_state; + if (control_state->common.value) { + + /* Predicate was true, the body of the loop was just executed */ + + /* + * This loop counter mechanism allows the interpreter to escape + * possibly infinite loops. This can occur in poorly written AML + * when the hardware does not respond within a while loop and the + * loop does not implement a timeout. + */ + control_state->control.loop_count++; + if (control_state->control.loop_count > + ACPI_MAX_LOOP_ITERATIONS) { + status = AE_AML_INFINITE_LOOP; + break; + } + + /* + * Go back and evaluate the predicate and maybe execute the loop + * another time + */ + status = AE_CTRL_PENDING; + walk_state->aml_last_while = + control_state->control.aml_predicate_start; + break; + } + + /* Predicate was false, terminate this while loop */ + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "[WHILE_OP] termination! Op=%p\n", op)); + + /* Pop this control state and free it */ + + control_state = + acpi_ut_pop_generic_state(&walk_state->control_state); + acpi_ut_delete_generic_state(control_state); + break; + + case AML_RETURN_OP: + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "[RETURN_OP] Op=%p Arg=%p\n", op, + op->common.value.arg)); + + /* + * One optional operand -- the return value + * It can be either an immediate operand or a result that + * has been bubbled up the tree + */ + if (op->common.value.arg) { + + /* Since we have a real Return(), delete any implicit return */ + + acpi_ds_clear_implicit_return(walk_state); + + /* Return statement has an immediate operand */ + + status = + acpi_ds_create_operands(walk_state, + op->common.value.arg); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * If value being returned is a Reference (such as + * an arg or local), resolve it now because it may + * cease to exist at the end of the method. + */ + status = + acpi_ex_resolve_to_value(&walk_state->operands[0], + walk_state); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Get the return value and save as the last result + * value. This is the only place where walk_state->return_desc + * is set to anything other than zero! + */ + walk_state->return_desc = walk_state->operands[0]; + } else if (walk_state->result_count) { + + /* Since we have a real Return(), delete any implicit return */ + + acpi_ds_clear_implicit_return(walk_state); + + /* + * The return value has come from a previous calculation. + * + * If value being returned is a Reference (such as + * an arg or local), resolve it now because it may + * cease to exist at the end of the method. + * + * Allow references created by the Index operator to return + * unchanged. + */ + if ((ACPI_GET_DESCRIPTOR_TYPE + (walk_state->results->results.obj_desc[0]) == + ACPI_DESC_TYPE_OPERAND) + && ((walk_state->results->results.obj_desc[0])-> + common.type == ACPI_TYPE_LOCAL_REFERENCE) + && ((walk_state->results->results.obj_desc[0])-> + reference.class != ACPI_REFCLASS_INDEX)) { + status = + acpi_ex_resolve_to_value(&walk_state-> + results->results. + obj_desc[0], + walk_state); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + walk_state->return_desc = + walk_state->results->results.obj_desc[0]; + } else { + /* No return operand */ + + if (walk_state->num_operands) { + acpi_ut_remove_reference(walk_state-> + operands[0]); + } + + walk_state->operands[0] = NULL; + walk_state->num_operands = 0; + walk_state->return_desc = NULL; + } + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Completed RETURN_OP State=%p, RetVal=%p\n", + walk_state, walk_state->return_desc)); + + /* End the control method execution right now */ + + status = AE_CTRL_TERMINATE; + break; + + case AML_NOOP_OP: + + /* Just do nothing! */ + + break; + + case AML_BREAK_POINT_OP: + + /* + * Set the single-step flag. This will cause the debugger (if present) + * to break to the console within the AML debugger at the start of the + * next AML instruction. + */ + ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE); + ACPI_DEBUGGER_EXEC(acpi_os_printf + ("**break** Executed AML BreakPoint opcode\n")); + + /* Call to the OSL in case OS wants a piece of the action */ + + status = acpi_os_signal(ACPI_SIGNAL_BREAKPOINT, + "Executed AML Breakpoint opcode"); + break; + + case AML_BREAK_OP: + case AML_CONTINUE_OP: /* ACPI 2.0 */ + + /* Pop and delete control states until we find a while */ + + while (walk_state->control_state && + (walk_state->control_state->control.opcode != + AML_WHILE_OP)) { + control_state = + acpi_ut_pop_generic_state(&walk_state-> + control_state); + acpi_ut_delete_generic_state(control_state); + } + + /* No while found? */ + + if (!walk_state->control_state) { + return (AE_AML_NO_WHILE); + } + + /* Was: walk_state->aml_last_while = walk_state->control_state->Control.aml_predicate_start; */ + + walk_state->aml_last_while = + walk_state->control_state->control.package_end; + + /* Return status depending on opcode */ + + if (op->common.aml_opcode == AML_BREAK_OP) { + status = AE_CTRL_BREAK; + } else { + status = AE_CTRL_CONTINUE; + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown control opcode=0x%X Op=%p", + op->common.aml_opcode, op)); + + status = AE_AML_BAD_OPCODE; + break; + } + + return (status); +} diff --git a/kernel/drivers/acpi/acpica/dsfield.c b/kernel/drivers/acpi/acpica/dsfield.c new file mode 100644 index 000000000..43b40de90 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsfield.c @@ -0,0 +1,794 @@ +/****************************************************************************** + * + * Module Name: dsfield - Dispatcher field routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acparser.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsfield") + +/* Local prototypes */ +#ifdef ACPI_ASL_COMPILER +#include "acdisasm.h" +static acpi_status +acpi_ds_create_external_region(acpi_status lookup_status, + union acpi_parse_object *op, + char *path, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **node); +#endif + +static acpi_status +acpi_ds_get_field_names(struct acpi_create_field_info *info, + struct acpi_walk_state *walk_state, + union acpi_parse_object *arg); + +#ifdef ACPI_ASL_COMPILER +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_external_region (iASL Disassembler only) + * + * PARAMETERS: lookup_status - Status from ns_lookup operation + * op - Op containing the Field definition and args + * path - Pathname of the region + * ` walk_state - Current method state + * node - Where the new region node is returned + * + * RETURN: Status + * + * DESCRIPTION: Add region to the external list if NOT_FOUND. Create a new + * region node/object. + * + ******************************************************************************/ + +static acpi_status +acpi_ds_create_external_region(acpi_status lookup_status, + union acpi_parse_object *op, + char *path, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **node) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + + if (lookup_status != AE_NOT_FOUND) { + return (lookup_status); + } + + /* + * Table disassembly: + * operation_region not found. Generate an External for it, and + * insert the name into the namespace. + */ + acpi_dm_add_op_to_external_list(op, path, ACPI_TYPE_REGION, 0, 0); + status = acpi_ns_lookup(walk_state->scope_info, path, ACPI_TYPE_REGION, + ACPI_IMODE_LOAD_PASS1, ACPI_NS_SEARCH_PARENT, + walk_state, node); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Must create and install a region object for the new node */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_REGION); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + obj_desc->region.node = *node; + status = acpi_ns_attach_object(*node, obj_desc, ACPI_TYPE_REGION); + return (status); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_buffer_field + * + * PARAMETERS: op - Current parse op (create_XXField) + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Execute the create_field operators: + * create_bit_field_op, + * create_byte_field_op, + * create_word_field_op, + * create_dword_field_op, + * create_qword_field_op, + * create_field_op (all of which define a field in a buffer) + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_buffer_field(union acpi_parse_object *op, + struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *arg; + struct acpi_namespace_node *node; + acpi_status status; + union acpi_operand_object *obj_desc; + union acpi_operand_object *second_desc = NULL; + u32 flags; + + ACPI_FUNCTION_TRACE(ds_create_buffer_field); + + /* + * Get the name_string argument (name of the new buffer_field) + */ + if (op->common.aml_opcode == AML_CREATE_FIELD_OP) { + + /* For create_field, name is the 4th argument */ + + arg = acpi_ps_get_arg(op, 3); + } else { + /* For all other create_XXXField operators, name is the 3rd argument */ + + arg = acpi_ps_get_arg(op, 2); + } + + if (!arg) { + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + if (walk_state->deferred_node) { + node = walk_state->deferred_node; + status = AE_OK; + } else { + /* Execute flag should always be set when this function is entered */ + + if (!(walk_state->parse_flags & ACPI_PARSE_EXECUTE)) { + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* Creating new namespace node, should not already exist */ + + flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | + ACPI_NS_ERROR_IF_FOUND; + + /* + * Mark node temporary if we are executing a normal control + * method. (Don't mark if this is a module-level code method) + */ + if (walk_state->method_node && + !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) { + flags |= ACPI_NS_TEMPORARY; + } + + /* Enter the name_string into the namespace */ + + status = + acpi_ns_lookup(walk_state->scope_info, + arg->common.value.string, ACPI_TYPE_ANY, + ACPI_IMODE_LOAD_PASS1, flags, walk_state, + &node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(arg->common.value.string, status); + return_ACPI_STATUS(status); + } + } + + /* + * We could put the returned object (Node) on the object stack for later, + * but for now, we will put it in the "op" object that the parser uses, + * so we can get it again at the end of this scope. + */ + op->common.node = node; + + /* + * If there is no object attached to the node, this node was just created + * and we need to create the field object. Otherwise, this was a lookup + * of an existing node and we don't want to create the field object again. + */ + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + return_ACPI_STATUS(AE_OK); + } + + /* + * The Field definition is not fully parsed at this time. + * (We must save the address of the AML for the buffer and index operands) + */ + + /* Create the buffer field object */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER_FIELD); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Remember location in AML stream of the field unit opcode and operands -- + * since the buffer and index operands must be evaluated. + */ + second_desc = obj_desc->common.next_object; + second_desc->extra.aml_start = op->named.data; + second_desc->extra.aml_length = op->named.length; + obj_desc->buffer_field.node = node; + + /* Attach constructed field descriptors to parent node */ + + status = acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_BUFFER_FIELD); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + +cleanup: + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_field_names + * + * PARAMETERS: info - create_field info structure + * ` walk_state - Current method state + * arg - First parser arg for the field name list + * + * RETURN: Status + * + * DESCRIPTION: Process all named fields in a field declaration. Names are + * entered into the namespace. + * + ******************************************************************************/ + +static acpi_status +acpi_ds_get_field_names(struct acpi_create_field_info *info, + struct acpi_walk_state *walk_state, + union acpi_parse_object *arg) +{ + acpi_status status; + u64 position; + union acpi_parse_object *child; + + ACPI_FUNCTION_TRACE_PTR(ds_get_field_names, info); + + /* First field starts at bit zero */ + + info->field_bit_position = 0; + + /* Process all elements in the field list (of parse nodes) */ + + while (arg) { + /* + * Four types of field elements are handled: + * 1) name - Enters a new named field into the namespace + * 2) offset - specifies a bit offset + * 3) access_as - changes the access mode/attributes + * 4) connection - Associate a resource template with the field + */ + switch (arg->common.aml_opcode) { + case AML_INT_RESERVEDFIELD_OP: + + position = (u64) info->field_bit_position + + (u64) arg->common.value.size; + + if (position > ACPI_UINT32_MAX) { + ACPI_ERROR((AE_INFO, + "Bit offset within field too large (> 0xFFFFFFFF)")); + return_ACPI_STATUS(AE_SUPPORT); + } + + info->field_bit_position = (u32) position; + break; + + case AML_INT_ACCESSFIELD_OP: + case AML_INT_EXTACCESSFIELD_OP: + /* + * Get new access_type, access_attribute, and access_length fields + * -- to be used for all field units that follow, until the + * end-of-field or another access_as keyword is encountered. + * NOTE. These three bytes are encoded in the integer value + * of the parseop for convenience. + * + * In field_flags, preserve the flag bits other than the + * ACCESS_TYPE bits. + */ + + /* access_type (byte_acc, word_acc, etc.) */ + + info->field_flags = (u8) + ((info-> + field_flags & ~(AML_FIELD_ACCESS_TYPE_MASK)) | + ((u8)((u32)(arg->common.value.integer & 0x07)))); + + /* access_attribute (attrib_quick, attrib_byte, etc.) */ + + info->attribute = + (u8)((arg->common.value.integer >> 8) & 0xFF); + + /* access_length (for serial/buffer protocols) */ + + info->access_length = + (u8)((arg->common.value.integer >> 16) & 0xFF); + break; + + case AML_INT_CONNECTION_OP: + /* + * Clear any previous connection. New connection is used for all + * fields that follow, similar to access_as + */ + info->resource_buffer = NULL; + info->connection_node = NULL; + info->pin_number_index = 0; + + /* + * A Connection() is either an actual resource descriptor (buffer) + * or a named reference to a resource template + */ + child = arg->common.value.arg; + if (child->common.aml_opcode == AML_INT_BYTELIST_OP) { + info->resource_buffer = child->named.data; + info->resource_length = + (u16)child->named.value.integer; + } else { + /* Lookup the Connection() namepath, it should already exist */ + + status = acpi_ns_lookup(walk_state->scope_info, + child->common.value. + name, ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, + ACPI_NS_DONT_OPEN_SCOPE, + walk_state, + &info->connection_node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(child->common. + value.name, + status); + return_ACPI_STATUS(status); + } + } + break; + + case AML_INT_NAMEDFIELD_OP: + + /* Lookup the name, it should already exist */ + + status = acpi_ns_lookup(walk_state->scope_info, + (char *)&arg->named.name, + info->field_type, + ACPI_IMODE_EXECUTE, + ACPI_NS_DONT_OPEN_SCOPE, + walk_state, &info->field_node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE((char *)&arg->named.name, + status); + return_ACPI_STATUS(status); + } else { + arg->common.node = info->field_node; + info->field_bit_length = arg->common.value.size; + + /* + * If there is no object attached to the node, this node was + * just created and we need to create the field object. + * Otherwise, this was a lookup of an existing node and we + * don't want to create the field object again. + */ + if (!acpi_ns_get_attached_object + (info->field_node)) { + status = acpi_ex_prep_field_value(info); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } + + /* Keep track of bit position for the next field */ + + position = (u64) info->field_bit_position + + (u64) arg->common.value.size; + + if (position > ACPI_UINT32_MAX) { + ACPI_ERROR((AE_INFO, + "Field [%4.4s] bit offset too large (> 0xFFFFFFFF)", + ACPI_CAST_PTR(char, + &info->field_node-> + name))); + return_ACPI_STATUS(AE_SUPPORT); + } + + info->field_bit_position += info->field_bit_length; + info->pin_number_index++; /* Index relative to previous Connection() */ + break; + + default: + + ACPI_ERROR((AE_INFO, + "Invalid opcode in field list: 0x%X", + arg->common.aml_opcode)); + return_ACPI_STATUS(AE_AML_BAD_OPCODE); + } + + arg = arg->common.next; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_field + * + * PARAMETERS: op - Op containing the Field definition and args + * region_node - Object for the containing Operation Region + * ` walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Create a new field in the specified operation region + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg; + struct acpi_create_field_info info; + + ACPI_FUNCTION_TRACE_PTR(ds_create_field, op); + + /* First arg is the name of the parent op_region (must already exist) */ + + arg = op->common.value.arg; + + if (!region_node) { + status = + acpi_ns_lookup(walk_state->scope_info, + arg->common.value.name, ACPI_TYPE_REGION, + ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, + walk_state, ®ion_node); +#ifdef ACPI_ASL_COMPILER + status = acpi_ds_create_external_region(status, arg, + arg->common.value.name, + walk_state, + ®ion_node); +#endif + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(arg->common.value.name, status); + return_ACPI_STATUS(status); + } + } + + ACPI_MEMSET(&info, 0, sizeof(struct acpi_create_field_info)); + + /* Second arg is the field flags */ + + arg = arg->common.next; + info.field_flags = (u8) arg->common.value.integer; + info.attribute = 0; + + /* Each remaining arg is a Named Field */ + + info.field_type = ACPI_TYPE_LOCAL_REGION_FIELD; + info.region_node = region_node; + + status = acpi_ds_get_field_names(&info, walk_state, arg->common.next); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_field_objects + * + * PARAMETERS: op - Op containing the Field definition and args + * ` walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: For each "Field Unit" name in the argument list that is + * part of the field declaration, enter the name into the + * namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ds_init_field_objects(union acpi_parse_object *op, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg = NULL; + struct acpi_namespace_node *node; + u8 type = 0; + u32 flags; + + ACPI_FUNCTION_TRACE_PTR(ds_init_field_objects, op); + + /* Execute flag should always be set when this function is entered */ + + if (!(walk_state->parse_flags & ACPI_PARSE_EXECUTE)) { + if (walk_state->parse_flags & ACPI_PARSE_DEFERRED_OP) { + + /* bank_field Op is deferred, just return OK */ + + return_ACPI_STATUS(AE_OK); + } + + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* + * Get the field_list argument for this opcode. This is the start of the + * list of field elements. + */ + switch (walk_state->opcode) { + case AML_FIELD_OP: + + arg = acpi_ps_get_arg(op, 2); + type = ACPI_TYPE_LOCAL_REGION_FIELD; + break; + + case AML_BANK_FIELD_OP: + + arg = acpi_ps_get_arg(op, 4); + type = ACPI_TYPE_LOCAL_BANK_FIELD; + break; + + case AML_INDEX_FIELD_OP: + + arg = acpi_ps_get_arg(op, 3); + type = ACPI_TYPE_LOCAL_INDEX_FIELD; + break; + + default: + + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Creating new namespace node(s), should not already exist */ + + flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | + ACPI_NS_ERROR_IF_FOUND; + + /* + * Mark node(s) temporary if we are executing a normal control + * method. (Don't mark if this is a module-level code method) + */ + if (walk_state->method_node && + !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) { + flags |= ACPI_NS_TEMPORARY; + } + + /* + * Walk the list of entries in the field_list + * Note: field_list can be of zero length. In this case, Arg will be NULL. + */ + while (arg) { + /* + * Ignore OFFSET/ACCESSAS/CONNECTION terms here; we are only interested + * in the field names in order to enter them into the namespace. + */ + if (arg->common.aml_opcode == AML_INT_NAMEDFIELD_OP) { + status = acpi_ns_lookup(walk_state->scope_info, + (char *)&arg->named.name, type, + ACPI_IMODE_LOAD_PASS1, flags, + walk_state, &node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE((char *)&arg->named.name, + status); + if (status != AE_ALREADY_EXISTS) { + return_ACPI_STATUS(status); + } + + /* Name already exists, just ignore this error */ + + status = AE_OK; + } + + arg->common.node = node; + } + + /* Get the next field element in the list */ + + arg = arg->common.next; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_bank_field + * + * PARAMETERS: op - Op containing the Field definition and args + * region_node - Object for the containing Operation Region + * walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Create a new bank field in the specified operation region + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_bank_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg; + struct acpi_create_field_info info; + + ACPI_FUNCTION_TRACE_PTR(ds_create_bank_field, op); + + /* First arg is the name of the parent op_region (must already exist) */ + + arg = op->common.value.arg; + if (!region_node) { + status = + acpi_ns_lookup(walk_state->scope_info, + arg->common.value.name, ACPI_TYPE_REGION, + ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, + walk_state, ®ion_node); +#ifdef ACPI_ASL_COMPILER + status = acpi_ds_create_external_region(status, arg, + arg->common.value.name, + walk_state, + ®ion_node); +#endif + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(arg->common.value.name, status); + return_ACPI_STATUS(status); + } + } + + /* Second arg is the Bank Register (Field) (must already exist) */ + + arg = arg->common.next; + status = + acpi_ns_lookup(walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, + &info.register_node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(arg->common.value.string, status); + return_ACPI_STATUS(status); + } + + /* + * Third arg is the bank_value + * This arg is a term_arg, not a constant + * It will be evaluated later, by acpi_ds_eval_bank_field_operands + */ + arg = arg->common.next; + + /* Fourth arg is the field flags */ + + arg = arg->common.next; + info.field_flags = (u8) arg->common.value.integer; + + /* Each remaining arg is a Named Field */ + + info.field_type = ACPI_TYPE_LOCAL_BANK_FIELD; + info.region_node = region_node; + + /* + * Use Info.data_register_node to store bank_field Op + * It's safe because data_register_node will never be used when create bank field + * We store aml_start and aml_length in the bank_field Op for late evaluation + * Used in acpi_ex_prep_field_value(Info) + * + * TBD: Or, should we add a field in struct acpi_create_field_info, like "void *ParentOp"? + */ + info.data_register_node = (struct acpi_namespace_node *)op; + + status = acpi_ds_get_field_names(&info, walk_state, arg->common.next); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_index_field + * + * PARAMETERS: op - Op containing the Field definition and args + * region_node - Object for the containing Operation Region + * ` walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Create a new index field in the specified operation region + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_index_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg; + struct acpi_create_field_info info; + + ACPI_FUNCTION_TRACE_PTR(ds_create_index_field, op); + + /* First arg is the name of the Index register (must already exist) */ + + arg = op->common.value.arg; + status = + acpi_ns_lookup(walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, + &info.register_node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(arg->common.value.string, status); + return_ACPI_STATUS(status); + } + + /* Second arg is the data register (must already exist) */ + + arg = arg->common.next; + status = + acpi_ns_lookup(walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, + &info.data_register_node); + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(arg->common.value.string, status); + return_ACPI_STATUS(status); + } + + /* Next arg is the field flags */ + + arg = arg->common.next; + info.field_flags = (u8) arg->common.value.integer; + + /* Each remaining arg is a Named Field */ + + info.field_type = ACPI_TYPE_LOCAL_INDEX_FIELD; + info.region_node = region_node; + + status = acpi_ds_get_field_names(&info, walk_state, arg->common.next); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dsinit.c b/kernel/drivers/acpi/acpica/dsinit.c new file mode 100644 index 000000000..bbe74bceb --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsinit.c @@ -0,0 +1,253 @@ +/****************************************************************************** + * + * Module Name: dsinit - Object initialization namespace walk + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "actables.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsinit") + +/* Local prototypes */ +static acpi_status +acpi_ds_init_one_object(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_one_object + * + * PARAMETERS: obj_handle - Node for the object + * level - Current nesting level + * context - Points to a init info struct + * return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every object + * within the namespace. + * + * Currently, the only objects that require initialization are: + * 1) Methods + * 2) Operation Regions + * + ******************************************************************************/ + +static acpi_status +acpi_ds_init_one_object(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + struct acpi_init_walk_info *info = + (struct acpi_init_walk_info *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + acpi_status status; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_ENTRY(); + + /* + * We are only interested in NS nodes owned by the table that + * was just loaded + */ + if (node->owner_id != info->owner_id) { + return (AE_OK); + } + + info->object_count++; + + /* And even then, we are only interested in a few object types */ + + switch (acpi_ns_get_type(obj_handle)) { + case ACPI_TYPE_REGION: + + status = acpi_ds_initialize_region(obj_handle); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During Region initialization %p [%4.4s]", + obj_handle, + acpi_ut_get_node_name(obj_handle))); + } + + info->op_region_count++; + break; + + case ACPI_TYPE_METHOD: + /* + * Auto-serialization support. We will examine each method that is + * not_serialized to determine if it creates any Named objects. If + * it does, it will be marked serialized to prevent problems if + * the method is entered by two or more threads and an attempt is + * made to create the same named object twice -- which results in + * an AE_ALREADY_EXISTS exception and method abort. + */ + info->method_count++; + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + break; + } + + /* Ignore if already serialized */ + + if (obj_desc->method.info_flags & ACPI_METHOD_SERIALIZED) { + info->serial_method_count++; + break; + } + + if (acpi_gbl_auto_serialize_methods) { + + /* Parse/scan method and serialize it if necessary */ + + acpi_ds_auto_serialize_method(node, obj_desc); + if (obj_desc->method. + info_flags & ACPI_METHOD_SERIALIZED) { + + /* Method was just converted to Serialized */ + + info->serial_method_count++; + info->serialized_method_count++; + break; + } + } + + info->non_serial_method_count++; + break; + + case ACPI_TYPE_DEVICE: + + info->device_count++; + break; + + default: + + break; + } + + /* + * We ignore errors from above, and always return OK, since + * we don't want to abort the walk on a single error. + */ + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_initialize_objects + * + * PARAMETERS: table_desc - Descriptor for parent ACPI table + * start_node - Root of subtree to be initialized. + * + * RETURN: Status + * + * DESCRIPTION: Walk the namespace starting at "StartNode" and perform any + * necessary initialization on the objects found therein + * + ******************************************************************************/ + +acpi_status +acpi_ds_initialize_objects(u32 table_index, + struct acpi_namespace_node * start_node) +{ + acpi_status status; + struct acpi_init_walk_info info; + struct acpi_table_header *table; + acpi_owner_id owner_id; + + ACPI_FUNCTION_TRACE(ds_initialize_objects); + + status = acpi_tb_get_owner_id(table_index, &owner_id); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "**** Starting initialization of namespace objects ****\n")); + + /* Set all init info to zero */ + + ACPI_MEMSET(&info, 0, sizeof(struct acpi_init_walk_info)); + + info.owner_id = owner_id; + info.table_index = table_index; + + /* Walk entire namespace from the supplied root */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * We don't use acpi_walk_namespace since we do not want to acquire + * the namespace reader lock. + */ + status = + acpi_ns_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, acpi_ds_init_one_object, + NULL, &info, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace")); + } + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + status = acpi_get_table_by_index(table_index, &table); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + "Table [%4.4s] (id %4.4X) - %4u Objects with %3u Devices, " + "%3u Regions, %3u Methods (%u/%u/%u Serial/Non/Cvt)\n", + table->signature, owner_id, info.object_count, + info.device_count, info.op_region_count, + info.method_count, info.serial_method_count, + info.non_serial_method_count, + info.serialized_method_count)); + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "%u Methods, %u Regions\n", + info.method_count, info.op_region_count)); + + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/dsmethod.c b/kernel/drivers/acpi/acpica/dsmethod.c new file mode 100644 index 000000000..d72565a3c --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsmethod.c @@ -0,0 +1,831 @@ +/****************************************************************************** + * + * Module Name: dsmethod - Parser/Interpreter interface - control method parsing + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#ifdef ACPI_DISASSEMBLER +#include "acdisasm.h" +#endif +#include "acparser.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsmethod") + +/* Local prototypes */ +static acpi_status +acpi_ds_detect_named_opcodes(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op); + +static acpi_status +acpi_ds_create_method_mutex(union acpi_operand_object *method_desc); + +/******************************************************************************* + * + * FUNCTION: acpi_ds_auto_serialize_method + * + * PARAMETERS: node - Namespace Node of the method + * obj_desc - Method object attached to node + * + * RETURN: Status + * + * DESCRIPTION: Parse a control method AML to scan for control methods that + * need serialization due to the creation of named objects. + * + * NOTE: It is a bit of overkill to mark all such methods serialized, since + * there is only a problem if the method actually blocks during execution. + * A blocking operation is, for example, a Sleep() operation, or any access + * to an operation region. However, it is probably not possible to easily + * detect whether a method will block or not, so we simply mark all suspicious + * methods as serialized. + * + * NOTE2: This code is essentially a generic routine for parsing a single + * control method. + * + ******************************************************************************/ + +acpi_status +acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, + union acpi_operand_object *obj_desc) +{ + acpi_status status; + union acpi_parse_object *op = NULL; + struct acpi_walk_state *walk_state; + + ACPI_FUNCTION_TRACE_PTR(ds_auto_serialize_method, node); + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Method auto-serialization parse [%4.4s] %p\n", + acpi_ut_get_node_name(node), node)); + + /* Create/Init a root op for the method parse tree */ + + op = acpi_ps_alloc_op(AML_METHOD_OP); + if (!op) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + acpi_ps_set_name(op, node->name.integer); + op->common.node = node; + + /* Create and initialize a new walk state */ + + walk_state = + acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL); + if (!walk_state) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + status = + acpi_ds_init_aml_walk(walk_state, op, node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, NULL, 0); + if (ACPI_FAILURE(status)) { + acpi_ds_delete_walk_state(walk_state); + return_ACPI_STATUS(status); + } + + walk_state->descending_callback = acpi_ds_detect_named_opcodes; + + /* Parse the method, scan for creation of named objects */ + + status = acpi_ps_parse_aml(walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_ps_delete_parse_tree(op); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_detect_named_opcodes + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * out_op - Unused, required for parser interface + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + * Currently used to detect methods that must be marked serialized + * in order to avoid problems with the creation of named objects. + * + ******************************************************************************/ + +static acpi_status +acpi_ds_detect_named_opcodes(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op) +{ + + ACPI_FUNCTION_NAME(acpi_ds_detect_named_opcodes); + + /* We are only interested in opcodes that create a new name */ + + if (! + (walk_state->op_info-> + flags & (AML_NAMED | AML_CREATE | AML_FIELD))) { + return (AE_OK); + } + + /* + * At this point, we know we have a Named object opcode. + * Mark the method as serialized. Later code will create a mutex for + * this method to enforce serialization. + * + * Note, ACPI_METHOD_IGNORE_SYNC_LEVEL flag means that we will ignore the + * Sync Level mechanism for this method, even though it is now serialized. + * Otherwise, there can be conflicts with existing ASL code that actually + * uses sync levels. + */ + walk_state->method_desc->method.sync_level = 0; + walk_state->method_desc->method.info_flags |= + (ACPI_METHOD_SERIALIZED | ACPI_METHOD_IGNORE_SYNC_LEVEL); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Method serialized [%4.4s] %p - [%s] (%4.4X)\n", + walk_state->method_node->name.ascii, + walk_state->method_node, walk_state->op_info->name, + walk_state->opcode)); + + /* Abort the parse, no need to examine this method any further */ + + return (AE_CTRL_TERMINATE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_error + * + * PARAMETERS: status - Execution status + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Called on method error. Invoke the global exception handler if + * present, dump the method data if the disassembler is configured + * + * Note: Allows the exception handler to change the status code + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_error(acpi_status status, struct acpi_walk_state * walk_state) +{ + ACPI_FUNCTION_ENTRY(); + + /* Ignore AE_OK and control exception codes */ + + if (ACPI_SUCCESS(status) || (status & AE_CODE_CONTROL)) { + return (status); + } + + /* Invoke the global exception handler */ + + if (acpi_gbl_exception_handler) { + + /* Exit the interpreter, allow handler to execute methods */ + + acpi_ex_exit_interpreter(); + + /* + * Handler can map the exception code to anything it wants, including + * AE_OK, in which case the executing method will not be aborted. + */ + status = acpi_gbl_exception_handler(status, + walk_state->method_node ? + walk_state->method_node-> + name.integer : 0, + walk_state->opcode, + walk_state->aml_offset, + NULL); + acpi_ex_enter_interpreter(); + } + + acpi_ds_clear_implicit_return(walk_state); + +#ifdef ACPI_DISASSEMBLER + if (ACPI_FAILURE(status)) { + + /* Display method locals/args if disassembler is present */ + + acpi_dm_dump_method_info(status, walk_state, walk_state->op); + } +#endif + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_method_mutex + * + * PARAMETERS: obj_desc - The method object + * + * RETURN: Status + * + * DESCRIPTION: Create a mutex object for a serialized control method + * + ******************************************************************************/ + +static acpi_status +acpi_ds_create_method_mutex(union acpi_operand_object *method_desc) +{ + union acpi_operand_object *mutex_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(ds_create_method_mutex); + + /* Create the new mutex object */ + + mutex_desc = acpi_ut_create_internal_object(ACPI_TYPE_MUTEX); + if (!mutex_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Create the actual OS Mutex */ + + status = acpi_os_create_mutex(&mutex_desc->mutex.os_mutex); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_object_desc(mutex_desc); + return_ACPI_STATUS(status); + } + + mutex_desc->mutex.sync_level = method_desc->method.sync_level; + method_desc->method.mutex = mutex_desc; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_begin_method_execution + * + * PARAMETERS: method_node - Node of the method + * obj_desc - The method object + * walk_state - current state, NULL if not yet executing + * a method. + * + * RETURN: Status + * + * DESCRIPTION: Prepare a method for execution. Parses the method if necessary, + * increments the thread count, and waits at the method semaphore + * for clearance to execute. + * + ******************************************************************************/ + +acpi_status +acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_PTR(ds_begin_method_execution, method_node); + + if (!method_node) { + return_ACPI_STATUS(AE_NULL_ENTRY); + } + + /* Prevent wraparound of thread count */ + + if (obj_desc->method.thread_count == ACPI_UINT8_MAX) { + ACPI_ERROR((AE_INFO, + "Method reached maximum reentrancy limit (255)")); + return_ACPI_STATUS(AE_AML_METHOD_LIMIT); + } + + /* + * If this method is serialized, we need to acquire the method mutex. + */ + if (obj_desc->method.info_flags & ACPI_METHOD_SERIALIZED) { + /* + * Create a mutex for the method if it is defined to be Serialized + * and a mutex has not already been created. We defer the mutex creation + * until a method is actually executed, to minimize the object count + */ + if (!obj_desc->method.mutex) { + status = acpi_ds_create_method_mutex(obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* + * The current_sync_level (per-thread) must be less than or equal to + * the sync level of the method. This mechanism provides some + * deadlock prevention. + * + * If the method was auto-serialized, we just ignore the sync level + * mechanism, because auto-serialization of methods can interfere + * with ASL code that actually uses sync levels. + * + * Top-level method invocation has no walk state at this point + */ + if (walk_state && + (!(obj_desc->method. + info_flags & ACPI_METHOD_IGNORE_SYNC_LEVEL)) + && (walk_state->thread->current_sync_level > + obj_desc->method.mutex->mutex.sync_level)) { + ACPI_ERROR((AE_INFO, + "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%u)", + acpi_ut_get_node_name(method_node), + walk_state->thread->current_sync_level)); + + return_ACPI_STATUS(AE_AML_MUTEX_ORDER); + } + + /* + * Obtain the method mutex if necessary. Do not acquire mutex for a + * recursive call. + */ + if (!walk_state || + !obj_desc->method.mutex->mutex.thread_id || + (walk_state->thread->thread_id != + obj_desc->method.mutex->mutex.thread_id)) { + /* + * Acquire the method mutex. This releases the interpreter if we + * block (and reacquires it before it returns) + */ + status = + acpi_ex_system_wait_mutex(obj_desc->method.mutex-> + mutex.os_mutex, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Update the mutex and walk info and save the original sync_level */ + + if (walk_state) { + obj_desc->method.mutex->mutex. + original_sync_level = + walk_state->thread->current_sync_level; + + obj_desc->method.mutex->mutex.thread_id = + walk_state->thread->thread_id; + walk_state->thread->current_sync_level = + obj_desc->method.sync_level; + } else { + obj_desc->method.mutex->mutex. + original_sync_level = + obj_desc->method.mutex->mutex.sync_level; + } + } + + /* Always increase acquisition depth */ + + obj_desc->method.mutex->mutex.acquisition_depth++; + } + + /* + * Allocate an Owner ID for this method, only if this is the first thread + * to begin concurrent execution. We only need one owner_id, even if the + * method is invoked recursively. + */ + if (!obj_desc->method.owner_id) { + status = acpi_ut_allocate_owner_id(&obj_desc->method.owner_id); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + } + + /* + * Increment the method parse tree thread count since it has been + * reentered one more time (even if it is the same thread) + */ + obj_desc->method.thread_count++; + acpi_method_count++; + return_ACPI_STATUS(status); + +cleanup: + /* On error, must release the method mutex (if present) */ + + if (obj_desc->method.mutex) { + acpi_os_release_mutex(obj_desc->method.mutex->mutex.os_mutex); + } + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_call_control_method + * + * PARAMETERS: thread - Info for this thread + * this_walk_state - Current walk state + * op - Current Op to be walked + * + * RETURN: Status + * + * DESCRIPTION: Transfer execution to a called control method + * + ******************************************************************************/ + +acpi_status +acpi_ds_call_control_method(struct acpi_thread_state *thread, + struct acpi_walk_state *this_walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + struct acpi_namespace_node *method_node; + struct acpi_walk_state *next_walk_state = NULL; + union acpi_operand_object *obj_desc; + struct acpi_evaluate_info *info; + u32 i; + + ACPI_FUNCTION_TRACE_PTR(ds_call_control_method, this_walk_state); + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Calling method %p, currentstate=%p\n", + this_walk_state->prev_op, this_walk_state)); + + /* + * Get the namespace entry for the control method we are about to call + */ + method_node = this_walk_state->method_call_node; + if (!method_node) { + return_ACPI_STATUS(AE_NULL_ENTRY); + } + + obj_desc = acpi_ns_get_attached_object(method_node); + if (!obj_desc) { + return_ACPI_STATUS(AE_NULL_OBJECT); + } + + /* Init for new method, possibly wait on method mutex */ + + status = acpi_ds_begin_method_execution(method_node, obj_desc, + this_walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Begin method parse/execution. Create a new walk state */ + + next_walk_state = acpi_ds_create_walk_state(obj_desc->method.owner_id, + NULL, obj_desc, thread); + if (!next_walk_state) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * The resolved arguments were put on the previous walk state's operand + * stack. Operands on the previous walk state stack always + * start at index 0. Also, null terminate the list of arguments + */ + this_walk_state->operands[this_walk_state->num_operands] = NULL; + + /* + * Allocate and initialize the evaluation information block + * TBD: this is somewhat inefficient, should change interface to + * ds_init_aml_walk. For now, keeps this struct off the CPU stack + */ + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + status = AE_NO_MEMORY; + goto cleanup; + } + + info->parameters = &this_walk_state->operands[0]; + + status = acpi_ds_init_aml_walk(next_walk_state, NULL, method_node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, info, + ACPI_IMODE_EXECUTE); + + ACPI_FREE(info); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * Delete the operands on the previous walkstate operand stack + * (they were copied to new objects) + */ + for (i = 0; i < obj_desc->method.param_count; i++) { + acpi_ut_remove_reference(this_walk_state->operands[i]); + this_walk_state->operands[i] = NULL; + } + + /* Clear the operand stack */ + + this_walk_state->num_operands = 0; + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "**** Begin nested execution of [%4.4s] **** WalkState=%p\n", + method_node->name.ascii, next_walk_state)); + + /* Invoke an internal method if necessary */ + + if (obj_desc->method.info_flags & ACPI_METHOD_INTERNAL_ONLY) { + status = + obj_desc->method.dispatch.implementation(next_walk_state); + if (status == AE_OK) { + status = AE_CTRL_TERMINATE; + } + } + + return_ACPI_STATUS(status); + +cleanup: + + /* On error, we must terminate the method properly */ + + acpi_ds_terminate_control_method(obj_desc, next_walk_state); + if (next_walk_state) { + acpi_ds_delete_walk_state(next_walk_state); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_restart_control_method + * + * PARAMETERS: walk_state - State for preempted method (caller) + * return_desc - Return value from the called method + * + * RETURN: Status + * + * DESCRIPTION: Restart a method that was preempted by another (nested) method + * invocation. Handle the return value (if any) from the callee. + * + ******************************************************************************/ + +acpi_status +acpi_ds_restart_control_method(struct acpi_walk_state *walk_state, + union acpi_operand_object *return_desc) +{ + acpi_status status; + int same_as_implicit_return; + + ACPI_FUNCTION_TRACE_PTR(ds_restart_control_method, walk_state); + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "****Restart [%4.4s] Op %p ReturnValueFromCallee %p\n", + acpi_ut_get_node_name(walk_state->method_node), + walk_state->method_call_op, return_desc)); + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + " ReturnFromThisMethodUsed?=%X ResStack %p Walk %p\n", + walk_state->return_used, + walk_state->results, walk_state)); + + /* Did the called method return a value? */ + + if (return_desc) { + + /* Is the implicit return object the same as the return desc? */ + + same_as_implicit_return = + (walk_state->implicit_return_obj == return_desc); + + /* Are we actually going to use the return value? */ + + if (walk_state->return_used) { + + /* Save the return value from the previous method */ + + status = acpi_ds_result_push(return_desc, walk_state); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + return_ACPI_STATUS(status); + } + + /* + * Save as THIS method's return value in case it is returned + * immediately to yet another method + */ + walk_state->return_desc = return_desc; + } + + /* + * The following code is the optional support for the so-called + * "implicit return". Some AML code assumes that the last value of the + * method is "implicitly" returned to the caller, in the absence of an + * explicit return value. + * + * Just save the last result of the method as the return value. + * + * NOTE: this is optional because the ASL language does not actually + * support this behavior. + */ + else if (!acpi_ds_do_implicit_return + (return_desc, walk_state, FALSE) + || same_as_implicit_return) { + /* + * Delete the return value if it will not be used by the + * calling method or remove one reference if the explicit return + * is the same as the implicit return value. + */ + acpi_ut_remove_reference(return_desc); + } + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_terminate_control_method + * + * PARAMETERS: method_desc - Method object + * walk_state - State associated with the method + * + * RETURN: None + * + * DESCRIPTION: Terminate a control method. Delete everything that the method + * created, delete all locals and arguments, and delete the parse + * tree if requested. + * + * MUTEX: Interpreter is locked + * + ******************************************************************************/ + +void +acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, + struct acpi_walk_state *walk_state) +{ + + ACPI_FUNCTION_TRACE_PTR(ds_terminate_control_method, walk_state); + + /* method_desc is required, walk_state is optional */ + + if (!method_desc) { + return_VOID; + } + + if (walk_state) { + + /* Delete all arguments and locals */ + + acpi_ds_method_data_delete_all(walk_state); + + /* + * If method is serialized, release the mutex and restore the + * current sync level for this thread + */ + if (method_desc->method.mutex) { + + /* Acquisition Depth handles recursive calls */ + + method_desc->method.mutex->mutex.acquisition_depth--; + if (!method_desc->method.mutex->mutex.acquisition_depth) { + walk_state->thread->current_sync_level = + method_desc->method.mutex->mutex. + original_sync_level; + + acpi_os_release_mutex(method_desc->method. + mutex->mutex.os_mutex); + method_desc->method.mutex->mutex.thread_id = 0; + } + } + + /* + * Delete any namespace objects created anywhere within the + * namespace by the execution of this method. Unless: + * 1) This method is a module-level executable code method, in which + * case we want make the objects permanent. + * 2) There are other threads executing the method, in which case we + * will wait until the last thread has completed. + */ + if (!(method_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) + && (method_desc->method.thread_count == 1)) { + + /* Delete any direct children of (created by) this method */ + + acpi_ns_delete_namespace_subtree(walk_state-> + method_node); + + /* + * Delete any objects that were created by this method + * elsewhere in the namespace (if any were created). + * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the + * deletion such that we don't have to perform an entire + * namespace walk for every control method execution. + */ + if (method_desc->method. + info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) { + acpi_ns_delete_namespace_by_owner(method_desc-> + method. + owner_id); + method_desc->method.info_flags &= + ~ACPI_METHOD_MODIFIED_NAMESPACE; + } + } + } + + /* Decrement the thread count on the method */ + + if (method_desc->method.thread_count) { + method_desc->method.thread_count--; + } else { + ACPI_ERROR((AE_INFO, "Invalid zero thread count in method")); + } + + /* Are there any other threads currently executing this method? */ + + if (method_desc->method.thread_count) { + /* + * Additional threads. Do not release the owner_id in this case, + * we immediately reuse it for the next thread executing this method + */ + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "*** Completed execution of one thread, %u threads remaining\n", + method_desc->method.thread_count)); + } else { + /* This is the only executing thread for this method */ + + /* + * Support to dynamically change a method from not_serialized to + * Serialized if it appears that the method is incorrectly written and + * does not support multiple thread execution. The best example of this + * is if such a method creates namespace objects and blocks. A second + * thread will fail with an AE_ALREADY_EXISTS exception. + * + * This code is here because we must wait until the last thread exits + * before marking the method as serialized. + */ + if (method_desc->method. + info_flags & ACPI_METHOD_SERIALIZED_PENDING) { + if (walk_state) { + ACPI_INFO((AE_INFO, + "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error", + walk_state->method_node->name. + ascii)); + } + + /* + * Method tried to create an object twice and was marked as + * "pending serialized". The probable cause is that the method + * cannot handle reentrancy. + * + * The method was created as not_serialized, but it tried to create + * a named object and then blocked, causing the second thread + * entrance to begin and then fail. Workaround this problem by + * marking the method permanently as Serialized when the last + * thread exits here. + */ + method_desc->method.info_flags &= + ~ACPI_METHOD_SERIALIZED_PENDING; + method_desc->method.info_flags |= + (ACPI_METHOD_SERIALIZED | + ACPI_METHOD_IGNORE_SYNC_LEVEL); + method_desc->method.sync_level = 0; + } + + /* No more threads, we can free the owner_id */ + + if (! + (method_desc->method. + info_flags & ACPI_METHOD_MODULE_LEVEL)) { + acpi_ut_release_owner_id(&method_desc->method.owner_id); + } + } + + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/dsmthdat.c b/kernel/drivers/acpi/acpica/dsmthdat.c new file mode 100644 index 000000000..2e4c42b37 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsmthdat.c @@ -0,0 +1,714 @@ +/******************************************************************************* + * + * Module Name: dsmthdat - control method arguments and local variables + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsmthdat") + +/* Local prototypes */ +static void +acpi_ds_method_data_delete_value(u8 type, + u32 index, struct acpi_walk_state *walk_state); + +static acpi_status +acpi_ds_method_data_set_value(u8 type, + u32 index, + union acpi_operand_object *object, + struct acpi_walk_state *walk_state); + +#ifdef ACPI_OBSOLETE_FUNCTIONS +acpi_object_type +acpi_ds_method_data_get_type(u16 opcode, + u32 index, struct acpi_walk_state *walk_state); +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_init + * + * PARAMETERS: walk_state - Current walk state object + * + * RETURN: Status + * + * DESCRIPTION: Initialize the data structures that hold the method's arguments + * and locals. The data struct is an array of namespace nodes for + * each - this allows ref_of and de_ref_of to work properly for these + * special data types. + * + * NOTES: walk_state fields are initialized to zero by the + * ACPI_ALLOCATE_ZEROED(). + * + * A pseudo-Namespace Node is assigned to each argument and local + * so that ref_of() can return a pointer to the Node. + * + ******************************************************************************/ + +void acpi_ds_method_data_init(struct acpi_walk_state *walk_state) +{ + u32 i; + + ACPI_FUNCTION_TRACE(ds_method_data_init); + + /* Init the method arguments */ + + for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) { + ACPI_MOVE_32_TO_32(&walk_state->arguments[i].name, + NAMEOF_ARG_NTE); + walk_state->arguments[i].name.integer |= (i << 24); + walk_state->arguments[i].descriptor_type = ACPI_DESC_TYPE_NAMED; + walk_state->arguments[i].type = ACPI_TYPE_ANY; + walk_state->arguments[i].flags = ANOBJ_METHOD_ARG; + } + + /* Init the method locals */ + + for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) { + ACPI_MOVE_32_TO_32(&walk_state->local_variables[i].name, + NAMEOF_LOCAL_NTE); + + walk_state->local_variables[i].name.integer |= (i << 24); + walk_state->local_variables[i].descriptor_type = + ACPI_DESC_TYPE_NAMED; + walk_state->local_variables[i].type = ACPI_TYPE_ANY; + walk_state->local_variables[i].flags = ANOBJ_METHOD_LOCAL; + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_delete_all + * + * PARAMETERS: walk_state - Current walk state object + * + * RETURN: None + * + * DESCRIPTION: Delete method locals and arguments. Arguments are only + * deleted if this method was called from another method. + * + ******************************************************************************/ + +void acpi_ds_method_data_delete_all(struct acpi_walk_state *walk_state) +{ + u32 index; + + ACPI_FUNCTION_TRACE(ds_method_data_delete_all); + + /* Detach the locals */ + + for (index = 0; index < ACPI_METHOD_NUM_LOCALS; index++) { + if (walk_state->local_variables[index].object) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Deleting Local%u=%p\n", + index, + walk_state->local_variables[index]. + object)); + + /* Detach object (if present) and remove a reference */ + + acpi_ns_detach_object(&walk_state-> + local_variables[index]); + } + } + + /* Detach the arguments */ + + for (index = 0; index < ACPI_METHOD_NUM_ARGS; index++) { + if (walk_state->arguments[index].object) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Deleting Arg%u=%p\n", + index, + walk_state->arguments[index].object)); + + /* Detach object (if present) and remove a reference */ + + acpi_ns_detach_object(&walk_state->arguments[index]); + } + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_init_args + * + * PARAMETERS: *params - Pointer to a parameter list for the method + * max_param_count - The arg count for this method + * walk_state - Current walk state object + * + * RETURN: Status + * + * DESCRIPTION: Initialize arguments for a method. The parameter list is a list + * of ACPI operand objects, either null terminated or whose length + * is defined by max_param_count. + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_data_init_args(union acpi_operand_object **params, + u32 max_param_count, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + u32 index = 0; + + ACPI_FUNCTION_TRACE_PTR(ds_method_data_init_args, params); + + if (!params) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "No param list passed to method\n")); + return_ACPI_STATUS(AE_OK); + } + + /* Copy passed parameters into the new method stack frame */ + + while ((index < ACPI_METHOD_NUM_ARGS) && + (index < max_param_count) && params[index]) { + /* + * A valid parameter. + * Store the argument in the method/walk descriptor. + * Do not copy the arg in order to implement call by reference + */ + status = acpi_ds_method_data_set_value(ACPI_REFCLASS_ARG, index, + params[index], + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + index++; + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%u args passed to method\n", index)); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_get_node + * + * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or + * ACPI_REFCLASS_ARG + * index - Which Local or Arg whose type to get + * walk_state - Current walk state object + * node - Where the node is returned. + * + * RETURN: Status and node + * + * DESCRIPTION: Get the Node associated with a local or arg. + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_data_get_node(u8 type, + u32 index, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **node) +{ + ACPI_FUNCTION_TRACE(ds_method_data_get_node); + + /* + * Method Locals and Arguments are supported + */ + switch (type) { + case ACPI_REFCLASS_LOCAL: + + if (index > ACPI_METHOD_MAX_LOCAL) { + ACPI_ERROR((AE_INFO, + "Local index %u is invalid (max %u)", + index, ACPI_METHOD_MAX_LOCAL)); + return_ACPI_STATUS(AE_AML_INVALID_INDEX); + } + + /* Return a pointer to the pseudo-node */ + + *node = &walk_state->local_variables[index]; + break; + + case ACPI_REFCLASS_ARG: + + if (index > ACPI_METHOD_MAX_ARG) { + ACPI_ERROR((AE_INFO, + "Arg index %u is invalid (max %u)", + index, ACPI_METHOD_MAX_ARG)); + return_ACPI_STATUS(AE_AML_INVALID_INDEX); + } + + /* Return a pointer to the pseudo-node */ + + *node = &walk_state->arguments[index]; + break; + + default: + + ACPI_ERROR((AE_INFO, "Type %u is invalid", type)); + return_ACPI_STATUS(AE_TYPE); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_set_value + * + * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or + * ACPI_REFCLASS_ARG + * index - Which Local or Arg to get + * object - Object to be inserted into the stack entry + * walk_state - Current walk state object + * + * RETURN: Status + * + * DESCRIPTION: Insert an object onto the method stack at entry Opcode:Index. + * Note: There is no "implicit conversion" for locals. + * + ******************************************************************************/ + +static acpi_status +acpi_ds_method_data_set_value(u8 type, + u32 index, + union acpi_operand_object *object, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(ds_method_data_set_value); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "NewObj %p Type %2.2X, Refs=%u [%s]\n", object, + type, object->common.reference_count, + acpi_ut_get_type_name(object->common.type))); + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node(type, index, walk_state, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Increment ref count so object can't be deleted while installed. + * NOTE: We do not copy the object in order to preserve the call by + * reference semantics of ACPI Control Method invocation. + * (See ACPI Specification 2.0C) + */ + acpi_ut_add_reference(object); + + /* Install the object */ + + node->object = object; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_get_value + * + * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or + * ACPI_REFCLASS_ARG + * index - Which localVar or argument to get + * walk_state - Current walk state object + * dest_desc - Where Arg or Local value is returned + * + * RETURN: Status + * + * DESCRIPTION: Retrieve value of selected Arg or Local for this method + * Used only in acpi_ex_resolve_to_value(). + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_data_get_value(u8 type, + u32 index, + struct acpi_walk_state *walk_state, + union acpi_operand_object **dest_desc) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *object; + + ACPI_FUNCTION_TRACE(ds_method_data_get_value); + + /* Validate the object descriptor */ + + if (!dest_desc) { + ACPI_ERROR((AE_INFO, "Null object descriptor pointer")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node(type, index, walk_state, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the object from the node */ + + object = node->object; + + /* Examine the returned object, it must be valid. */ + + if (!object) { + /* + * Index points to uninitialized object. + * This means that either 1) The expected argument was + * not passed to the method, or 2) A local variable + * was referenced by the method (via the ASL) + * before it was initialized. Either case is an error. + */ + + /* If slack enabled, init the local_x/arg_x to an Integer of value zero */ + + if (acpi_gbl_enable_interpreter_slack) { + object = acpi_ut_create_integer_object((u64) 0); + if (!object) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + node->object = object; + } + + /* Otherwise, return the error */ + + else + switch (type) { + case ACPI_REFCLASS_ARG: + + ACPI_ERROR((AE_INFO, + "Uninitialized Arg[%u] at node %p", + index, node)); + + return_ACPI_STATUS(AE_AML_UNINITIALIZED_ARG); + + case ACPI_REFCLASS_LOCAL: + /* + * No error message for this case, will be trapped again later to + * detect and ignore cases of Store(local_x,local_x) + */ + return_ACPI_STATUS(AE_AML_UNINITIALIZED_LOCAL); + + default: + + ACPI_ERROR((AE_INFO, + "Not a Arg/Local opcode: 0x%X", + type)); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + } + + /* + * The Index points to an initialized and valid object. + * Return an additional reference to the object + */ + *dest_desc = object; + acpi_ut_add_reference(object); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_delete_value + * + * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or + * ACPI_REFCLASS_ARG + * index - Which localVar or argument to delete + * walk_state - Current walk state object + * + * RETURN: None + * + * DESCRIPTION: Delete the entry at Opcode:Index. Inserts + * a null into the stack slot after the object is deleted. + * + ******************************************************************************/ + +static void +acpi_ds_method_data_delete_value(u8 type, + u32 index, struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *object; + + ACPI_FUNCTION_TRACE(ds_method_data_delete_value); + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node(type, index, walk_state, &node); + if (ACPI_FAILURE(status)) { + return_VOID; + } + + /* Get the associated object */ + + object = acpi_ns_get_attached_object(node); + + /* + * Undefine the Arg or Local by setting its descriptor + * pointer to NULL. Locals/Args can contain both + * ACPI_OPERAND_OBJECTS and ACPI_NAMESPACE_NODEs + */ + node->object = NULL; + + if ((object) && + (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_OPERAND)) { + /* + * There is a valid object. + * Decrement the reference count by one to balance the + * increment when the object was stored. + */ + acpi_ut_remove_reference(object); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_store_object_to_local + * + * PARAMETERS: type - Either ACPI_REFCLASS_LOCAL or + * ACPI_REFCLASS_ARG + * index - Which Local or Arg to set + * obj_desc - Value to be stored + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Store a value in an Arg or Local. The obj_desc is installed + * as the new value for the Arg or Local and the reference count + * for obj_desc is incremented. + * + ******************************************************************************/ + +acpi_status +acpi_ds_store_object_to_local(u8 type, + u32 index, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *current_obj_desc; + union acpi_operand_object *new_obj_desc; + + ACPI_FUNCTION_TRACE(ds_store_object_to_local); + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Type=%2.2X Index=%u Obj=%p\n", + type, index, obj_desc)); + + /* Parameter validation */ + + if (!obj_desc) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node(type, index, walk_state, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + current_obj_desc = acpi_ns_get_attached_object(node); + if (current_obj_desc == obj_desc) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Obj=%p already installed!\n", + obj_desc)); + return_ACPI_STATUS(status); + } + + /* + * If the reference count on the object is more than one, we must + * take a copy of the object before we store. A reference count + * of exactly 1 means that the object was just created during the + * evaluation of an expression, and we can safely use it since it + * is not used anywhere else. + */ + new_obj_desc = obj_desc; + if (obj_desc->common.reference_count > 1) { + status = + acpi_ut_copy_iobject_to_iobject(obj_desc, &new_obj_desc, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* + * If there is an object already in this slot, we either + * have to delete it, or if this is an argument and there + * is an object reference stored there, we have to do + * an indirect store! + */ + if (current_obj_desc) { + /* + * Check for an indirect store if an argument + * contains an object reference (stored as an Node). + * We don't allow this automatic dereferencing for + * locals, since a store to a local should overwrite + * anything there, including an object reference. + * + * If both Arg0 and Local0 contain ref_of (Local4): + * + * Store (1, Arg0) - Causes indirect store to local4 + * Store (1, Local0) - Stores 1 in local0, overwriting + * the reference to local4 + * Store (1, de_refof (Local0)) - Causes indirect store to local4 + * + * Weird, but true. + */ + if (type == ACPI_REFCLASS_ARG) { + /* + * If we have a valid reference object that came from ref_of(), + * do the indirect store + */ + if ((ACPI_GET_DESCRIPTOR_TYPE(current_obj_desc) == + ACPI_DESC_TYPE_OPERAND) + && (current_obj_desc->common.type == + ACPI_TYPE_LOCAL_REFERENCE) + && (current_obj_desc->reference.class == + ACPI_REFCLASS_REFOF)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Arg (%p) is an ObjRef(Node), storing in node %p\n", + new_obj_desc, + current_obj_desc)); + + /* + * Store this object to the Node (perform the indirect store) + * NOTE: No implicit conversion is performed, as per the ACPI + * specification rules on storing to Locals/Args. + */ + status = + acpi_ex_store_object_to_node(new_obj_desc, + current_obj_desc-> + reference. + object, + walk_state, + ACPI_NO_IMPLICIT_CONVERSION); + + /* Remove local reference if we copied the object above */ + + if (new_obj_desc != obj_desc) { + acpi_ut_remove_reference(new_obj_desc); + } + return_ACPI_STATUS(status); + } + } + + /* Delete the existing object before storing the new one */ + + acpi_ds_method_data_delete_value(type, index, walk_state); + } + + /* + * Install the Obj descriptor (*new_obj_desc) into + * the descriptor for the Arg or Local. + * (increments the object reference count by one) + */ + status = + acpi_ds_method_data_set_value(type, index, new_obj_desc, + walk_state); + + /* Remove local reference if we copied the object above */ + + if (new_obj_desc != obj_desc) { + acpi_ut_remove_reference(new_obj_desc); + } + + return_ACPI_STATUS(status); +} + +#ifdef ACPI_OBSOLETE_FUNCTIONS +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_get_type + * + * PARAMETERS: opcode - Either AML_LOCAL_OP or AML_ARG_OP + * index - Which Local or Arg whose type to get + * walk_state - Current walk state object + * + * RETURN: Data type of current value of the selected Arg or Local + * + * DESCRIPTION: Get the type of the object stored in the Local or Arg + * + ******************************************************************************/ + +acpi_object_type +acpi_ds_method_data_get_type(u16 opcode, + u32 index, struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *object; + + ACPI_FUNCTION_TRACE(ds_method_data_get_type); + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node(opcode, index, walk_state, &node); + if (ACPI_FAILURE(status)) { + return_VALUE((ACPI_TYPE_NOT_FOUND)); + } + + /* Get the object */ + + object = acpi_ns_get_attached_object(node); + if (!object) { + + /* Uninitialized local/arg, return TYPE_ANY */ + + return_VALUE(ACPI_TYPE_ANY); + } + + /* Get the object type */ + + return_VALUE(object->type); +} +#endif diff --git a/kernel/drivers/acpi/acpica/dsobject.c b/kernel/drivers/acpi/acpica/dsobject.c new file mode 100644 index 000000000..8a7b07b6a --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsobject.c @@ -0,0 +1,850 @@ +/****************************************************************************** + * + * Module Name: dsobject - Dispatcher object management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsobject") + +/* Local prototypes */ +static acpi_status +acpi_ds_build_internal_object(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + union acpi_operand_object **obj_desc_ptr); + +#ifndef ACPI_NO_METHOD_EXECUTION +/******************************************************************************* + * + * FUNCTION: acpi_ds_build_internal_object + * + * PARAMETERS: walk_state - Current walk state + * op - Parser object to be translated + * obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op object to the equivalent namespace object + * Simple objects are any objects other than a package object! + * + ******************************************************************************/ + +static acpi_status +acpi_ds_build_internal_object(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + acpi_object_type type; + + ACPI_FUNCTION_TRACE(ds_build_internal_object); + + *obj_desc_ptr = NULL; + if (op->common.aml_opcode == AML_INT_NAMEPATH_OP) { + /* + * This is a named object reference. If this name was + * previously looked up in the namespace, it was stored in this op. + * Otherwise, go ahead and look it up now + */ + if (!op->common.node) { + status = acpi_ns_lookup(walk_state->scope_info, + op->common.value.string, + ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT | + ACPI_NS_DONT_OPEN_SCOPE, NULL, + ACPI_CAST_INDIRECT_PTR(struct + acpi_namespace_node, + &(op-> + common. + node))); + if (ACPI_FAILURE(status)) { + + /* Check if we are resolving a named reference within a package */ + + if ((status == AE_NOT_FOUND) + && (acpi_gbl_enable_interpreter_slack) + && + ((op->common.parent->common.aml_opcode == + AML_PACKAGE_OP) + || (op->common.parent->common.aml_opcode == + AML_VAR_PACKAGE_OP))) { + /* + * We didn't find the target and we are populating elements + * of a package - ignore if slack enabled. Some ASL code + * contains dangling invalid references in packages and + * expects that no exception will be issued. Leave the + * element as a null element. It cannot be used, but it + * can be overwritten by subsequent ASL code - this is + * typically the case. + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Ignoring unresolved reference in package [%4.4s]\n", + walk_state-> + scope_info->scope. + node->name.ascii)); + + return_ACPI_STATUS(AE_OK); + } else { + ACPI_ERROR_NAMESPACE(op->common.value. + string, status); + } + + return_ACPI_STATUS(status); + } + } + + /* Special object resolution for elements of a package */ + + if ((op->common.parent->common.aml_opcode == AML_PACKAGE_OP) || + (op->common.parent->common.aml_opcode == + AML_VAR_PACKAGE_OP)) { + /* + * Attempt to resolve the node to a value before we insert it into + * the package. If this is a reference to a common data type, + * resolve it immediately. According to the ACPI spec, package + * elements can only be "data objects" or method references. + * Attempt to resolve to an Integer, Buffer, String or Package. + * If cannot, return the named reference (for things like Devices, + * Methods, etc.) Buffer Fields and Fields will resolve to simple + * objects (int/buf/str/pkg). + * + * NOTE: References to things like Devices, Methods, Mutexes, etc. + * will remain as named references. This behavior is not described + * in the ACPI spec, but it appears to be an oversight. + */ + obj_desc = + ACPI_CAST_PTR(union acpi_operand_object, + op->common.node); + + status = + acpi_ex_resolve_node_to_value(ACPI_CAST_INDIRECT_PTR + (struct + acpi_namespace_node, + &obj_desc), + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Special handling for Alias objects. We need to setup the type + * and the Op->Common.Node to point to the Alias target. Note, + * Alias has at most one level of indirection internally. + */ + type = op->common.node->type; + if (type == ACPI_TYPE_LOCAL_ALIAS) { + type = obj_desc->common.type; + op->common.node = + ACPI_CAST_PTR(struct acpi_namespace_node, + op->common.node->object); + } + + switch (type) { + /* + * For these types, we need the actual node, not the subobject. + * However, the subobject did not get an extra reference count above. + * + * TBD: should ex_resolve_node_to_value be changed to fix this? + */ + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_THERMAL: + + acpi_ut_add_reference(op->common.node->object); + + /*lint -fallthrough */ + /* + * For these types, we need the actual node, not the subobject. + * The subobject got an extra reference count in ex_resolve_node_to_value. + */ + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_METHOD: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_REGION: + + /* We will create a reference object for these types below */ + break; + + default: + /* + * All other types - the node was resolved to an actual + * object, we are done. + */ + goto exit; + } + } + } + + /* Create and init a new internal ACPI object */ + + obj_desc = acpi_ut_create_internal_object((acpi_ps_get_opcode_info + (op->common.aml_opcode))-> + object_type); + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + status = + acpi_ds_init_object_from_op(walk_state, op, op->common.aml_opcode, + &obj_desc); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); + } + +exit: + *obj_desc_ptr = obj_desc; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_build_internal_buffer_obj + * + * PARAMETERS: walk_state - Current walk state + * op - Parser object to be translated + * buffer_length - Length of the buffer + * obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op package object to the equivalent + * namespace object + * + ******************************************************************************/ + +acpi_status +acpi_ds_build_internal_buffer_obj(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 buffer_length, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_parse_object *arg; + union acpi_operand_object *obj_desc; + union acpi_parse_object *byte_list; + u32 byte_list_length = 0; + + ACPI_FUNCTION_TRACE(ds_build_internal_buffer_obj); + + /* + * If we are evaluating a Named buffer object "Name (xxxx, Buffer)". + * The buffer object already exists (from the NS node), otherwise it must + * be created. + */ + obj_desc = *obj_desc_ptr; + if (!obj_desc) { + + /* Create a new buffer object */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER); + *obj_desc_ptr = obj_desc; + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + } + + /* + * Second arg is the buffer data (optional) byte_list can be either + * individual bytes or a string initializer. In either case, a + * byte_list appears in the AML. + */ + arg = op->common.value.arg; /* skip first arg */ + + byte_list = arg->named.next; + if (byte_list) { + if (byte_list->common.aml_opcode != AML_INT_BYTELIST_OP) { + ACPI_ERROR((AE_INFO, + "Expecting bytelist, found AML opcode 0x%X in op %p", + byte_list->common.aml_opcode, byte_list)); + + acpi_ut_remove_reference(obj_desc); + return (AE_TYPE); + } + + byte_list_length = (u32) byte_list->common.value.integer; + } + + /* + * The buffer length (number of bytes) will be the larger of: + * 1) The specified buffer length and + * 2) The length of the initializer byte list + */ + obj_desc->buffer.length = buffer_length; + if (byte_list_length > buffer_length) { + obj_desc->buffer.length = byte_list_length; + } + + /* Allocate the buffer */ + + if (obj_desc->buffer.length == 0) { + obj_desc->buffer.pointer = NULL; + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Buffer defined with zero length in AML, creating\n")); + } else { + obj_desc->buffer.pointer = + ACPI_ALLOCATE_ZEROED(obj_desc->buffer.length); + if (!obj_desc->buffer.pointer) { + acpi_ut_delete_object_desc(obj_desc); + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Initialize buffer from the byte_list (if present) */ + + if (byte_list) { + ACPI_MEMCPY(obj_desc->buffer.pointer, + byte_list->named.data, byte_list_length); + } + } + + obj_desc->buffer.flags |= AOPOBJ_DATA_VALID; + op->common.node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_desc); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_build_internal_package_obj + * + * PARAMETERS: walk_state - Current walk state + * op - Parser object to be translated + * element_count - Number of elements in the package - this is + * the num_elements argument to Package() + * obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op package object to the equivalent + * namespace object + * + * NOTE: The number of elements in the package will be always be the num_elements + * count, regardless of the number of elements in the package list. If + * num_elements is smaller, only that many package list elements are used. + * if num_elements is larger, the Package object is padded out with + * objects of type Uninitialized (as per ACPI spec.) + * + * Even though the ASL compilers do not allow num_elements to be smaller + * than the Package list length (for the fixed length package opcode), some + * BIOS code modifies the AML on the fly to adjust the num_elements, and + * this code compensates for that. This also provides compatibility with + * other AML interpreters. + * + ******************************************************************************/ + +acpi_status +acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 element_count, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_parse_object *arg; + union acpi_parse_object *parent; + union acpi_operand_object *obj_desc = NULL; + acpi_status status = AE_OK; + u32 i; + u16 index; + u16 reference_count; + + ACPI_FUNCTION_TRACE(ds_build_internal_package_obj); + + /* Find the parent of a possibly nested package */ + + parent = op->common.parent; + while ((parent->common.aml_opcode == AML_PACKAGE_OP) || + (parent->common.aml_opcode == AML_VAR_PACKAGE_OP)) { + parent = parent->common.parent; + } + + /* + * If we are evaluating a Named package object "Name (xxxx, Package)", + * the package object already exists, otherwise it must be created. + */ + obj_desc = *obj_desc_ptr; + if (!obj_desc) { + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_PACKAGE); + *obj_desc_ptr = obj_desc; + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + obj_desc->package.node = parent->common.node; + } + + /* + * Allocate the element array (array of pointers to the individual + * objects) based on the num_elements parameter. Add an extra pointer slot + * so that the list is always null terminated. + */ + obj_desc->package.elements = ACPI_ALLOCATE_ZEROED(((acpi_size) + element_count + + 1) * sizeof(void *)); + + if (!obj_desc->package.elements) { + acpi_ut_delete_object_desc(obj_desc); + return_ACPI_STATUS(AE_NO_MEMORY); + } + + obj_desc->package.count = element_count; + + /* + * Initialize the elements of the package, up to the num_elements count. + * Package is automatically padded with uninitialized (NULL) elements + * if num_elements is greater than the package list length. Likewise, + * Package is truncated if num_elements is less than the list length. + */ + arg = op->common.value.arg; + arg = arg->common.next; + for (i = 0; arg && (i < element_count); i++) { + if (arg->common.aml_opcode == AML_INT_RETURN_VALUE_OP) { + if (arg->common.node->type == ACPI_TYPE_METHOD) { + /* + * A method reference "looks" to the parser to be a method + * invocation, so we special case it here + */ + arg->common.aml_opcode = AML_INT_NAMEPATH_OP; + status = + acpi_ds_build_internal_object(walk_state, + arg, + &obj_desc-> + package. + elements[i]); + } else { + /* This package element is already built, just get it */ + + obj_desc->package.elements[i] = + ACPI_CAST_PTR(union acpi_operand_object, + arg->common.node); + } + } else { + status = acpi_ds_build_internal_object(walk_state, arg, + &obj_desc-> + package. + elements[i]); + } + + if (*obj_desc_ptr) { + + /* Existing package, get existing reference count */ + + reference_count = + (*obj_desc_ptr)->common.reference_count; + if (reference_count > 1) { + + /* Make new element ref count match original ref count */ + + for (index = 0; index < (reference_count - 1); + index++) { + acpi_ut_add_reference((obj_desc-> + package. + elements[i])); + } + } + } + + arg = arg->common.next; + } + + /* Check for match between num_elements and actual length of package_list */ + + if (arg) { + /* + * num_elements was exhausted, but there are remaining elements in the + * package_list. Truncate the package to num_elements. + * + * Note: technically, this is an error, from ACPI spec: "It is an error + * for NumElements to be less than the number of elements in the + * PackageList". However, we just print a message and + * no exception is returned. This provides Windows compatibility. Some + * BIOSs will alter the num_elements on the fly, creating this type + * of ill-formed package object. + */ + while (arg) { + /* + * We must delete any package elements that were created earlier + * and are not going to be used because of the package truncation. + */ + if (arg->common.node) { + acpi_ut_remove_reference(ACPI_CAST_PTR + (union + acpi_operand_object, + arg->common.node)); + arg->common.node = NULL; + } + + /* Find out how many elements there really are */ + + i++; + arg = arg->common.next; + } + + ACPI_INFO((AE_INFO, + "Actual Package length (%u) is larger than NumElements field (%u), truncated", + i, element_count)); + } else if (i < element_count) { + /* + * Arg list (elements) was exhausted, but we did not reach num_elements count. + * Note: this is not an error, the package is padded out with NULLs. + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Package List length (%u) smaller than NumElements count (%u), padded with null elements\n", + i, element_count)); + } + + obj_desc->package.flags |= AOPOBJ_DATA_VALID; + op->common.node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_node + * + * PARAMETERS: walk_state - Current walk state + * node - NS Node to be initialized + * op - Parser object to be translated + * + * RETURN: Status + * + * DESCRIPTION: Create the object to be associated with a namespace node + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_node(struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_TRACE_PTR(ds_create_node, op); + + /* + * Because of the execution pass through the non-control-method + * parts of the table, we can arrive here twice. Only init + * the named object node the first time through + */ + if (acpi_ns_get_attached_object(node)) { + return_ACPI_STATUS(AE_OK); + } + + if (!op->common.value.arg) { + + /* No arguments, there is nothing to do */ + + return_ACPI_STATUS(AE_OK); + } + + /* Build an internal object for the argument(s) */ + + status = acpi_ds_build_internal_object(walk_state, op->common.value.arg, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Re-type the object according to its argument */ + + node->type = obj_desc->common.type; + + /* Attach obj to node */ + + status = acpi_ns_attach_object(node, obj_desc, node->type); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +#endif /* ACPI_NO_METHOD_EXECUTION */ + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_object_from_op + * + * PARAMETERS: walk_state - Current walk state + * op - Parser op used to init the internal object + * opcode - AML opcode associated with the object + * ret_obj_desc - Namespace object to be initialized + * + * RETURN: Status + * + * DESCRIPTION: Initialize a namespace object from a parser Op and its + * associated arguments. The namespace object is a more compact + * representation of the Op and its arguments. + * + ******************************************************************************/ + +acpi_status +acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u16 opcode, + union acpi_operand_object **ret_obj_desc) +{ + const struct acpi_opcode_info *op_info; + union acpi_operand_object *obj_desc; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ds_init_object_from_op); + + obj_desc = *ret_obj_desc; + op_info = acpi_ps_get_opcode_info(opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + + /* Unknown opcode */ + + return_ACPI_STATUS(AE_TYPE); + } + + /* Perform per-object initialization */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_BUFFER: + /* + * Defer evaluation of Buffer term_arg operand + */ + obj_desc->buffer.node = + ACPI_CAST_PTR(struct acpi_namespace_node, + walk_state->operands[0]); + obj_desc->buffer.aml_start = op->named.data; + obj_desc->buffer.aml_length = op->named.length; + break; + + case ACPI_TYPE_PACKAGE: + /* + * Defer evaluation of Package term_arg operand + */ + obj_desc->package.node = + ACPI_CAST_PTR(struct acpi_namespace_node, + walk_state->operands[0]); + obj_desc->package.aml_start = op->named.data; + obj_desc->package.aml_length = op->named.length; + break; + + case ACPI_TYPE_INTEGER: + + switch (op_info->type) { + case AML_TYPE_CONSTANT: + /* + * Resolve AML Constants here - AND ONLY HERE! + * All constants are integers. + * We mark the integer with a flag that indicates that it started + * life as a constant -- so that stores to constants will perform + * as expected (noop). zero_op is used as a placeholder for optional + * target operands. + */ + obj_desc->common.flags = AOPOBJ_AML_CONSTANT; + + switch (opcode) { + case AML_ZERO_OP: + + obj_desc->integer.value = 0; + break; + + case AML_ONE_OP: + + obj_desc->integer.value = 1; + break; + + case AML_ONES_OP: + + obj_desc->integer.value = ACPI_UINT64_MAX; + + /* Truncate value if we are executing from a 32-bit ACPI table */ + +#ifndef ACPI_NO_METHOD_EXECUTION + (void)acpi_ex_truncate_for32bit_table(obj_desc); +#endif + break; + + case AML_REVISION_OP: + + obj_desc->integer.value = ACPI_CA_VERSION; + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown constant opcode 0x%X", + opcode)); + status = AE_AML_OPERAND_TYPE; + break; + } + break; + + case AML_TYPE_LITERAL: + + obj_desc->integer.value = op->common.value.integer; + +#ifndef ACPI_NO_METHOD_EXECUTION + if (acpi_ex_truncate_for32bit_table(obj_desc)) { + + /* Warn if we found a 64-bit constant in a 32-bit table */ + + ACPI_WARNING((AE_INFO, + "Truncated 64-bit constant found in 32-bit table: %8.8X%8.8X => %8.8X", + ACPI_FORMAT_UINT64(op->common. + value.integer), + (u32)obj_desc->integer.value)); + } +#endif + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown Integer type 0x%X", + op_info->type)); + status = AE_AML_OPERAND_TYPE; + break; + } + break; + + case ACPI_TYPE_STRING: + + obj_desc->string.pointer = op->common.value.string; + obj_desc->string.length = + (u32) ACPI_STRLEN(op->common.value.string); + + /* + * The string is contained in the ACPI table, don't ever try + * to delete it + */ + obj_desc->common.flags |= AOPOBJ_STATIC_POINTER; + break; + + case ACPI_TYPE_METHOD: + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + switch (op_info->type) { + case AML_TYPE_LOCAL_VARIABLE: + + /* Local ID (0-7) is (AML opcode - base AML_LOCAL_OP) */ + + obj_desc->reference.value = + ((u32)opcode) - AML_LOCAL_OP; + obj_desc->reference.class = ACPI_REFCLASS_LOCAL; + +#ifndef ACPI_NO_METHOD_EXECUTION + status = + acpi_ds_method_data_get_node(ACPI_REFCLASS_LOCAL, + obj_desc->reference. + value, walk_state, + ACPI_CAST_INDIRECT_PTR + (struct + acpi_namespace_node, + &obj_desc->reference. + object)); +#endif + break; + + case AML_TYPE_METHOD_ARGUMENT: + + /* Arg ID (0-6) is (AML opcode - base AML_ARG_OP) */ + + obj_desc->reference.value = ((u32)opcode) - AML_ARG_OP; + obj_desc->reference.class = ACPI_REFCLASS_ARG; + +#ifndef ACPI_NO_METHOD_EXECUTION + status = acpi_ds_method_data_get_node(ACPI_REFCLASS_ARG, + obj_desc-> + reference.value, + walk_state, + ACPI_CAST_INDIRECT_PTR + (struct + acpi_namespace_node, + &obj_desc-> + reference. + object)); +#endif + break; + + default: /* Object name or Debug object */ + + switch (op->common.aml_opcode) { + case AML_INT_NAMEPATH_OP: + + /* Node was saved in Op */ + + obj_desc->reference.node = op->common.node; + obj_desc->reference.object = + op->common.node->object; + obj_desc->reference.class = ACPI_REFCLASS_NAME; + break; + + case AML_DEBUG_OP: + + obj_desc->reference.class = ACPI_REFCLASS_DEBUG; + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unimplemented reference type for AML opcode: 0x%4.4X", + opcode)); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + break; + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Unimplemented data type: 0x%X", + obj_desc->common.type)); + + status = AE_AML_OPERAND_TYPE; + break; + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dsopcode.c b/kernel/drivers/acpi/acpica/dsopcode.c new file mode 100644 index 000000000..ea0cc4e08 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsopcode.c @@ -0,0 +1,757 @@ +/****************************************************************************** + * + * Module Name: dsopcode - Dispatcher support for regions and fields + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acevents.h" +#include "actables.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsopcode") + +/* Local prototypes */ +static acpi_status +acpi_ds_init_buffer_field(u16 aml_opcode, + union acpi_operand_object *obj_desc, + union acpi_operand_object *buffer_desc, + union acpi_operand_object *offset_desc, + union acpi_operand_object *length_desc, + union acpi_operand_object *result_desc); + +/******************************************************************************* + * + * FUNCTION: acpi_ds_initialize_region + * + * PARAMETERS: obj_handle - Region namespace node + * + * RETURN: Status + * + * DESCRIPTION: Front end to ev_initialize_region + * + ******************************************************************************/ + +acpi_status acpi_ds_initialize_region(acpi_handle obj_handle) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + obj_desc = acpi_ns_get_attached_object(obj_handle); + + /* Namespace is NOT locked */ + + status = acpi_ev_initialize_region(obj_desc, FALSE); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_buffer_field + * + * PARAMETERS: aml_opcode - create_xxx_field + * obj_desc - buffer_field object + * buffer_desc - Host Buffer + * offset_desc - Offset into buffer + * length_desc - Length of field (CREATE_FIELD_OP only) + * result_desc - Where to store the result + * + * RETURN: Status + * + * DESCRIPTION: Perform actual initialization of a buffer field + * + ******************************************************************************/ + +static acpi_status +acpi_ds_init_buffer_field(u16 aml_opcode, + union acpi_operand_object *obj_desc, + union acpi_operand_object *buffer_desc, + union acpi_operand_object *offset_desc, + union acpi_operand_object *length_desc, + union acpi_operand_object *result_desc) +{ + u32 offset; + u32 bit_offset; + u32 bit_count; + u8 field_flags; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ds_init_buffer_field, obj_desc); + + /* Host object must be a Buffer */ + + if (buffer_desc->common.type != ACPI_TYPE_BUFFER) { + ACPI_ERROR((AE_INFO, + "Target of Create Field is not a Buffer object - %s", + acpi_ut_get_object_type_name(buffer_desc))); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* + * The last parameter to all of these opcodes (result_desc) started + * out as a name_string, and should therefore now be a NS node + * after resolution in acpi_ex_resolve_operands(). + */ + if (ACPI_GET_DESCRIPTOR_TYPE(result_desc) != ACPI_DESC_TYPE_NAMED) { + ACPI_ERROR((AE_INFO, + "(%s) destination not a NS Node [%s]", + acpi_ps_get_opcode_name(aml_opcode), + acpi_ut_get_descriptor_name(result_desc))); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + offset = (u32) offset_desc->integer.value; + + /* + * Setup the Bit offsets and counts, according to the opcode + */ + switch (aml_opcode) { + case AML_CREATE_FIELD_OP: + + /* Offset is in bits, count is in bits */ + + field_flags = AML_FIELD_ACCESS_BYTE; + bit_offset = offset; + bit_count = (u32) length_desc->integer.value; + + /* Must have a valid (>0) bit count */ + + if (bit_count == 0) { + ACPI_ERROR((AE_INFO, + "Attempt to CreateField of length zero")); + status = AE_AML_OPERAND_VALUE; + goto cleanup; + } + break; + + case AML_CREATE_BIT_FIELD_OP: + + /* Offset is in bits, Field is one bit */ + + bit_offset = offset; + bit_count = 1; + field_flags = AML_FIELD_ACCESS_BYTE; + break; + + case AML_CREATE_BYTE_FIELD_OP: + + /* Offset is in bytes, field is one byte */ + + bit_offset = 8 * offset; + bit_count = 8; + field_flags = AML_FIELD_ACCESS_BYTE; + break; + + case AML_CREATE_WORD_FIELD_OP: + + /* Offset is in bytes, field is one word */ + + bit_offset = 8 * offset; + bit_count = 16; + field_flags = AML_FIELD_ACCESS_WORD; + break; + + case AML_CREATE_DWORD_FIELD_OP: + + /* Offset is in bytes, field is one dword */ + + bit_offset = 8 * offset; + bit_count = 32; + field_flags = AML_FIELD_ACCESS_DWORD; + break; + + case AML_CREATE_QWORD_FIELD_OP: + + /* Offset is in bytes, field is one qword */ + + bit_offset = 8 * offset; + bit_count = 64; + field_flags = AML_FIELD_ACCESS_QWORD; + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown field creation opcode 0x%02X", + aml_opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + /* Entire field must fit within the current length of the buffer */ + + if ((bit_offset + bit_count) > (8 * (u32) buffer_desc->buffer.length)) { + ACPI_ERROR((AE_INFO, + "Field [%4.4s] at %u exceeds Buffer [%4.4s] size %u (bits)", + acpi_ut_get_node_name(result_desc), + bit_offset + bit_count, + acpi_ut_get_node_name(buffer_desc->buffer.node), + 8 * (u32) buffer_desc->buffer.length)); + status = AE_AML_BUFFER_LIMIT; + goto cleanup; + } + + /* + * Initialize areas of the field object that are common to all fields + * For field_flags, use LOCK_RULE = 0 (NO_LOCK), + * UPDATE_RULE = 0 (UPDATE_PRESERVE) + */ + status = acpi_ex_prep_common_field_object(obj_desc, field_flags, 0, + bit_offset, bit_count); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + obj_desc->buffer_field.buffer_obj = buffer_desc; + + /* Reference count for buffer_desc inherits obj_desc count */ + + buffer_desc->common.reference_count = (u16) + (buffer_desc->common.reference_count + + obj_desc->common.reference_count); + +cleanup: + + /* Always delete the operands */ + + acpi_ut_remove_reference(offset_desc); + acpi_ut_remove_reference(buffer_desc); + + if (aml_opcode == AML_CREATE_FIELD_OP) { + acpi_ut_remove_reference(length_desc); + } + + /* On failure, delete the result descriptor */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(result_desc); /* Result descriptor */ + } else { + /* Now the address and length are valid for this buffer_field */ + + obj_desc->buffer_field.flags |= AOPOBJ_DATA_VALID; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_eval_buffer_field_operands + * + * PARAMETERS: walk_state - Current walk + * op - A valid buffer_field Op object + * + * RETURN: Status + * + * DESCRIPTION: Get buffer_field Buffer and Index + * Called from acpi_ds_exec_end_op during buffer_field parse tree walk + * + ******************************************************************************/ + +acpi_status +acpi_ds_eval_buffer_field_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + union acpi_parse_object *next_op; + + ACPI_FUNCTION_TRACE_PTR(ds_eval_buffer_field_operands, op); + + /* + * This is where we evaluate the address and length fields of the + * create_xxx_field declaration + */ + node = op->common.node; + + /* next_op points to the op that holds the Buffer */ + + next_op = op->common.value.arg; + + /* Evaluate/create the address and length operands */ + + status = acpi_ds_create_operands(walk_state, next_op); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Resolve the operands */ + + status = acpi_ex_resolve_operands(op->common.aml_opcode, + ACPI_WALK_OPERANDS, walk_state); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "(%s) bad operand(s), status 0x%X", + acpi_ps_get_opcode_name(op->common.aml_opcode), + status)); + + return_ACPI_STATUS(status); + } + + /* Initialize the Buffer Field */ + + if (op->common.aml_opcode == AML_CREATE_FIELD_OP) { + + /* NOTE: Slightly different operands for this opcode */ + + status = + acpi_ds_init_buffer_field(op->common.aml_opcode, obj_desc, + walk_state->operands[0], + walk_state->operands[1], + walk_state->operands[2], + walk_state->operands[3]); + } else { + /* All other, create_xxx_field opcodes */ + + status = + acpi_ds_init_buffer_field(op->common.aml_opcode, obj_desc, + walk_state->operands[0], + walk_state->operands[1], NULL, + walk_state->operands[2]); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_eval_region_operands + * + * PARAMETERS: walk_state - Current walk + * op - A valid region Op object + * + * RETURN: Status + * + * DESCRIPTION: Get region address and length + * Called from acpi_ds_exec_end_op during op_region parse tree walk + * + ******************************************************************************/ + +acpi_status +acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + union acpi_operand_object *operand_desc; + struct acpi_namespace_node *node; + union acpi_parse_object *next_op; + + ACPI_FUNCTION_TRACE_PTR(ds_eval_region_operands, op); + + /* + * This is where we evaluate the address and length fields of the + * op_region declaration + */ + node = op->common.node; + + /* next_op points to the op that holds the space_ID */ + + next_op = op->common.value.arg; + + /* next_op points to address op */ + + next_op = next_op->common.next; + + /* Evaluate/create the address and length operands */ + + status = acpi_ds_create_operands(walk_state, next_op); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Resolve the length and address operands to numbers */ + + status = acpi_ex_resolve_operands(op->common.aml_opcode, + ACPI_WALK_OPERANDS, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* + * Get the length operand and save it + * (at Top of stack) + */ + operand_desc = walk_state->operands[walk_state->num_operands - 1]; + + obj_desc->region.length = (u32) operand_desc->integer.value; + acpi_ut_remove_reference(operand_desc); + + /* + * Get the address and save it + * (at top of stack - 1) + */ + operand_desc = walk_state->operands[walk_state->num_operands - 2]; + + obj_desc->region.address = (acpi_physical_address) + operand_desc->integer.value; + acpi_ut_remove_reference(operand_desc); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n", + obj_desc, + ACPI_FORMAT_UINT64(obj_desc->region.address), + obj_desc->region.length)); + + /* Now the address and length are valid for this opregion */ + + obj_desc->region.flags |= AOPOBJ_DATA_VALID; + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_eval_table_region_operands + * + * PARAMETERS: walk_state - Current walk + * op - A valid region Op object + * + * RETURN: Status + * + * DESCRIPTION: Get region address and length. + * Called from acpi_ds_exec_end_op during data_table_region parse + * tree walk. + * + ******************************************************************************/ + +acpi_status +acpi_ds_eval_table_region_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + union acpi_operand_object **operand; + struct acpi_namespace_node *node; + union acpi_parse_object *next_op; + u32 table_index; + struct acpi_table_header *table; + + ACPI_FUNCTION_TRACE_PTR(ds_eval_table_region_operands, op); + + /* + * This is where we evaluate the Signature string, oem_id string, + * and oem_table_id string of the Data Table Region declaration + */ + node = op->common.node; + + /* next_op points to Signature string op */ + + next_op = op->common.value.arg; + + /* + * Evaluate/create the Signature string, oem_id string, + * and oem_table_id string operands + */ + status = acpi_ds_create_operands(walk_state, next_op); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Resolve the Signature string, oem_id string, + * and oem_table_id string operands + */ + status = acpi_ex_resolve_operands(op->common.aml_opcode, + ACPI_WALK_OPERANDS, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + operand = &walk_state->operands[0]; + + /* Find the ACPI table */ + + status = acpi_tb_find_table(operand[0]->string.pointer, + operand[1]->string.pointer, + operand[2]->string.pointer, &table_index); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_ut_remove_reference(operand[0]); + acpi_ut_remove_reference(operand[1]); + acpi_ut_remove_reference(operand[2]); + + status = acpi_get_table_by_index(table_index, &table); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + obj_desc->region.address = ACPI_PTR_TO_PHYSADDR(table); + obj_desc->region.length = table->length; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "RgnObj %p Addr %8.8X%8.8X Len %X\n", + obj_desc, + ACPI_FORMAT_UINT64(obj_desc->region.address), + obj_desc->region.length)); + + /* Now the address and length are valid for this opregion */ + + obj_desc->region.flags |= AOPOBJ_DATA_VALID; + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_eval_data_object_operands + * + * PARAMETERS: walk_state - Current walk + * op - A valid data_object Op object + * obj_desc - data_object + * + * RETURN: Status + * + * DESCRIPTION: Get the operands and complete the following data object types: + * Buffer, Package. + * + ******************************************************************************/ + +acpi_status +acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + union acpi_operand_object *obj_desc) +{ + acpi_status status; + union acpi_operand_object *arg_desc; + u32 length; + + ACPI_FUNCTION_TRACE(ds_eval_data_object_operands); + + /* The first operand (for all of these data objects) is the length */ + + /* + * Set proper index into operand stack for acpi_ds_obj_stack_push + * invoked inside acpi_ds_create_operand. + */ + walk_state->operand_index = walk_state->num_operands; + + status = acpi_ds_create_operand(walk_state, op->common.value.arg, 1); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ex_resolve_operands(walk_state->opcode, + &(walk_state-> + operands[walk_state->num_operands - + 1]), walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Extract length operand */ + + arg_desc = walk_state->operands[walk_state->num_operands - 1]; + length = (u32) arg_desc->integer.value; + + /* Cleanup for length operand */ + + status = acpi_ds_obj_stack_pop(1, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_ut_remove_reference(arg_desc); + + /* + * Create the actual data object + */ + switch (op->common.aml_opcode) { + case AML_BUFFER_OP: + + status = + acpi_ds_build_internal_buffer_obj(walk_state, op, length, + &obj_desc); + break; + + case AML_PACKAGE_OP: + case AML_VAR_PACKAGE_OP: + + status = + acpi_ds_build_internal_package_obj(walk_state, op, length, + &obj_desc); + break; + + default: + + return_ACPI_STATUS(AE_AML_BAD_OPCODE); + } + + if (ACPI_SUCCESS(status)) { + /* + * Return the object in the walk_state, unless the parent is a package - + * in this case, the return object will be stored in the parse tree + * for the package. + */ + if ((!op->common.parent) || + ((op->common.parent->common.aml_opcode != AML_PACKAGE_OP) && + (op->common.parent->common.aml_opcode != + AML_VAR_PACKAGE_OP) + && (op->common.parent->common.aml_opcode != + AML_NAME_OP))) { + walk_state->result_obj = obj_desc; + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_eval_bank_field_operands + * + * PARAMETERS: walk_state - Current walk + * op - A valid bank_field Op object + * + * RETURN: Status + * + * DESCRIPTION: Get bank_field bank_value + * Called from acpi_ds_exec_end_op during bank_field parse tree walk + * + ******************************************************************************/ + +acpi_status +acpi_ds_eval_bank_field_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + union acpi_operand_object *operand_desc; + struct acpi_namespace_node *node; + union acpi_parse_object *next_op; + union acpi_parse_object *arg; + + ACPI_FUNCTION_TRACE_PTR(ds_eval_bank_field_operands, op); + + /* + * This is where we evaluate the bank_value field of the + * bank_field declaration + */ + + /* next_op points to the op that holds the Region */ + + next_op = op->common.value.arg; + + /* next_op points to the op that holds the Bank Register */ + + next_op = next_op->common.next; + + /* next_op points to the op that holds the Bank Value */ + + next_op = next_op->common.next; + + /* + * Set proper index into operand stack for acpi_ds_obj_stack_push + * invoked inside acpi_ds_create_operand. + * + * We use walk_state->Operands[0] to store the evaluated bank_value + */ + walk_state->operand_index = 0; + + status = acpi_ds_create_operand(walk_state, next_op, 0); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ex_resolve_to_value(&walk_state->operands[0], walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DUMP_OPERANDS(ACPI_WALK_OPERANDS, + acpi_ps_get_opcode_name(op->common.aml_opcode), 1); + /* + * Get the bank_value operand and save it + * (at Top of stack) + */ + operand_desc = walk_state->operands[0]; + + /* Arg points to the start Bank Field */ + + arg = acpi_ps_get_arg(op, 4); + while (arg) { + + /* Ignore OFFSET and ACCESSAS terms here */ + + if (arg->common.aml_opcode == AML_INT_NAMEDFIELD_OP) { + node = arg->common.node; + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + obj_desc->bank_field.value = + (u32) operand_desc->integer.value; + } + + /* Move to next field in the list */ + + arg = arg->common.next; + } + + acpi_ut_remove_reference(operand_desc); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dsutils.c b/kernel/drivers/acpi/acpica/dsutils.c new file mode 100644 index 000000000..deeddd6d2 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dsutils.c @@ -0,0 +1,879 @@ +/******************************************************************************* + * + * Module Name: dsutils - Dispatcher utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dsutils") + +/******************************************************************************* + * + * FUNCTION: acpi_ds_clear_implicit_return + * + * PARAMETERS: walk_state - Current State + * + * RETURN: None. + * + * DESCRIPTION: Clear and remove a reference on an implicit return value. Used + * to delete "stale" return values (if enabled, the return value + * from every operator is saved at least momentarily, in case the + * parent method exits.) + * + ******************************************************************************/ +void acpi_ds_clear_implicit_return(struct acpi_walk_state *walk_state) +{ + ACPI_FUNCTION_NAME(ds_clear_implicit_return); + + /* + * Slack must be enabled for this feature + */ + if (!acpi_gbl_enable_interpreter_slack) { + return; + } + + if (walk_state->implicit_return_obj) { + /* + * Delete any "stale" implicit return. However, in + * complex statements, the implicit return value can be + * bubbled up several levels. + */ + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Removing reference on stale implicit return obj %p\n", + walk_state->implicit_return_obj)); + + acpi_ut_remove_reference(walk_state->implicit_return_obj); + walk_state->implicit_return_obj = NULL; + } +} + +#ifndef ACPI_NO_METHOD_EXECUTION +/******************************************************************************* + * + * FUNCTION: acpi_ds_do_implicit_return + * + * PARAMETERS: return_desc - The return value + * walk_state - Current State + * add_reference - True if a reference should be added to the + * return object + * + * RETURN: TRUE if implicit return enabled, FALSE otherwise + * + * DESCRIPTION: Implements the optional "implicit return". We save the result + * of every ASL operator and control method invocation in case the + * parent method exit. Before storing a new return value, we + * delete the previous return value. + * + ******************************************************************************/ + +u8 +acpi_ds_do_implicit_return(union acpi_operand_object *return_desc, + struct acpi_walk_state *walk_state, u8 add_reference) +{ + ACPI_FUNCTION_NAME(ds_do_implicit_return); + + /* + * Slack must be enabled for this feature, and we must + * have a valid return object + */ + if ((!acpi_gbl_enable_interpreter_slack) || (!return_desc)) { + return (FALSE); + } + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Result %p will be implicitly returned; Prev=%p\n", + return_desc, walk_state->implicit_return_obj)); + + /* + * Delete any "stale" implicit return value first. However, in + * complex statements, the implicit return value can be + * bubbled up several levels, so we don't clear the value if it + * is the same as the return_desc. + */ + if (walk_state->implicit_return_obj) { + if (walk_state->implicit_return_obj == return_desc) { + return (TRUE); + } + acpi_ds_clear_implicit_return(walk_state); + } + + /* Save the implicit return value, add a reference if requested */ + + walk_state->implicit_return_obj = return_desc; + if (add_reference) { + acpi_ut_add_reference(return_desc); + } + + return (TRUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_is_result_used + * + * PARAMETERS: op - Current Op + * walk_state - Current State + * + * RETURN: TRUE if result is used, FALSE otherwise + * + * DESCRIPTION: Check if a result object will be used by the parent + * + ******************************************************************************/ + +u8 +acpi_ds_is_result_used(union acpi_parse_object * op, + struct acpi_walk_state * walk_state) +{ + const struct acpi_opcode_info *parent_info; + + ACPI_FUNCTION_TRACE_PTR(ds_is_result_used, op); + + /* Must have both an Op and a Result Object */ + + if (!op) { + ACPI_ERROR((AE_INFO, "Null Op")); + return_UINT8(TRUE); + } + + /* + * We know that this operator is not a + * Return() operator (would not come here.) The following code is the + * optional support for a so-called "implicit return". Some AML code + * assumes that the last value of the method is "implicitly" returned + * to the caller. Just save the last result as the return value. + * NOTE: this is optional because the ASL language does not actually + * support this behavior. + */ + (void)acpi_ds_do_implicit_return(walk_state->result_obj, walk_state, + TRUE); + + /* + * Now determine if the parent will use the result + * + * If there is no parent, or the parent is a scope_op, we are executing + * at the method level. An executing method typically has no parent, + * since each method is parsed separately. A method invoked externally + * via execute_control_method has a scope_op as the parent. + */ + if ((!op->common.parent) || + (op->common.parent->common.aml_opcode == AML_SCOPE_OP)) { + + /* No parent, the return value cannot possibly be used */ + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "At Method level, result of [%s] not used\n", + acpi_ps_get_opcode_name(op->common. + aml_opcode))); + return_UINT8(FALSE); + } + + /* Get info on the parent. The root_op is AML_SCOPE */ + + parent_info = + acpi_ps_get_opcode_info(op->common.parent->common.aml_opcode); + if (parent_info->class == AML_CLASS_UNKNOWN) { + ACPI_ERROR((AE_INFO, "Unknown parent opcode Op=%p", op)); + return_UINT8(FALSE); + } + + /* + * Decide what to do with the result based on the parent. If + * the parent opcode will not use the result, delete the object. + * Otherwise leave it as is, it will be deleted when it is used + * as an operand later. + */ + switch (parent_info->class) { + case AML_CLASS_CONTROL: + + switch (op->common.parent->common.aml_opcode) { + case AML_RETURN_OP: + + /* Never delete the return value associated with a return opcode */ + + goto result_used; + + case AML_IF_OP: + case AML_WHILE_OP: + /* + * If we are executing the predicate AND this is the predicate op, + * we will use the return value + */ + if ((walk_state->control_state->common.state == + ACPI_CONTROL_PREDICATE_EXECUTING) + && (walk_state->control_state->control. + predicate_op == op)) { + goto result_used; + } + break; + + default: + + /* Ignore other control opcodes */ + + break; + } + + /* The general control opcode returns no result */ + + goto result_not_used; + + case AML_CLASS_CREATE: + /* + * These opcodes allow term_arg(s) as operands and therefore + * the operands can be method calls. The result is used. + */ + goto result_used; + + case AML_CLASS_NAMED_OBJECT: + + if ((op->common.parent->common.aml_opcode == AML_REGION_OP) || + (op->common.parent->common.aml_opcode == AML_DATA_REGION_OP) + || (op->common.parent->common.aml_opcode == AML_PACKAGE_OP) + || (op->common.parent->common.aml_opcode == + AML_VAR_PACKAGE_OP) + || (op->common.parent->common.aml_opcode == AML_BUFFER_OP) + || (op->common.parent->common.aml_opcode == + AML_INT_EVAL_SUBTREE_OP) + || (op->common.parent->common.aml_opcode == + AML_BANK_FIELD_OP)) { + /* + * These opcodes allow term_arg(s) as operands and therefore + * the operands can be method calls. The result is used. + */ + goto result_used; + } + + goto result_not_used; + + default: + /* + * In all other cases. the parent will actually use the return + * object, so keep it. + */ + goto result_used; + } + +result_used: + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Result of [%s] used by Parent [%s] Op=%p\n", + acpi_ps_get_opcode_name(op->common.aml_opcode), + acpi_ps_get_opcode_name(op->common.parent->common. + aml_opcode), op)); + + return_UINT8(TRUE); + +result_not_used: + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Result of [%s] not used by Parent [%s] Op=%p\n", + acpi_ps_get_opcode_name(op->common.aml_opcode), + acpi_ps_get_opcode_name(op->common.parent->common. + aml_opcode), op)); + + return_UINT8(FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_delete_result_if_not_used + * + * PARAMETERS: op - Current parse Op + * result_obj - Result of the operation + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Used after interpretation of an opcode. If there is an internal + * result descriptor, check if the parent opcode will actually use + * this result. If not, delete the result now so that it will + * not become orphaned. + * + ******************************************************************************/ + +void +acpi_ds_delete_result_if_not_used(union acpi_parse_object *op, + union acpi_operand_object *result_obj, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ds_delete_result_if_not_used, result_obj); + + if (!op) { + ACPI_ERROR((AE_INFO, "Null Op")); + return_VOID; + } + + if (!result_obj) { + return_VOID; + } + + if (!acpi_ds_is_result_used(op, walk_state)) { + + /* Must pop the result stack (obj_desc should be equal to result_obj) */ + + status = acpi_ds_result_pop(&obj_desc, walk_state); + if (ACPI_SUCCESS(status)) { + acpi_ut_remove_reference(result_obj); + } + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_resolve_operands + * + * PARAMETERS: walk_state - Current walk state with operands on stack + * + * RETURN: Status + * + * DESCRIPTION: Resolve all operands to their values. Used to prepare + * arguments to a control method invocation (a call from one + * method to another.) + * + ******************************************************************************/ + +acpi_status acpi_ds_resolve_operands(struct acpi_walk_state *walk_state) +{ + u32 i; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_PTR(ds_resolve_operands, walk_state); + + /* + * Attempt to resolve each of the valid operands + * Method arguments are passed by reference, not by value. This means + * that the actual objects are passed, not copies of the objects. + */ + for (i = 0; i < walk_state->num_operands; i++) { + status = + acpi_ex_resolve_to_value(&walk_state->operands[i], + walk_state); + if (ACPI_FAILURE(status)) { + break; + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_clear_operands + * + * PARAMETERS: walk_state - Current walk state with operands on stack + * + * RETURN: None + * + * DESCRIPTION: Clear all operands on the current walk state operand stack. + * + ******************************************************************************/ + +void acpi_ds_clear_operands(struct acpi_walk_state *walk_state) +{ + u32 i; + + ACPI_FUNCTION_TRACE_PTR(ds_clear_operands, walk_state); + + /* Remove a reference on each operand on the stack */ + + for (i = 0; i < walk_state->num_operands; i++) { + /* + * Remove a reference to all operands, including both + * "Arguments" and "Targets". + */ + acpi_ut_remove_reference(walk_state->operands[i]); + walk_state->operands[i] = NULL; + } + + walk_state->num_operands = 0; + return_VOID; +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_operand + * + * PARAMETERS: walk_state - Current walk state + * arg - Parse object for the argument + * arg_index - Which argument (zero based) + * + * RETURN: Status + * + * DESCRIPTION: Translate a parse tree object that is an argument to an AML + * opcode to the equivalent interpreter object. This may include + * looking up a name or entering a new name into the internal + * namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_operand(struct acpi_walk_state *walk_state, + union acpi_parse_object *arg, u32 arg_index) +{ + acpi_status status = AE_OK; + char *name_string; + u32 name_length; + union acpi_operand_object *obj_desc; + union acpi_parse_object *parent_op; + u16 opcode; + acpi_interpreter_mode interpreter_mode; + const struct acpi_opcode_info *op_info; + + ACPI_FUNCTION_TRACE_PTR(ds_create_operand, arg); + + /* A valid name must be looked up in the namespace */ + + if ((arg->common.aml_opcode == AML_INT_NAMEPATH_OP) && + (arg->common.value.string) && + !(arg->common.flags & ACPI_PARSEOP_IN_STACK)) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Getting a name: Arg=%p\n", + arg)); + + /* Get the entire name string from the AML stream */ + + status = + acpi_ex_get_name_string(ACPI_TYPE_ANY, + arg->common.value.buffer, + &name_string, &name_length); + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* All prefixes have been handled, and the name is in name_string */ + + /* + * Special handling for buffer_field declarations. This is a deferred + * opcode that unfortunately defines the field name as the last + * parameter instead of the first. We get here when we are performing + * the deferred execution, so the actual name of the field is already + * in the namespace. We don't want to attempt to look it up again + * because we may be executing in a different scope than where the + * actual opcode exists. + */ + if ((walk_state->deferred_node) && + (walk_state->deferred_node->type == ACPI_TYPE_BUFFER_FIELD) + && (arg_index == + (u32) ((walk_state->opcode == + AML_CREATE_FIELD_OP) ? 3 : 2))) { + obj_desc = + ACPI_CAST_PTR(union acpi_operand_object, + walk_state->deferred_node); + status = AE_OK; + } else { /* All other opcodes */ + + /* + * Differentiate between a namespace "create" operation + * versus a "lookup" operation (IMODE_LOAD_PASS2 vs. + * IMODE_EXECUTE) in order to support the creation of + * namespace objects during the execution of control methods. + */ + parent_op = arg->common.parent; + op_info = + acpi_ps_get_opcode_info(parent_op->common. + aml_opcode); + if ((op_info->flags & AML_NSNODE) + && (parent_op->common.aml_opcode != + AML_INT_METHODCALL_OP) + && (parent_op->common.aml_opcode != AML_REGION_OP) + && (parent_op->common.aml_opcode != + AML_INT_NAMEPATH_OP)) { + + /* Enter name into namespace if not found */ + + interpreter_mode = ACPI_IMODE_LOAD_PASS2; + } else { + /* Return a failure if name not found */ + + interpreter_mode = ACPI_IMODE_EXECUTE; + } + + status = + acpi_ns_lookup(walk_state->scope_info, name_string, + ACPI_TYPE_ANY, interpreter_mode, + ACPI_NS_SEARCH_PARENT | + ACPI_NS_DONT_OPEN_SCOPE, walk_state, + ACPI_CAST_INDIRECT_PTR(struct + acpi_namespace_node, + &obj_desc)); + /* + * The only case where we pass through (ignore) a NOT_FOUND + * error is for the cond_ref_of opcode. + */ + if (status == AE_NOT_FOUND) { + if (parent_op->common.aml_opcode == + AML_COND_REF_OF_OP) { + /* + * For the Conditional Reference op, it's OK if + * the name is not found; We just need a way to + * indicate this to the interpreter, set the + * object to the root + */ + obj_desc = + ACPI_CAST_PTR(union + acpi_operand_object, + acpi_gbl_root_node); + status = AE_OK; + } else if (parent_op->common.aml_opcode == + AML_EXTERNAL_OP) { + + /* TBD: May only be temporary */ + + obj_desc = + acpi_ut_create_string_object((acpi_size) name_length); + + ACPI_STRNCPY(obj_desc->string.pointer, + name_string, name_length); + status = AE_OK; + } else { + /* + * We just plain didn't find it -- which is a + * very serious error at this point + */ + status = AE_AML_NAME_NOT_FOUND; + } + } + + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(name_string, status); + } + } + + /* Free the namestring created above */ + + ACPI_FREE(name_string); + + /* Check status from the lookup */ + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Put the resulting object onto the current object stack */ + + status = acpi_ds_obj_stack_push(obj_desc, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object + (obj_desc, walk_state)); + } else { + /* Check for null name case */ + + if ((arg->common.aml_opcode == AML_INT_NAMEPATH_OP) && + !(arg->common.flags & ACPI_PARSEOP_IN_STACK)) { + /* + * If the name is null, this means that this is an + * optional result parameter that was not specified + * in the original ASL. Create a Zero Constant for a + * placeholder. (Store to a constant is a Noop.) + */ + opcode = AML_ZERO_OP; /* Has no arguments! */ + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Null namepath: Arg=%p\n", arg)); + } else { + opcode = arg->common.aml_opcode; + } + + /* Get the object type of the argument */ + + op_info = acpi_ps_get_opcode_info(opcode); + if (op_info->object_type == ACPI_TYPE_INVALID) { + return_ACPI_STATUS(AE_NOT_IMPLEMENTED); + } + + if ((op_info->flags & AML_HAS_RETVAL) + || (arg->common.flags & ACPI_PARSEOP_IN_STACK)) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Argument previously created, already stacked\n")); + + ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object + (walk_state-> + operands[walk_state->num_operands - + 1], walk_state)); + + /* + * Use value that was already previously returned + * by the evaluation of this argument + */ + status = acpi_ds_result_pop(&obj_desc, walk_state); + if (ACPI_FAILURE(status)) { + /* + * Only error is underflow, and this indicates + * a missing or null operand! + */ + ACPI_EXCEPTION((AE_INFO, status, + "Missing or null operand")); + return_ACPI_STATUS(status); + } + } else { + /* Create an ACPI_INTERNAL_OBJECT for the argument */ + + obj_desc = + acpi_ut_create_internal_object(op_info-> + object_type); + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Initialize the new object */ + + status = + acpi_ds_init_object_from_op(walk_state, arg, opcode, + &obj_desc); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_object_desc(obj_desc); + return_ACPI_STATUS(status); + } + } + + /* Put the operand object on the object stack */ + + status = acpi_ds_obj_stack_push(obj_desc, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object + (obj_desc, walk_state)); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_operands + * + * PARAMETERS: walk_state - Current state + * first_arg - First argument of a parser argument tree + * + * RETURN: Status + * + * DESCRIPTION: Convert an operator's arguments from a parse tree format to + * namespace objects and place those argument object on the object + * stack in preparation for evaluation by the interpreter. + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *first_arg) +{ + acpi_status status = AE_OK; + union acpi_parse_object *arg; + union acpi_parse_object *arguments[ACPI_OBJ_NUM_OPERANDS]; + u32 arg_count = 0; + u32 index = walk_state->num_operands; + u32 i; + + ACPI_FUNCTION_TRACE_PTR(ds_create_operands, first_arg); + + /* Get all arguments in the list */ + + arg = first_arg; + while (arg) { + if (index >= ACPI_OBJ_NUM_OPERANDS) { + return_ACPI_STATUS(AE_BAD_DATA); + } + + arguments[index] = arg; + walk_state->operands[index] = NULL; + + /* Move on to next argument, if any */ + + arg = arg->common.next; + arg_count++; + index++; + } + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "NumOperands %d, ArgCount %d, Index %d\n", + walk_state->num_operands, arg_count, index)); + + /* Create the interpreter arguments, in reverse order */ + + index--; + for (i = 0; i < arg_count; i++) { + arg = arguments[index]; + walk_state->operand_index = (u8)index; + + status = acpi_ds_create_operand(walk_state, arg, index); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Created Arg #%u (%p) %u args total\n", + index, arg, arg_count)); + index--; + } + + return_ACPI_STATUS(status); + +cleanup: + /* + * We must undo everything done above; meaning that we must + * pop everything off of the operand stack and delete those + * objects + */ + acpi_ds_obj_stack_pop_and_delete(arg_count, walk_state); + + ACPI_EXCEPTION((AE_INFO, status, "While creating Arg %u", index)); + return_ACPI_STATUS(status); +} + +/***************************************************************************** + * + * FUNCTION: acpi_ds_evaluate_name_path + * + * PARAMETERS: walk_state - Current state of the parse tree walk, + * the opcode of current operation should be + * AML_INT_NAMEPATH_OP + * + * RETURN: Status + * + * DESCRIPTION: Translate the -name_path- parse tree object to the equivalent + * interpreter object, convert it to value, if needed, duplicate + * it, if needed, and push it onto the current result stack. + * + ****************************************************************************/ + +acpi_status acpi_ds_evaluate_name_path(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_parse_object *op = walk_state->op; + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *new_obj_desc; + u8 type; + + ACPI_FUNCTION_TRACE_PTR(ds_evaluate_name_path, walk_state); + + if (!op->common.parent) { + + /* This happens after certain exception processing */ + + goto exit; + } + + if ((op->common.parent->common.aml_opcode == AML_PACKAGE_OP) || + (op->common.parent->common.aml_opcode == AML_VAR_PACKAGE_OP) || + (op->common.parent->common.aml_opcode == AML_REF_OF_OP)) { + + /* TBD: Should we specify this feature as a bit of op_info->Flags of these opcodes? */ + + goto exit; + } + + status = acpi_ds_create_operand(walk_state, op, 0); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (op->common.flags & ACPI_PARSEOP_TARGET) { + new_obj_desc = *operand; + goto push_result; + } + + type = (*operand)->common.type; + + status = acpi_ex_resolve_to_value(operand, walk_state); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (type == ACPI_TYPE_INTEGER) { + + /* It was incremented by acpi_ex_resolve_to_value */ + + acpi_ut_remove_reference(*operand); + + status = + acpi_ut_copy_iobject_to_iobject(*operand, &new_obj_desc, + walk_state); + if (ACPI_FAILURE(status)) { + goto exit; + } + } else { + /* + * The object either was anew created or is + * a Namespace node - don't decrement it. + */ + new_obj_desc = *operand; + } + + /* Cleanup for name-path operand */ + + status = acpi_ds_obj_stack_pop(1, walk_state); + if (ACPI_FAILURE(status)) { + walk_state->result_obj = new_obj_desc; + goto exit; + } + +push_result: + + walk_state->result_obj = new_obj_desc; + + status = acpi_ds_result_push(walk_state->result_obj, walk_state); + if (ACPI_SUCCESS(status)) { + + /* Force to take it from stack */ + + op->common.flags |= ACPI_PARSEOP_IN_STACK; + } + +exit: + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dswexec.c b/kernel/drivers/acpi/acpica/dswexec.c new file mode 100644 index 000000000..df54d4622 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dswexec.c @@ -0,0 +1,759 @@ +/****************************************************************************** + * + * Module Name: dswexec - Dispatcher method execution callbacks; + * dispatch to interpreter. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dswexec") + +/* + * Dispatch table for opcode classes + */ +static acpi_execute_op acpi_gbl_op_type_dispatch[] = { + acpi_ex_opcode_0A_0T_1R, + acpi_ex_opcode_1A_0T_0R, + acpi_ex_opcode_1A_0T_1R, + acpi_ex_opcode_1A_1T_0R, + acpi_ex_opcode_1A_1T_1R, + acpi_ex_opcode_2A_0T_0R, + acpi_ex_opcode_2A_0T_1R, + acpi_ex_opcode_2A_1T_1R, + acpi_ex_opcode_2A_2T_1R, + acpi_ex_opcode_3A_0T_0R, + acpi_ex_opcode_3A_1T_1R, + acpi_ex_opcode_6A_0T_1R +}; + +/***************************************************************************** + * + * FUNCTION: acpi_ds_get_predicate_value + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * result_obj - if non-zero, pop result from result stack + * + * RETURN: Status + * + * DESCRIPTION: Get the result of a predicate evaluation + * + ****************************************************************************/ + +acpi_status +acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state, + union acpi_operand_object *result_obj) +{ + acpi_status status = AE_OK; + union acpi_operand_object *obj_desc; + union acpi_operand_object *local_obj_desc = NULL; + + ACPI_FUNCTION_TRACE_PTR(ds_get_predicate_value, walk_state); + + walk_state->control_state->common.state = 0; + + if (result_obj) { + status = acpi_ds_result_pop(&obj_desc, walk_state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not get result from predicate evaluation")); + + return_ACPI_STATUS(status); + } + } else { + status = acpi_ds_create_operand(walk_state, walk_state->op, 0); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = + acpi_ex_resolve_to_value(&walk_state->operands[0], + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + obj_desc = walk_state->operands[0]; + } + + if (!obj_desc) { + ACPI_ERROR((AE_INFO, + "No predicate ObjDesc=%p State=%p", + obj_desc, walk_state)); + + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + /* + * Result of predicate evaluation must be an Integer + * object. Implicitly convert the argument if necessary. + */ + status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc, 16); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + if (local_obj_desc->common.type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, + "Bad predicate (not an integer) ObjDesc=%p State=%p Type=0x%X", + obj_desc, walk_state, obj_desc->common.type)); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* Truncate the predicate to 32-bits if necessary */ + + (void)acpi_ex_truncate_for32bit_table(local_obj_desc); + + /* + * Save the result of the predicate evaluation on + * the control stack + */ + if (local_obj_desc->integer.value) { + walk_state->control_state->common.value = TRUE; + } else { + /* + * Predicate is FALSE, we will just toss the + * rest of the package + */ + walk_state->control_state->common.value = FALSE; + status = AE_CTRL_FALSE; + } + + /* Predicate can be used for an implicit return value */ + + (void)acpi_ds_do_implicit_return(local_obj_desc, walk_state, TRUE); + +cleanup: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Completed a predicate eval=%X Op=%p\n", + walk_state->control_state->common.value, + walk_state->op)); + + /* Break to debugger to display result */ + + ACPI_DEBUGGER_EXEC(acpi_db_display_result_object + (local_obj_desc, walk_state)); + + /* + * Delete the predicate result object (we know that + * we don't need it anymore) + */ + if (local_obj_desc != obj_desc) { + acpi_ut_remove_reference(local_obj_desc); + } + acpi_ut_remove_reference(obj_desc); + + walk_state->control_state->common.state = ACPI_CONTROL_NORMAL; + return_ACPI_STATUS(status); +} + +/***************************************************************************** + * + * FUNCTION: acpi_ds_exec_begin_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * out_op - Where to return op if a new one is created + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the execution of control + * methods. This is where most operators and operands are + * dispatched to the interpreter. + * + ****************************************************************************/ + +acpi_status +acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op) +{ + union acpi_parse_object *op; + acpi_status status = AE_OK; + u32 opcode_class; + + ACPI_FUNCTION_TRACE_PTR(ds_exec_begin_op, walk_state); + + op = walk_state->op; + if (!op) { + status = acpi_ds_load2_begin_op(walk_state, out_op); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + op = *out_op; + walk_state->op = op; + walk_state->opcode = op->common.aml_opcode; + walk_state->op_info = + acpi_ps_get_opcode_info(op->common.aml_opcode); + + if (acpi_ns_opens_scope(walk_state->op_info->object_type)) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "(%s) Popping scope for Op %p\n", + acpi_ut_get_type_name(walk_state-> + op_info-> + object_type), + op)); + + status = acpi_ds_scope_stack_pop(walk_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + } + } + + if (op == walk_state->origin) { + if (out_op) { + *out_op = op; + } + + return_ACPI_STATUS(AE_OK); + } + + /* + * If the previous opcode was a conditional, this opcode + * must be the beginning of the associated predicate. + * Save this knowledge in the current scope descriptor + */ + if ((walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_CONDITIONAL_EXECUTING)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Exec predicate Op=%p State=%p\n", op, + walk_state)); + + walk_state->control_state->common.state = + ACPI_CONTROL_PREDICATE_EXECUTING; + + /* Save start of predicate */ + + walk_state->control_state->control.predicate_op = op; + } + + opcode_class = walk_state->op_info->class; + + /* We want to send namepaths to the load code */ + + if (op->common.aml_opcode == AML_INT_NAMEPATH_OP) { + opcode_class = AML_CLASS_NAMED_OBJECT; + } + + /* + * Handle the opcode based upon the opcode type + */ + switch (opcode_class) { + case AML_CLASS_CONTROL: + + status = acpi_ds_exec_begin_control_op(walk_state, op); + break; + + case AML_CLASS_NAMED_OBJECT: + + if (walk_state->walk_type & ACPI_WALK_METHOD) { + /* + * Found a named object declaration during method execution; + * we must enter this object into the namespace. The created + * object is temporary and will be deleted upon completion of + * the execution of this method. + * + * Note 10/2010: Except for the Scope() op. This opcode does + * not actually create a new object, it refers to an existing + * object. However, for Scope(), we want to indeed open a + * new scope. + */ + if (op->common.aml_opcode != AML_SCOPE_OP) { + status = + acpi_ds_load2_begin_op(walk_state, NULL); + } else { + status = + acpi_ds_scope_stack_push(op->named.node, + op->named.node-> + type, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } + break; + + case AML_CLASS_EXECUTE: + case AML_CLASS_CREATE: + + break; + + default: + + break; + } + + /* Nothing to do here during method execution */ + + return_ACPI_STATUS(status); + +error_exit: + status = acpi_ds_method_error(status, walk_state); + return_ACPI_STATUS(status); +} + +/***************************************************************************** + * + * FUNCTION: acpi_ds_exec_end_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the execution of control + * methods. The only thing we really need to do here is to + * notice the beginning of IF, ELSE, and WHILE blocks. + * + ****************************************************************************/ + +acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *op; + acpi_status status = AE_OK; + u32 op_type; + u32 op_class; + union acpi_parse_object *next_op; + union acpi_parse_object *first_arg; + + ACPI_FUNCTION_TRACE_PTR(ds_exec_end_op, walk_state); + + op = walk_state->op; + op_type = walk_state->op_info->type; + op_class = walk_state->op_info->class; + + if (op_class == AML_CLASS_UNKNOWN) { + ACPI_ERROR((AE_INFO, "Unknown opcode 0x%X", + op->common.aml_opcode)); + return_ACPI_STATUS(AE_NOT_IMPLEMENTED); + } + + first_arg = op->common.value.arg; + + /* Init the walk state */ + + walk_state->num_operands = 0; + walk_state->operand_index = 0; + walk_state->return_desc = NULL; + walk_state->result_obj = NULL; + + /* Call debugger for single step support (DEBUG build only) */ + + ACPI_DEBUGGER_EXEC(status = + acpi_db_single_step(walk_state, op, op_class)); + ACPI_DEBUGGER_EXEC(if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status);} + ) ; + + /* Decode the Opcode Class */ + + switch (op_class) { + case AML_CLASS_ARGUMENT: /* Constants, literals, etc. */ + + if (walk_state->opcode == AML_INT_NAMEPATH_OP) { + status = acpi_ds_evaluate_name_path(walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + } + break; + + case AML_CLASS_EXECUTE: /* Most operators with arguments */ + + /* Build resolved operand stack */ + + status = acpi_ds_create_operands(walk_state, first_arg); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * All opcodes require operand resolution, with the only exceptions + * being the object_type and size_of operators. + */ + if (!(walk_state->op_info->flags & AML_NO_OPERAND_RESOLVE)) { + + /* Resolve all operands */ + + status = acpi_ex_resolve_operands(walk_state->opcode, + &(walk_state-> + operands + [walk_state-> + num_operands - 1]), + walk_state); + } + + if (ACPI_SUCCESS(status)) { + /* + * Dispatch the request to the appropriate interpreter handler + * routine. There is one routine per opcode "type" based upon the + * number of opcode arguments and return type. + */ + status = + acpi_gbl_op_type_dispatch[op_type] (walk_state); + } else { + /* + * Treat constructs of the form "Store(LocalX,LocalX)" as noops when the + * Local is uninitialized. + */ + if ((status == AE_AML_UNINITIALIZED_LOCAL) && + (walk_state->opcode == AML_STORE_OP) && + (walk_state->operands[0]->common.type == + ACPI_TYPE_LOCAL_REFERENCE) + && (walk_state->operands[1]->common.type == + ACPI_TYPE_LOCAL_REFERENCE) + && (walk_state->operands[0]->reference.class == + walk_state->operands[1]->reference.class) + && (walk_state->operands[0]->reference.value == + walk_state->operands[1]->reference.value)) { + status = AE_OK; + } else { + ACPI_EXCEPTION((AE_INFO, status, + "While resolving operands for [%s]", + acpi_ps_get_opcode_name + (walk_state->opcode))); + } + } + + /* Always delete the argument objects and clear the operand stack */ + + acpi_ds_clear_operands(walk_state); + + /* + * If a result object was returned from above, push it on the + * current result stack + */ + if (ACPI_SUCCESS(status) && walk_state->result_obj) { + status = + acpi_ds_result_push(walk_state->result_obj, + walk_state); + } + break; + + default: + + switch (op_type) { + case AML_TYPE_CONTROL: /* Type 1 opcode, IF/ELSE/WHILE/NOOP */ + + /* 1 Operand, 0 external_result, 0 internal_result */ + + status = acpi_ds_exec_end_control_op(walk_state, op); + + break; + + case AML_TYPE_METHOD_CALL: + /* + * If the method is referenced from within a package + * declaration, it is not a invocation of the method, just + * a reference to it. + */ + if ((op->asl.parent) && + ((op->asl.parent->asl.aml_opcode == AML_PACKAGE_OP) + || (op->asl.parent->asl.aml_opcode == + AML_VAR_PACKAGE_OP))) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Method Reference in a Package, Op=%p\n", + op)); + + op->common.node = + (struct acpi_namespace_node *)op->asl.value. + arg->asl.node; + acpi_ut_add_reference(op->asl.value.arg->asl. + node->object); + return_ACPI_STATUS(AE_OK); + } + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Method invocation, Op=%p\n", op)); + + /* + * (AML_METHODCALL) Op->Asl.Value.Arg->Asl.Node contains + * the method Node pointer + */ + /* next_op points to the op that holds the method name */ + + next_op = first_arg; + + /* next_op points to first argument op */ + + next_op = next_op->common.next; + + /* + * Get the method's arguments and put them on the operand stack + */ + status = acpi_ds_create_operands(walk_state, next_op); + if (ACPI_FAILURE(status)) { + break; + } + + /* + * Since the operands will be passed to another control method, + * we must resolve all local references here (Local variables, + * arguments to *this* method, etc.) + */ + status = acpi_ds_resolve_operands(walk_state); + if (ACPI_FAILURE(status)) { + + /* On error, clear all resolved operands */ + + acpi_ds_clear_operands(walk_state); + break; + } + + /* + * Tell the walk loop to preempt this running method and + * execute the new method + */ + status = AE_CTRL_TRANSFER; + + /* + * Return now; we don't want to disturb anything, + * especially the operand count! + */ + return_ACPI_STATUS(status); + + case AML_TYPE_CREATE_FIELD: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Executing CreateField Buffer/Index Op=%p\n", + op)); + + status = acpi_ds_load2_end_op(walk_state); + if (ACPI_FAILURE(status)) { + break; + } + + status = + acpi_ds_eval_buffer_field_operands(walk_state, op); + break; + + case AML_TYPE_CREATE_OBJECT: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Executing CreateObject (Buffer/Package) Op=%p\n", + op)); + + switch (op->common.parent->common.aml_opcode) { + case AML_NAME_OP: + /* + * Put the Node on the object stack (Contains the ACPI Name + * of this object) + */ + walk_state->operands[0] = + (void *)op->common.parent->common.node; + walk_state->num_operands = 1; + + status = acpi_ds_create_node(walk_state, + op->common.parent-> + common.node, + op->common.parent); + if (ACPI_FAILURE(status)) { + break; + } + + /* Fall through */ + /*lint -fallthrough */ + + case AML_INT_EVAL_SUBTREE_OP: + + status = + acpi_ds_eval_data_object_operands + (walk_state, op, + acpi_ns_get_attached_object(op->common. + parent->common. + node)); + break; + + default: + + status = + acpi_ds_eval_data_object_operands + (walk_state, op, NULL); + break; + } + + /* + * If a result object was returned from above, push it on the + * current result stack + */ + if (walk_state->result_obj) { + status = + acpi_ds_result_push(walk_state->result_obj, + walk_state); + } + break; + + case AML_TYPE_NAMED_FIELD: + case AML_TYPE_NAMED_COMPLEX: + case AML_TYPE_NAMED_SIMPLE: + case AML_TYPE_NAMED_NO_OBJ: + + status = acpi_ds_load2_end_op(walk_state); + if (ACPI_FAILURE(status)) { + break; + } + + if (op->common.aml_opcode == AML_REGION_OP) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Executing OpRegion Address/Length Op=%p\n", + op)); + + status = + acpi_ds_eval_region_operands(walk_state, + op); + if (ACPI_FAILURE(status)) { + break; + } + } else if (op->common.aml_opcode == AML_DATA_REGION_OP) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Executing DataTableRegion Strings Op=%p\n", + op)); + + status = + acpi_ds_eval_table_region_operands + (walk_state, op); + if (ACPI_FAILURE(status)) { + break; + } + } else if (op->common.aml_opcode == AML_BANK_FIELD_OP) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Executing BankField Op=%p\n", + op)); + + status = + acpi_ds_eval_bank_field_operands(walk_state, + op); + if (ACPI_FAILURE(status)) { + break; + } + } + break; + + case AML_TYPE_UNDEFINED: + + ACPI_ERROR((AE_INFO, + "Undefined opcode type Op=%p", op)); + return_ACPI_STATUS(AE_NOT_IMPLEMENTED); + + case AML_TYPE_BOGUS: + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Internal opcode=%X type Op=%p\n", + walk_state->opcode, op)); + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unimplemented opcode, class=0x%X type=0x%X Opcode=0x%X Op=%p", + op_class, op_type, op->common.aml_opcode, + op)); + + status = AE_NOT_IMPLEMENTED; + break; + } + } + + /* + * ACPI 2.0 support for 64-bit integers: Truncate numeric + * result value if we are executing from a 32-bit ACPI table + */ + (void)acpi_ex_truncate_for32bit_table(walk_state->result_obj); + + /* + * Check if we just completed the evaluation of a + * conditional predicate + */ + if ((ACPI_SUCCESS(status)) && + (walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_PREDICATE_EXECUTING) && + (walk_state->control_state->control.predicate_op == op)) { + status = + acpi_ds_get_predicate_value(walk_state, + walk_state->result_obj); + walk_state->result_obj = NULL; + } + +cleanup: + + if (walk_state->result_obj) { + + /* Break to debugger to display result */ + + ACPI_DEBUGGER_EXEC(acpi_db_display_result_object + (walk_state->result_obj, walk_state)); + + /* + * Delete the result op if and only if: + * Parent will not use the result -- such as any + * non-nested type2 op in a method (parent will be method) + */ + acpi_ds_delete_result_if_not_used(op, walk_state->result_obj, + walk_state); + } +#ifdef _UNDER_DEVELOPMENT + + if (walk_state->parser_state.aml == walk_state->parser_state.aml_end) { + acpi_db_method_end(walk_state); + } +#endif + + /* Invoke exception handler on error */ + + if (ACPI_FAILURE(status)) { + status = acpi_ds_method_error(status, walk_state); + } + + /* Always clear the object stack */ + + walk_state->num_operands = 0; + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dswload.c b/kernel/drivers/acpi/acpica/dswload.c new file mode 100644 index 000000000..843942fb4 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dswload.c @@ -0,0 +1,570 @@ +/****************************************************************************** + * + * Module Name: dswload - Dispatcher first pass namespace load callbacks + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" + +#ifdef ACPI_ASL_COMPILER +#include "acdisasm.h" +#endif + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dswload") + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_callbacks + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * pass_number - 1, 2, or 3 + * + * RETURN: Status + * + * DESCRIPTION: Init walk state callbacks + * + ******************************************************************************/ +acpi_status +acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number) +{ + + switch (pass_number) { + case 0: + + /* Parse only - caller will setup callbacks */ + + walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 | + ACPI_PARSE_DELETE_TREE | ACPI_PARSE_DISASSEMBLE; + walk_state->descending_callback = NULL; + walk_state->ascending_callback = NULL; + break; + + case 1: + + /* Load pass 1 */ + + walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 | + ACPI_PARSE_DELETE_TREE; + walk_state->descending_callback = acpi_ds_load1_begin_op; + walk_state->ascending_callback = acpi_ds_load1_end_op; + break; + + case 2: + + /* Load pass 2 */ + + walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 | + ACPI_PARSE_DELETE_TREE; + walk_state->descending_callback = acpi_ds_load2_begin_op; + walk_state->ascending_callback = acpi_ds_load2_end_op; + break; + + case 3: + + /* Execution pass */ + +#ifndef ACPI_NO_METHOD_EXECUTION + walk_state->parse_flags |= ACPI_PARSE_EXECUTE | + ACPI_PARSE_DELETE_TREE; + walk_state->descending_callback = acpi_ds_exec_begin_op; + walk_state->ascending_callback = acpi_ds_exec_end_op; +#endif + break; + + default: + + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load1_begin_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * out_op - Where to return op if a new one is created + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + * + ******************************************************************************/ + +acpi_status +acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state, + union acpi_parse_object ** out_op) +{ + union acpi_parse_object *op; + struct acpi_namespace_node *node; + acpi_status status; + acpi_object_type object_type; + char *path; + u32 flags; + + ACPI_FUNCTION_TRACE(ds_load1_begin_op); + + op = walk_state->op; + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op, + walk_state)); + + /* We are only interested in opcodes that have an associated name */ + + if (op) { + if (!(walk_state->op_info->flags & AML_NAMED)) { + *out_op = op; + return_ACPI_STATUS(AE_OK); + } + + /* Check if this object has already been installed in the namespace */ + + if (op->common.node) { + *out_op = op; + return_ACPI_STATUS(AE_OK); + } + } + + path = acpi_ps_get_next_namestring(&walk_state->parser_state); + + /* Map the raw opcode into an internal object type */ + + object_type = walk_state->op_info->object_type; + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "State=%p Op=%p [%s]\n", walk_state, op, + acpi_ut_get_type_name(object_type))); + + switch (walk_state->opcode) { + case AML_SCOPE_OP: + /* + * The target name of the Scope() operator must exist at this point so + * that we can actually open the scope to enter new names underneath it. + * Allow search-to-root for single namesegs. + */ + status = + acpi_ns_lookup(walk_state->scope_info, path, object_type, + ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, + walk_state, &(node)); +#ifdef ACPI_ASL_COMPILER + if (status == AE_NOT_FOUND) { + /* + * Table disassembly: + * Target of Scope() not found. Generate an External for it, and + * insert the name into the namespace. + */ + acpi_dm_add_op_to_external_list(op, path, + ACPI_TYPE_DEVICE, 0, 0); + status = + acpi_ns_lookup(walk_state->scope_info, path, + object_type, ACPI_IMODE_LOAD_PASS1, + ACPI_NS_SEARCH_PARENT, walk_state, + &node); + } +#endif + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(path, status); + return_ACPI_STATUS(status); + } + + /* + * Check to make sure that the target is + * one of the opcodes that actually opens a scope + */ + switch (node->type) { + case ACPI_TYPE_ANY: + case ACPI_TYPE_LOCAL_SCOPE: /* Scope */ + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + /* These are acceptable types */ + break; + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + /* + * These types we will allow, but we will change the type. + * This enables some existing code of the form: + * + * Name (DEB, 0) + * Scope (DEB) { ... } + * + * Note: silently change the type here. On the second pass, + * we will report a warning + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Type override - [%4.4s] had invalid type (%s) " + "for Scope operator, changed to type ANY\n", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type))); + + node->type = ACPI_TYPE_ANY; + walk_state->scope_info->common.value = ACPI_TYPE_ANY; + break; + + case ACPI_TYPE_METHOD: + /* + * Allow scope change to root during execution of module-level + * code. Root is typed METHOD during this time. + */ + if ((node == acpi_gbl_root_node) && + (walk_state-> + parse_flags & ACPI_PARSE_MODULE_LEVEL)) { + break; + } + + /*lint -fallthrough */ + + default: + + /* All other types are an error */ + + ACPI_ERROR((AE_INFO, + "Invalid type (%s) for target of " + "Scope operator [%4.4s] (Cannot override)", + acpi_ut_get_type_name(node->type), + acpi_ut_get_node_name(node))); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + break; + + default: + /* + * For all other named opcodes, we will enter the name into + * the namespace. + * + * Setup the search flags. + * Since we are entering a name into the namespace, we do not want to + * enable the search-to-root upsearch. + * + * There are only two conditions where it is acceptable that the name + * already exists: + * 1) the Scope() operator can reopen a scoping object that was + * previously defined (Scope, Method, Device, etc.) + * 2) Whenever we are parsing a deferred opcode (op_region, Buffer, + * buffer_field, or Package), the name of the object is already + * in the namespace. + */ + if (walk_state->deferred_node) { + + /* This name is already in the namespace, get the node */ + + node = walk_state->deferred_node; + status = AE_OK; + break; + } + + /* + * If we are executing a method, do not create any namespace objects + * during the load phase, only during execution. + */ + if (walk_state->method_node) { + node = NULL; + status = AE_OK; + break; + } + + flags = ACPI_NS_NO_UPSEARCH; + if ((walk_state->opcode != AML_SCOPE_OP) && + (!(walk_state->parse_flags & ACPI_PARSE_DEFERRED_OP))) { + flags |= ACPI_NS_ERROR_IF_FOUND; + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "[%s] Cannot already exist\n", + acpi_ut_get_type_name(object_type))); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "[%s] Both Find or Create allowed\n", + acpi_ut_get_type_name(object_type))); + } + + /* + * Enter the named type into the internal namespace. We enter the name + * as we go downward in the parse tree. Any necessary subobjects that + * involve arguments to the opcode must be created as we go back up the + * parse tree later. + */ + status = + acpi_ns_lookup(walk_state->scope_info, path, object_type, + ACPI_IMODE_LOAD_PASS1, flags, walk_state, + &node); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + + /* The name already exists in this scope */ + + if (node->flags & ANOBJ_IS_EXTERNAL) { + /* + * Allow one create on an object or segment that was + * previously declared External + */ + node->flags &= ~ANOBJ_IS_EXTERNAL; + node->type = (u8) object_type; + + /* Just retyped a node, probably will need to open a scope */ + + if (acpi_ns_opens_scope(object_type)) { + status = + acpi_ds_scope_stack_push + (node, object_type, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS + (status); + } + } + + status = AE_OK; + } + } + + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(path, status); + return_ACPI_STATUS(status); + } + } + break; + } + + /* Common exit */ + + if (!op) { + + /* Create a new op */ + + op = acpi_ps_alloc_op(walk_state->opcode); + if (!op) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + } + + /* Initialize the op */ + +#if (defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY)) + op->named.path = ACPI_CAST_PTR(u8, path); +#endif + + if (node) { + /* + * Put the Node in the "op" object that the parser uses, so we + * can get it again quickly when this scope is closed + */ + op->common.node = node; + op->named.name = node->name.integer; + } + + acpi_ps_append_arg(acpi_ps_get_parent_scope(&walk_state->parser_state), + op); + *out_op = op; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load1_end_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the loading of the namespace, + * both control methods and everything else. + * + ******************************************************************************/ + +acpi_status acpi_ds_load1_end_op(struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *op; + acpi_object_type object_type; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ds_load1_end_op); + + op = walk_state->op; + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op, + walk_state)); + + /* We are only interested in opcodes that have an associated name */ + + if (!(walk_state->op_info->flags & (AML_NAMED | AML_FIELD))) { + return_ACPI_STATUS(AE_OK); + } + + /* Get the object type to determine if we should pop the scope */ + + object_type = walk_state->op_info->object_type; + +#ifndef ACPI_NO_METHOD_EXECUTION + if (walk_state->op_info->flags & AML_FIELD) { + /* + * If we are executing a method, do not create any namespace objects + * during the load phase, only during execution. + */ + if (!walk_state->method_node) { + if (walk_state->opcode == AML_FIELD_OP || + walk_state->opcode == AML_BANK_FIELD_OP || + walk_state->opcode == AML_INDEX_FIELD_OP) { + status = + acpi_ds_init_field_objects(op, walk_state); + } + } + return_ACPI_STATUS(status); + } + + /* + * If we are executing a method, do not create any namespace objects + * during the load phase, only during execution. + */ + if (!walk_state->method_node) { + if (op->common.aml_opcode == AML_REGION_OP) { + status = + acpi_ex_create_region(op->named.data, + op->named.length, + (acpi_adr_space_type) ((op-> + common. + value. + arg)-> + common. + value. + integer), + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } else if (op->common.aml_opcode == AML_DATA_REGION_OP) { + status = + acpi_ex_create_region(op->named.data, + op->named.length, + ACPI_ADR_SPACE_DATA_TABLE, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } +#endif + + if (op->common.aml_opcode == AML_NAME_OP) { + + /* For Name opcode, get the object type from the argument */ + + if (op->common.value.arg) { + object_type = (acpi_ps_get_opcode_info((op->common. + value.arg)-> + common. + aml_opcode))-> + object_type; + + /* Set node type if we have a namespace node */ + + if (op->common.node) { + op->common.node->type = (u8) object_type; + } + } + } + + /* + * If we are executing a method, do not create any namespace objects + * during the load phase, only during execution. + */ + if (!walk_state->method_node) { + if (op->common.aml_opcode == AML_METHOD_OP) { + /* + * method_op pkg_length name_string method_flags term_list + * + * Note: We must create the method node/object pair as soon as we + * see the method declaration. This allows later pass1 parsing + * of invocations of the method (need to know the number of + * arguments.) + */ + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "LOADING-Method: State=%p Op=%p NamedObj=%p\n", + walk_state, op, op->named.node)); + + if (!acpi_ns_get_attached_object(op->named.node)) { + walk_state->operands[0] = + ACPI_CAST_PTR(void, op->named.node); + walk_state->num_operands = 1; + + status = + acpi_ds_create_operands(walk_state, + op->common.value. + arg); + if (ACPI_SUCCESS(status)) { + status = + acpi_ex_create_method(op->named. + data, + op->named. + length, + walk_state); + } + + walk_state->operands[0] = NULL; + walk_state->num_operands = 0; + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } + } + + /* Pop the scope stack (only if loading a table) */ + + if (!walk_state->method_node && acpi_ns_opens_scope(object_type)) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "(%s): Popping scope for Op %p\n", + acpi_ut_get_type_name(object_type), op)); + + status = acpi_ds_scope_stack_pop(walk_state); + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dswload2.c b/kernel/drivers/acpi/acpica/dswload2.c new file mode 100644 index 000000000..fcaa30c61 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dswload2.c @@ -0,0 +1,738 @@ +/****************************************************************************** + * + * Module Name: dswload2 - Dispatcher second pass namespace load callbacks + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acevents.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dswload2") + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load2_begin_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * out_op - Wher to return op if a new one is created + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + * + ******************************************************************************/ +acpi_status +acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op) +{ + union acpi_parse_object *op; + struct acpi_namespace_node *node; + acpi_status status; + acpi_object_type object_type; + char *buffer_ptr; + u32 flags; + + ACPI_FUNCTION_TRACE(ds_load2_begin_op); + + op = walk_state->op; + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op, + walk_state)); + + if (op) { + if ((walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_CONDITIONAL_EXECUTING)) { + + /* We are executing a while loop outside of a method */ + + status = acpi_ds_exec_begin_op(walk_state, out_op); + return_ACPI_STATUS(status); + } + + /* We only care about Namespace opcodes here */ + + if ((!(walk_state->op_info->flags & AML_NSOPCODE) && + (walk_state->opcode != AML_INT_NAMEPATH_OP)) || + (!(walk_state->op_info->flags & AML_NAMED))) { + return_ACPI_STATUS(AE_OK); + } + + /* Get the name we are going to enter or lookup in the namespace */ + + if (walk_state->opcode == AML_INT_NAMEPATH_OP) { + + /* For Namepath op, get the path string */ + + buffer_ptr = op->common.value.string; + if (!buffer_ptr) { + + /* No name, just exit */ + + return_ACPI_STATUS(AE_OK); + } + } else { + /* Get name from the op */ + + buffer_ptr = ACPI_CAST_PTR(char, &op->named.name); + } + } else { + /* Get the namestring from the raw AML */ + + buffer_ptr = + acpi_ps_get_next_namestring(&walk_state->parser_state); + } + + /* Map the opcode into an internal object type */ + + object_type = walk_state->op_info->object_type; + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "State=%p Op=%p Type=%X\n", walk_state, op, + object_type)); + + switch (walk_state->opcode) { + case AML_FIELD_OP: + case AML_BANK_FIELD_OP: + case AML_INDEX_FIELD_OP: + + node = NULL; + status = AE_OK; + break; + + case AML_INT_NAMEPATH_OP: + /* + * The name_path is an object reference to an existing object. + * Don't enter the name into the namespace, but look it up + * for use later. + */ + status = + acpi_ns_lookup(walk_state->scope_info, buffer_ptr, + object_type, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, &(node)); + break; + + case AML_SCOPE_OP: + + /* Special case for Scope(\) -> refers to the Root node */ + + if (op && (op->named.node == acpi_gbl_root_node)) { + node = op->named.node; + + status = + acpi_ds_scope_stack_push(node, object_type, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } else { + /* + * The Path is an object reference to an existing object. + * Don't enter the name into the namespace, but look it up + * for use later. + */ + status = + acpi_ns_lookup(walk_state->scope_info, buffer_ptr, + object_type, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, + &(node)); + if (ACPI_FAILURE(status)) { +#ifdef ACPI_ASL_COMPILER + if (status == AE_NOT_FOUND) { + status = AE_OK; + } else { + ACPI_ERROR_NAMESPACE(buffer_ptr, + status); + } +#else + ACPI_ERROR_NAMESPACE(buffer_ptr, status); +#endif + return_ACPI_STATUS(status); + } + } + + /* + * We must check to make sure that the target is + * one of the opcodes that actually opens a scope + */ + switch (node->type) { + case ACPI_TYPE_ANY: + case ACPI_TYPE_LOCAL_SCOPE: /* Scope */ + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + /* These are acceptable types */ + break; + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* + * These types we will allow, but we will change the type. + * This enables some existing code of the form: + * + * Name (DEB, 0) + * Scope (DEB) { ... } + */ + ACPI_WARNING((AE_INFO, + "Type override - [%4.4s] had invalid type (%s) " + "for Scope operator, changed to type ANY", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type))); + + node->type = ACPI_TYPE_ANY; + walk_state->scope_info->common.value = ACPI_TYPE_ANY; + break; + + case ACPI_TYPE_METHOD: + + /* + * Allow scope change to root during execution of module-level + * code. Root is typed METHOD during this time. + */ + if ((node == acpi_gbl_root_node) && + (walk_state-> + parse_flags & ACPI_PARSE_MODULE_LEVEL)) { + break; + } + + /*lint -fallthrough */ + + default: + + /* All other types are an error */ + + ACPI_ERROR((AE_INFO, + "Invalid type (%s) for target of " + "Scope operator [%4.4s] (Cannot override)", + acpi_ut_get_type_name(node->type), + acpi_ut_get_node_name(node))); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + break; + + default: + + /* All other opcodes */ + + if (op && op->common.node) { + + /* This op/node was previously entered into the namespace */ + + node = op->common.node; + + if (acpi_ns_opens_scope(object_type)) { + status = + acpi_ds_scope_stack_push(node, object_type, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + return_ACPI_STATUS(AE_OK); + } + + /* + * Enter the named type into the internal namespace. We enter the name + * as we go downward in the parse tree. Any necessary subobjects that + * involve arguments to the opcode must be created as we go back up the + * parse tree later. + * + * Note: Name may already exist if we are executing a deferred opcode. + */ + if (walk_state->deferred_node) { + + /* This name is already in the namespace, get the node */ + + node = walk_state->deferred_node; + status = AE_OK; + break; + } + + flags = ACPI_NS_NO_UPSEARCH; + if (walk_state->pass_number == ACPI_IMODE_EXECUTE) { + + /* Execution mode, node cannot already exist, node is temporary */ + + flags |= ACPI_NS_ERROR_IF_FOUND; + + if (! + (walk_state-> + parse_flags & ACPI_PARSE_MODULE_LEVEL)) { + flags |= ACPI_NS_TEMPORARY; + } + } + + /* Add new entry or lookup existing entry */ + + status = + acpi_ns_lookup(walk_state->scope_info, buffer_ptr, + object_type, ACPI_IMODE_LOAD_PASS2, flags, + walk_state, &node); + + if (ACPI_SUCCESS(status) && (flags & ACPI_NS_TEMPORARY)) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "***New Node [%4.4s] %p is temporary\n", + acpi_ut_get_node_name(node), node)); + } + break; + } + + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(buffer_ptr, status); + return_ACPI_STATUS(status); + } + + if (!op) { + + /* Create a new op */ + + op = acpi_ps_alloc_op(walk_state->opcode); + if (!op) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Initialize the new op */ + + if (node) { + op->named.name = node->name.integer; + } + *out_op = op; + } + + /* + * Put the Node in the "op" object that the parser uses, so we + * can get it again quickly when this scope is closed + */ + op->common.node = node; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load2_end_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the loading of the namespace, + * both control methods and everything else. + * + ******************************************************************************/ + +acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *op; + acpi_status status = AE_OK; + acpi_object_type object_type; + struct acpi_namespace_node *node; + union acpi_parse_object *arg; + struct acpi_namespace_node *new_node; +#ifndef ACPI_NO_METHOD_EXECUTION + u32 i; + u8 region_space; +#endif + + ACPI_FUNCTION_TRACE(ds_load2_end_op); + + op = walk_state->op; + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Opcode [%s] Op %p State %p\n", + walk_state->op_info->name, op, walk_state)); + + /* Check if opcode had an associated namespace object */ + + if (!(walk_state->op_info->flags & AML_NSOBJECT)) { + return_ACPI_STATUS(AE_OK); + } + + if (op->common.aml_opcode == AML_SCOPE_OP) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Ending scope Op=%p State=%p\n", op, + walk_state)); + } + + object_type = walk_state->op_info->object_type; + + /* + * Get the Node/name from the earlier lookup + * (It was saved in the *op structure) + */ + node = op->common.node; + + /* + * Put the Node on the object stack (Contains the ACPI Name of + * this object) + */ + walk_state->operands[0] = (void *)node; + walk_state->num_operands = 1; + + /* Pop the scope stack */ + + if (acpi_ns_opens_scope(object_type) && + (op->common.aml_opcode != AML_INT_METHODCALL_OP)) { + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "(%s) Popping scope for Op %p\n", + acpi_ut_get_type_name(object_type), op)); + + status = acpi_ds_scope_stack_pop(walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + } + + /* + * Named operations are as follows: + * + * AML_ALIAS + * AML_BANKFIELD + * AML_CREATEBITFIELD + * AML_CREATEBYTEFIELD + * AML_CREATEDWORDFIELD + * AML_CREATEFIELD + * AML_CREATEQWORDFIELD + * AML_CREATEWORDFIELD + * AML_DATA_REGION + * AML_DEVICE + * AML_EVENT + * AML_FIELD + * AML_INDEXFIELD + * AML_METHOD + * AML_METHODCALL + * AML_MUTEX + * AML_NAME + * AML_NAMEDFIELD + * AML_OPREGION + * AML_POWERRES + * AML_PROCESSOR + * AML_SCOPE + * AML_THERMALZONE + */ + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "Create-Load [%s] State=%p Op=%p NamedObj=%p\n", + acpi_ps_get_opcode_name(op->common.aml_opcode), + walk_state, op, node)); + + /* Decode the opcode */ + + arg = op->common.value.arg; + + switch (walk_state->op_info->type) { +#ifndef ACPI_NO_METHOD_EXECUTION + + case AML_TYPE_CREATE_FIELD: + /* + * Create the field object, but the field buffer and index must + * be evaluated later during the execution phase + */ + status = acpi_ds_create_buffer_field(op, walk_state); + break; + + case AML_TYPE_NAMED_FIELD: + /* + * If we are executing a method, initialize the field + */ + if (walk_state->method_node) { + status = acpi_ds_init_field_objects(op, walk_state); + } + + switch (op->common.aml_opcode) { + case AML_INDEX_FIELD_OP: + + status = + acpi_ds_create_index_field(op, + (acpi_handle) arg-> + common.node, walk_state); + break; + + case AML_BANK_FIELD_OP: + + status = + acpi_ds_create_bank_field(op, arg->common.node, + walk_state); + break; + + case AML_FIELD_OP: + + status = + acpi_ds_create_field(op, arg->common.node, + walk_state); + break; + + default: + + /* All NAMED_FIELD opcodes must be handled above */ + break; + } + break; + + case AML_TYPE_NAMED_SIMPLE: + + status = acpi_ds_create_operands(walk_state, arg); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + switch (op->common.aml_opcode) { + case AML_PROCESSOR_OP: + + status = acpi_ex_create_processor(walk_state); + break; + + case AML_POWER_RES_OP: + + status = acpi_ex_create_power_resource(walk_state); + break; + + case AML_MUTEX_OP: + + status = acpi_ex_create_mutex(walk_state); + break; + + case AML_EVENT_OP: + + status = acpi_ex_create_event(walk_state); + break; + + case AML_ALIAS_OP: + + status = acpi_ex_create_alias(walk_state); + break; + + default: + + /* Unknown opcode */ + + status = AE_OK; + goto cleanup; + } + + /* Delete operands */ + + for (i = 1; i < walk_state->num_operands; i++) { + acpi_ut_remove_reference(walk_state->operands[i]); + walk_state->operands[i] = NULL; + } + + break; +#endif /* ACPI_NO_METHOD_EXECUTION */ + + case AML_TYPE_NAMED_COMPLEX: + + switch (op->common.aml_opcode) { +#ifndef ACPI_NO_METHOD_EXECUTION + case AML_REGION_OP: + case AML_DATA_REGION_OP: + + if (op->common.aml_opcode == AML_REGION_OP) { + region_space = (acpi_adr_space_type) + ((op->common.value.arg)->common.value. + integer); + } else { + region_space = ACPI_ADR_SPACE_DATA_TABLE; + } + + /* + * The op_region is not fully parsed at this time. The only valid + * argument is the space_id. (We must save the address of the + * AML of the address and length operands) + * + * If we have a valid region, initialize it. The namespace is + * unlocked at this point. + * + * Need to unlock interpreter if it is locked (if we are running + * a control method), in order to allow _REG methods to be run + * during acpi_ev_initialize_region. + */ + if (walk_state->method_node) { + /* + * Executing a method: initialize the region and unlock + * the interpreter + */ + status = + acpi_ex_create_region(op->named.data, + op->named.length, + region_space, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_ex_exit_interpreter(); + } + + status = + acpi_ev_initialize_region + (acpi_ns_get_attached_object(node), FALSE); + if (walk_state->method_node) { + acpi_ex_enter_interpreter(); + } + + if (ACPI_FAILURE(status)) { + /* + * If AE_NOT_EXIST is returned, it is not fatal + * because many regions get created before a handler + * is installed for said region. + */ + if (AE_NOT_EXIST == status) { + status = AE_OK; + } + } + break; + + case AML_NAME_OP: + + status = acpi_ds_create_node(walk_state, node, op); + break; + + case AML_METHOD_OP: + /* + * method_op pkg_length name_string method_flags term_list + * + * Note: We must create the method node/object pair as soon as we + * see the method declaration. This allows later pass1 parsing + * of invocations of the method (need to know the number of + * arguments.) + */ + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "LOADING-Method: State=%p Op=%p NamedObj=%p\n", + walk_state, op, op->named.node)); + + if (!acpi_ns_get_attached_object(op->named.node)) { + walk_state->operands[0] = + ACPI_CAST_PTR(void, op->named.node); + walk_state->num_operands = 1; + + status = + acpi_ds_create_operands(walk_state, + op->common.value. + arg); + if (ACPI_SUCCESS(status)) { + status = + acpi_ex_create_method(op->named. + data, + op->named. + length, + walk_state); + } + walk_state->operands[0] = NULL; + walk_state->num_operands = 0; + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + break; + +#endif /* ACPI_NO_METHOD_EXECUTION */ + + default: + + /* All NAMED_COMPLEX opcodes must be handled above */ + break; + } + break; + + case AML_CLASS_INTERNAL: + + /* case AML_INT_NAMEPATH_OP: */ + break; + + case AML_CLASS_METHOD_CALL: + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "RESOLVING-MethodCall: State=%p Op=%p NamedObj=%p\n", + walk_state, op, node)); + + /* + * Lookup the method name and save the Node + */ + status = + acpi_ns_lookup(walk_state->scope_info, + arg->common.value.string, ACPI_TYPE_ANY, + ACPI_IMODE_LOAD_PASS2, + ACPI_NS_SEARCH_PARENT | + ACPI_NS_DONT_OPEN_SCOPE, walk_state, + &(new_node)); + if (ACPI_SUCCESS(status)) { + /* + * Make sure that what we found is indeed a method + * We didn't search for a method on purpose, to see if the name + * would resolve + */ + if (new_node->type != ACPI_TYPE_METHOD) { + status = AE_AML_OPERAND_TYPE; + } + + /* We could put the returned object (Node) on the object stack for + * later, but for now, we will put it in the "op" object that the + * parser uses, so we can get it again at the end of this scope + */ + op->common.node = new_node; + } else { + ACPI_ERROR_NAMESPACE(arg->common.value.string, status); + } + break; + + default: + + break; + } + +cleanup: + + /* Remove the Node pushed at the very beginning */ + + walk_state->operands[0] = NULL; + walk_state->num_operands = 0; + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/dswscope.c b/kernel/drivers/acpi/acpica/dswscope.c new file mode 100644 index 000000000..43b3ea40c --- /dev/null +++ b/kernel/drivers/acpi/acpica/dswscope.c @@ -0,0 +1,214 @@ +/****************************************************************************** + * + * Module Name: dswscope - Scope stack manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dswscope") + +/**************************************************************************** + * + * FUNCTION: acpi_ds_scope_stack_clear + * + * PARAMETERS: walk_state - Current state + * + * RETURN: None + * + * DESCRIPTION: Pop (and free) everything on the scope stack except the + * root scope object (which remains at the stack top.) + * + ***************************************************************************/ +void acpi_ds_scope_stack_clear(struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *scope_info; + + ACPI_FUNCTION_NAME(ds_scope_stack_clear); + + while (walk_state->scope_info) { + + /* Pop a scope off the stack */ + + scope_info = walk_state->scope_info; + walk_state->scope_info = scope_info->scope.next; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Popped object type (%s)\n", + acpi_ut_get_type_name(scope_info->common. + value))); + acpi_ut_delete_generic_state(scope_info); + } +} + +/**************************************************************************** + * + * FUNCTION: acpi_ds_scope_stack_push + * + * PARAMETERS: node - Name to be made current + * type - Type of frame being pushed + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Push the current scope on the scope stack, and make the + * passed Node current. + * + ***************************************************************************/ + +acpi_status +acpi_ds_scope_stack_push(struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *scope_info; + union acpi_generic_state *old_scope_info; + + ACPI_FUNCTION_TRACE(ds_scope_stack_push); + + if (!node) { + + /* Invalid scope */ + + ACPI_ERROR((AE_INFO, "Null scope parameter")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Make sure object type is valid */ + + if (!acpi_ut_valid_object_type(type)) { + ACPI_WARNING((AE_INFO, "Invalid object type: 0x%X", type)); + } + + /* Allocate a new scope object */ + + scope_info = acpi_ut_create_generic_state(); + if (!scope_info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Init new scope object */ + + scope_info->common.descriptor_type = ACPI_DESC_TYPE_STATE_WSCOPE; + scope_info->scope.node = node; + scope_info->common.value = (u16) type; + + walk_state->scope_depth++; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[%.2d] Pushed scope ", + (u32) walk_state->scope_depth)); + + old_scope_info = walk_state->scope_info; + if (old_scope_info) { + ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, + "[%4.4s] (%s)", + acpi_ut_get_node_name(old_scope_info-> + scope.node), + acpi_ut_get_type_name(old_scope_info-> + common.value))); + } else { + ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "[\\___] (%s)", "ROOT")); + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, + ", New scope -> [%4.4s] (%s)\n", + acpi_ut_get_node_name(scope_info->scope.node), + acpi_ut_get_type_name(scope_info->common.value))); + + /* Push new scope object onto stack */ + + acpi_ut_push_generic_state(&walk_state->scope_info, scope_info); + return_ACPI_STATUS(AE_OK); +} + +/**************************************************************************** + * + * FUNCTION: acpi_ds_scope_stack_pop + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Pop the scope stack once. + * + ***************************************************************************/ + +acpi_status acpi_ds_scope_stack_pop(struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *scope_info; + union acpi_generic_state *new_scope_info; + + ACPI_FUNCTION_TRACE(ds_scope_stack_pop); + + /* + * Pop scope info object off the stack. + */ + scope_info = acpi_ut_pop_generic_state(&walk_state->scope_info); + if (!scope_info) { + return_ACPI_STATUS(AE_STACK_UNDERFLOW); + } + + walk_state->scope_depth--; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[%.2d] Popped scope [%4.4s] (%s), New scope -> ", + (u32) walk_state->scope_depth, + acpi_ut_get_node_name(scope_info->scope.node), + acpi_ut_get_type_name(scope_info->common.value))); + + new_scope_info = walk_state->scope_info; + if (new_scope_info) { + ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, + "[%4.4s] (%s)\n", + acpi_ut_get_node_name(new_scope_info-> + scope.node), + acpi_ut_get_type_name(new_scope_info-> + common.value))); + } else { + ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "[\\___] (ROOT)\n")); + } + + acpi_ut_delete_generic_state(scope_info); + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/dswstate.c b/kernel/drivers/acpi/acpica/dswstate.c new file mode 100644 index 000000000..89ac20224 --- /dev/null +++ b/kernel/drivers/acpi/acpica/dswstate.c @@ -0,0 +1,757 @@ +/****************************************************************************** + * + * Module Name: dswstate - Dispatcher parse tree walk management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_DISPATCHER +ACPI_MODULE_NAME("dswstate") + + /* Local prototypes */ +static acpi_status +acpi_ds_result_stack_push(struct acpi_walk_state *walk_state); +static acpi_status acpi_ds_result_stack_pop(struct acpi_walk_state *walk_state); + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_pop + * + * PARAMETERS: object - Where to return the popped object + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop an object off the top of this walk's result stack + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_pop(union acpi_operand_object **object, + struct acpi_walk_state *walk_state) +{ + u32 index; + union acpi_generic_state *state; + acpi_status status; + + ACPI_FUNCTION_NAME(ds_result_pop); + + state = walk_state->results; + + /* Incorrect state of result stack */ + + if (state && !walk_state->result_count) { + ACPI_ERROR((AE_INFO, "No results on result stack")); + return (AE_AML_INTERNAL); + } + + if (!state && walk_state->result_count) { + ACPI_ERROR((AE_INFO, "No result state for result stack")); + return (AE_AML_INTERNAL); + } + + /* Empty result stack */ + + if (!state) { + ACPI_ERROR((AE_INFO, "Result stack is empty! State=%p", + walk_state)); + return (AE_AML_NO_RETURN_VALUE); + } + + /* Return object of the top element and clean that top element result stack */ + + walk_state->result_count--; + index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; + + *object = state->results.obj_desc[index]; + if (!*object) { + ACPI_ERROR((AE_INFO, + "No result objects on result stack, State=%p", + walk_state)); + return (AE_AML_NO_RETURN_VALUE); + } + + state->results.obj_desc[index] = NULL; + if (index == 0) { + status = acpi_ds_result_stack_pop(walk_state); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Obj=%p [%s] Index=%X State=%p Num=%X\n", *object, + acpi_ut_get_object_type_name(*object), + index, walk_state, walk_state->result_count)); + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_push + * + * PARAMETERS: object - Where to return the popped object + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto the current result stack + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_push(union acpi_operand_object * object, + struct acpi_walk_state * walk_state) +{ + union acpi_generic_state *state; + acpi_status status; + u32 index; + + ACPI_FUNCTION_NAME(ds_result_push); + + if (walk_state->result_count > walk_state->result_size) { + ACPI_ERROR((AE_INFO, "Result stack is full")); + return (AE_AML_INTERNAL); + } else if (walk_state->result_count == walk_state->result_size) { + + /* Extend the result stack */ + + status = acpi_ds_result_stack_push(walk_state); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Failed to extend the result stack")); + return (status); + } + } + + if (!(walk_state->result_count < walk_state->result_size)) { + ACPI_ERROR((AE_INFO, "No free elements in result stack")); + return (AE_AML_INTERNAL); + } + + state = walk_state->results; + if (!state) { + ACPI_ERROR((AE_INFO, "No result stack frame during push")); + return (AE_AML_INTERNAL); + } + + if (!object) { + ACPI_ERROR((AE_INFO, + "Null Object! Obj=%p State=%p Num=%u", + object, walk_state, walk_state->result_count)); + return (AE_BAD_PARAMETER); + } + + /* Assign the address of object to the top free element of result stack */ + + index = (u32)walk_state->result_count % ACPI_RESULTS_FRAME_OBJ_NUM; + state->results.obj_desc[index] = object; + walk_state->result_count++; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Obj=%p [%s] State=%p Num=%X Cur=%X\n", + object, + acpi_ut_get_object_type_name((union + acpi_operand_object *) + object), walk_state, + walk_state->result_count, + walk_state->current_result)); + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_stack_push + * + * PARAMETERS: walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto the walk_state result stack + * + ******************************************************************************/ + +static acpi_status acpi_ds_result_stack_push(struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_NAME(ds_result_stack_push); + + /* Check for stack overflow */ + + if (((u32) walk_state->result_size + ACPI_RESULTS_FRAME_OBJ_NUM) > + ACPI_RESULTS_OBJ_NUM_MAX) { + ACPI_ERROR((AE_INFO, "Result stack overflow: State=%p Num=%u", + walk_state, walk_state->result_size)); + return (AE_STACK_OVERFLOW); + } + + state = acpi_ut_create_generic_state(); + if (!state) { + return (AE_NO_MEMORY); + } + + state->common.descriptor_type = ACPI_DESC_TYPE_STATE_RESULT; + acpi_ut_push_generic_state(&walk_state->results, state); + + /* Increase the length of the result stack by the length of frame */ + + walk_state->result_size += ACPI_RESULTS_FRAME_OBJ_NUM; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Results=%p State=%p\n", + state, walk_state)); + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_stack_pop + * + * PARAMETERS: walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop an object off of the walk_state result stack + * + ******************************************************************************/ + +static acpi_status acpi_ds_result_stack_pop(struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_NAME(ds_result_stack_pop); + + /* Check for stack underflow */ + + if (walk_state->results == NULL) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Result stack underflow - State=%p\n", + walk_state)); + return (AE_AML_NO_OPERAND); + } + + if (walk_state->result_size < ACPI_RESULTS_FRAME_OBJ_NUM) { + ACPI_ERROR((AE_INFO, "Insufficient result stack size")); + return (AE_AML_INTERNAL); + } + + state = acpi_ut_pop_generic_state(&walk_state->results); + acpi_ut_delete_generic_state(state); + + /* Decrease the length of result stack by the length of frame */ + + walk_state->result_size -= ACPI_RESULTS_FRAME_OBJ_NUM; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Result=%p RemainingResults=%X State=%p\n", + state, walk_state->result_count, walk_state)); + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_push + * + * PARAMETERS: object - Object to push + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto this walk's object/operand stack + * + ******************************************************************************/ + +acpi_status +acpi_ds_obj_stack_push(void *object, struct acpi_walk_state * walk_state) +{ + ACPI_FUNCTION_NAME(ds_obj_stack_push); + + /* Check for stack overflow */ + + if (walk_state->num_operands >= ACPI_OBJ_NUM_OPERANDS) { + ACPI_ERROR((AE_INFO, + "Object stack overflow! Obj=%p State=%p #Ops=%u", + object, walk_state, walk_state->num_operands)); + return (AE_STACK_OVERFLOW); + } + + /* Put the object onto the stack */ + + walk_state->operands[walk_state->operand_index] = object; + walk_state->num_operands++; + + /* For the usual order of filling the operand stack */ + + walk_state->operand_index++; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Obj=%p [%s] State=%p #Ops=%X\n", + object, + acpi_ut_get_object_type_name((union + acpi_operand_object *) + object), walk_state, + walk_state->num_operands)); + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_pop + * + * PARAMETERS: pop_count - Number of objects/entries to pop + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT + * deleted by this routine. + * + ******************************************************************************/ + +acpi_status +acpi_ds_obj_stack_pop(u32 pop_count, struct acpi_walk_state * walk_state) +{ + u32 i; + + ACPI_FUNCTION_NAME(ds_obj_stack_pop); + + for (i = 0; i < pop_count; i++) { + + /* Check for stack underflow */ + + if (walk_state->num_operands == 0) { + ACPI_ERROR((AE_INFO, + "Object stack underflow! Count=%X State=%p #Ops=%u", + pop_count, walk_state, + walk_state->num_operands)); + return (AE_STACK_UNDERFLOW); + } + + /* Just set the stack entry to null */ + + walk_state->num_operands--; + walk_state->operands[walk_state->num_operands] = NULL; + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%u\n", + pop_count, walk_state, walk_state->num_operands)); + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_pop_and_delete + * + * PARAMETERS: pop_count - Number of objects/entries to pop + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack and delete each object that is + * popped off. + * + ******************************************************************************/ + +void +acpi_ds_obj_stack_pop_and_delete(u32 pop_count, + struct acpi_walk_state *walk_state) +{ + s32 i; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_NAME(ds_obj_stack_pop_and_delete); + + if (pop_count == 0) { + return; + } + + for (i = (s32) pop_count - 1; i >= 0; i--) { + if (walk_state->num_operands == 0) { + return; + } + + /* Pop the stack and delete an object if present in this stack entry */ + + walk_state->num_operands--; + obj_desc = walk_state->operands[i]; + if (obj_desc) { + acpi_ut_remove_reference(walk_state->operands[i]); + walk_state->operands[i] = NULL; + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%X\n", + pop_count, walk_state, walk_state->num_operands)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_current_walk_state + * + * PARAMETERS: thread - Get current active state for this Thread + * + * RETURN: Pointer to the current walk state + * + * DESCRIPTION: Get the walk state that is at the head of the list (the "current" + * walk state.) + * + ******************************************************************************/ + +struct acpi_walk_state *acpi_ds_get_current_walk_state(struct acpi_thread_state + *thread) +{ + ACPI_FUNCTION_NAME(ds_get_current_walk_state); + + if (!thread) { + return (NULL); + } + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "Current WalkState %p\n", + thread->walk_state_list)); + + return (thread->walk_state_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_push_walk_state + * + * PARAMETERS: walk_state - State to push + * thread - Thread state object + * + * RETURN: None + * + * DESCRIPTION: Place the Thread state at the head of the state list + * + ******************************************************************************/ + +void +acpi_ds_push_walk_state(struct acpi_walk_state *walk_state, + struct acpi_thread_state *thread) +{ + ACPI_FUNCTION_TRACE(ds_push_walk_state); + + walk_state->next = thread->walk_state_list; + thread->walk_state_list = walk_state; + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_pop_walk_state + * + * PARAMETERS: thread - Current thread state + * + * RETURN: A walk_state object popped from the thread's stack + * + * DESCRIPTION: Remove and return the walkstate object that is at the head of + * the walk stack for the given walk list. NULL indicates that + * the list is empty. + * + ******************************************************************************/ + +struct acpi_walk_state *acpi_ds_pop_walk_state(struct acpi_thread_state *thread) +{ + struct acpi_walk_state *walk_state; + + ACPI_FUNCTION_TRACE(ds_pop_walk_state); + + walk_state = thread->walk_state_list; + + if (walk_state) { + + /* Next walk state becomes the current walk state */ + + thread->walk_state_list = walk_state->next; + + /* + * Don't clear the NEXT field, this serves as an indicator + * that there is a parent WALK STATE + * Do Not: walk_state->Next = NULL; + */ + } + + return_PTR(walk_state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_walk_state + * + * PARAMETERS: owner_id - ID for object creation + * origin - Starting point for this walk + * method_desc - Method object + * thread - Current thread state + * + * RETURN: Pointer to the new walk state. + * + * DESCRIPTION: Allocate and initialize a new walk state. The current walk + * state is set to this new state. + * + ******************************************************************************/ + +struct acpi_walk_state *acpi_ds_create_walk_state(acpi_owner_id owner_id, + union acpi_parse_object + *origin, + union acpi_operand_object + *method_desc, + struct acpi_thread_state + *thread) +{ + struct acpi_walk_state *walk_state; + + ACPI_FUNCTION_TRACE(ds_create_walk_state); + + walk_state = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_walk_state)); + if (!walk_state) { + return_PTR(NULL); + } + + walk_state->descriptor_type = ACPI_DESC_TYPE_WALK; + walk_state->method_desc = method_desc; + walk_state->owner_id = owner_id; + walk_state->origin = origin; + walk_state->thread = thread; + + walk_state->parser_state.start_op = origin; + + /* Init the method args/local */ + +#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) + acpi_ds_method_data_init(walk_state); +#endif + + /* Put the new state at the head of the walk list */ + + if (thread) { + acpi_ds_push_walk_state(walk_state, thread); + } + + return_PTR(walk_state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_aml_walk + * + * PARAMETERS: walk_state - New state to be initialized + * op - Current parse op + * method_node - Control method NS node, if any + * aml_start - Start of AML + * aml_length - Length of AML + * info - Method info block (params, etc.) + * pass_number - 1, 2, or 3 + * + * RETURN: Status + * + * DESCRIPTION: Initialize a walk state for a pass 1 or 2 parse tree walk + * + ******************************************************************************/ + +acpi_status +acpi_ds_init_aml_walk(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + struct acpi_namespace_node *method_node, + u8 * aml_start, + u32 aml_length, + struct acpi_evaluate_info *info, u8 pass_number) +{ + acpi_status status; + struct acpi_parse_state *parser_state = &walk_state->parser_state; + union acpi_parse_object *extra_op; + + ACPI_FUNCTION_TRACE(ds_init_aml_walk); + + walk_state->parser_state.aml = + walk_state->parser_state.aml_start = aml_start; + walk_state->parser_state.aml_end = + walk_state->parser_state.pkg_end = aml_start + aml_length; + + /* The next_op of the next_walk will be the beginning of the method */ + + walk_state->next_op = NULL; + walk_state->pass_number = pass_number; + + if (info) { + walk_state->params = info->parameters; + walk_state->caller_return_desc = &info->return_object; + } + + status = acpi_ps_init_scope(&walk_state->parser_state, op); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (method_node) { + walk_state->parser_state.start_node = method_node; + walk_state->walk_type = ACPI_WALK_METHOD; + walk_state->method_node = method_node; + walk_state->method_desc = + acpi_ns_get_attached_object(method_node); + + /* Push start scope on scope stack and make it current */ + + status = + acpi_ds_scope_stack_push(method_node, ACPI_TYPE_METHOD, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Init the method arguments */ + + status = acpi_ds_method_data_init_args(walk_state->params, + ACPI_METHOD_NUM_ARGS, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } else { + /* + * Setup the current scope. + * Find a Named Op that has a namespace node associated with it. + * search upwards from this Op. Current scope is the first + * Op with a namespace node. + */ + extra_op = parser_state->start_op; + while (extra_op && !extra_op->common.node) { + extra_op = extra_op->common.parent; + } + + if (!extra_op) { + parser_state->start_node = NULL; + } else { + parser_state->start_node = extra_op->common.node; + } + + if (parser_state->start_node) { + + /* Push start scope on scope stack and make it current */ + + status = + acpi_ds_scope_stack_push(parser_state->start_node, + parser_state->start_node-> + type, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } + + status = acpi_ds_init_callbacks(walk_state, pass_number); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ds_delete_walk_state + * + * PARAMETERS: walk_state - State to delete + * + * RETURN: Status + * + * DESCRIPTION: Delete a walk state including all internal data structures + * + ******************************************************************************/ + +void acpi_ds_delete_walk_state(struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_TRACE_PTR(ds_delete_walk_state, walk_state); + + if (!walk_state) { + return_VOID; + } + + if (walk_state->descriptor_type != ACPI_DESC_TYPE_WALK) { + ACPI_ERROR((AE_INFO, "%p is not a valid walk state", + walk_state)); + return_VOID; + } + + /* There should not be any open scopes */ + + if (walk_state->parser_state.scope) { + ACPI_ERROR((AE_INFO, "%p walk still has a scope list", + walk_state)); + acpi_ps_cleanup_scope(&walk_state->parser_state); + } + + /* Always must free any linked control states */ + + while (walk_state->control_state) { + state = walk_state->control_state; + walk_state->control_state = state->common.next; + + acpi_ut_delete_generic_state(state); + } + + /* Always must free any linked parse states */ + + while (walk_state->scope_info) { + state = walk_state->scope_info; + walk_state->scope_info = state->common.next; + + acpi_ut_delete_generic_state(state); + } + + /* Always must free any stacked result states */ + + while (walk_state->results) { + state = walk_state->results; + walk_state->results = state->common.next; + + acpi_ut_delete_generic_state(state); + } + + ACPI_FREE(walk_state); + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/evevent.c b/kernel/drivers/acpi/acpica/evevent.c new file mode 100644 index 000000000..bf6873f95 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evevent.c @@ -0,0 +1,297 @@ +/****************************************************************************** + * + * Module Name: evevent - Fixed Event handling and dispatch + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evevent") +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/* Local prototypes */ +static acpi_status acpi_ev_fixed_event_initialize(void); + +static u32 acpi_ev_fixed_event_dispatch(u32 event); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_events + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize global data structures for ACPI events (Fixed, GPE) + * + ******************************************************************************/ + +acpi_status acpi_ev_initialize_events(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_initialize_events); + + /* If Hardware Reduced flag is set, there are no fixed events */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + + /* + * Initialize the Fixed and General Purpose Events. This is done prior to + * enabling SCIs to prevent interrupts from occurring before the handlers + * are installed. + */ + status = acpi_ev_fixed_event_initialize(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to initialize fixed events")); + return_ACPI_STATUS(status); + } + + status = acpi_ev_gpe_initialize(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to initialize general purpose events")); + return_ACPI_STATUS(status); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_xrupt_handlers + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Install interrupt handlers for the SCI and Global Lock + * + ******************************************************************************/ + +acpi_status acpi_ev_install_xrupt_handlers(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_install_xrupt_handlers); + + /* If Hardware Reduced flag is set, there is no ACPI h/w */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + + /* Install the SCI handler */ + + status = acpi_ev_install_sci_handler(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to install System Control Interrupt handler")); + return_ACPI_STATUS(status); + } + + /* Install the handler for the Global Lock */ + + status = acpi_ev_init_global_lock_handler(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to initialize Global Lock handler")); + return_ACPI_STATUS(status); + } + + acpi_gbl_events_initialized = TRUE; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_fixed_event_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Install the fixed event handlers and disable all fixed events. + * + ******************************************************************************/ + +static acpi_status acpi_ev_fixed_event_initialize(void) +{ + u32 i; + acpi_status status; + + /* + * Initialize the structure that keeps track of fixed event handlers and + * enable the fixed events. + */ + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + acpi_gbl_fixed_event_handlers[i].handler = NULL; + acpi_gbl_fixed_event_handlers[i].context = NULL; + + /* Disable the fixed event */ + + if (acpi_gbl_fixed_event_info[i].enable_register_id != 0xFF) { + status = + acpi_write_bit_register(acpi_gbl_fixed_event_info + [i].enable_register_id, + ACPI_DISABLE_EVENT); + if (ACPI_FAILURE(status)) { + return (status); + } + } + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_fixed_event_detect + * + * PARAMETERS: None + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Checks the PM status register for active fixed events + * + ******************************************************************************/ + +u32 acpi_ev_fixed_event_detect(void) +{ + u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; + u32 fixed_status; + u32 fixed_enable; + u32 i; + + ACPI_FUNCTION_NAME(ev_fixed_event_detect); + + /* + * Read the fixed feature status and enable registers, as all the cases + * depend on their values. Ignore errors here. + */ + (void)acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status); + (void)acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable); + + ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, + "Fixed Event Block: Enable %08X Status %08X\n", + fixed_enable, fixed_status)); + + /* + * Check for all possible Fixed Events and dispatch those that are active + */ + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + + /* Both the status and enable bits must be on for this event */ + + if ((fixed_status & acpi_gbl_fixed_event_info[i]. + status_bit_mask) + && (fixed_enable & acpi_gbl_fixed_event_info[i]. + enable_bit_mask)) { + /* + * Found an active (signalled) event. Invoke global event + * handler if present. + */ + acpi_fixed_event_count[i]++; + if (acpi_gbl_global_event_handler) { + acpi_gbl_global_event_handler + (ACPI_EVENT_TYPE_FIXED, NULL, i, + acpi_gbl_global_event_handler_context); + } + + int_status |= acpi_ev_fixed_event_dispatch(i); + } + } + + return (int_status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_fixed_event_dispatch + * + * PARAMETERS: event - Event type + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Clears the status bit for the requested event, calls the + * handler that previously registered for the event. + * NOTE: If there is no handler for the event, the event is + * disabled to prevent further interrupts. + * + ******************************************************************************/ + +static u32 acpi_ev_fixed_event_dispatch(u32 event) +{ + + ACPI_FUNCTION_ENTRY(); + + /* Clear the status bit */ + + (void)acpi_write_bit_register(acpi_gbl_fixed_event_info[event]. + status_register_id, ACPI_CLEAR_STATUS); + + /* + * Make sure that a handler exists. If not, report an error + * and disable the event to prevent further interrupts. + */ + if (!acpi_gbl_fixed_event_handlers[event].handler) { + (void)acpi_write_bit_register(acpi_gbl_fixed_event_info[event]. + enable_register_id, + ACPI_DISABLE_EVENT); + + ACPI_ERROR((AE_INFO, + "No installed handler for fixed event - %s (%u), disabling", + acpi_ut_get_event_name(event), event)); + + return (ACPI_INTERRUPT_NOT_HANDLED); + } + + /* Invoke the Fixed Event handler */ + + return ((acpi_gbl_fixed_event_handlers[event]. + handler) (acpi_gbl_fixed_event_handlers[event].context)); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evglock.c b/kernel/drivers/acpi/acpica/evglock.c new file mode 100644 index 000000000..b78dc7c6d --- /dev/null +++ b/kernel/drivers/acpi/acpica/evglock.c @@ -0,0 +1,344 @@ +/****************************************************************************** + * + * Module Name: evglock - Global Lock support + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evglock") +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/* Local prototypes */ +static u32 acpi_ev_global_lock_handler(void *context); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_init_global_lock_handler + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for the global lock release event + * + ******************************************************************************/ + +acpi_status acpi_ev_init_global_lock_handler(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_init_global_lock_handler); + + /* If Hardware Reduced flag is set, there is no global lock */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + + /* Attempt installation of the global lock handler */ + + status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL, + acpi_ev_global_lock_handler, + NULL); + + /* + * If the global lock does not exist on this platform, the attempt to + * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick). + * Map to AE_OK, but mark global lock as not present. Any attempt to + * actually use the global lock will be flagged with an error. + */ + acpi_gbl_global_lock_present = FALSE; + if (status == AE_NO_HARDWARE_RESPONSE) { + ACPI_ERROR((AE_INFO, + "No response from Global Lock hardware, disabling lock")); + + return_ACPI_STATUS(AE_OK); + } + + status = acpi_os_create_lock(&acpi_gbl_global_lock_pending_lock); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_gbl_global_lock_pending = FALSE; + acpi_gbl_global_lock_present = TRUE; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_remove_global_lock_handler + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Remove the handler for the Global Lock + * + ******************************************************************************/ + +acpi_status acpi_ev_remove_global_lock_handler(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_remove_global_lock_handler); + + acpi_gbl_global_lock_present = FALSE; + status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL, + acpi_ev_global_lock_handler); + + acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_global_lock_handler + * + * PARAMETERS: context - From thread interface, not used + * + * RETURN: ACPI_INTERRUPT_HANDLED + * + * DESCRIPTION: Invoked directly from the SCI handler when a global lock + * release interrupt occurs. If there is actually a pending + * request for the lock, signal the waiting thread. + * + ******************************************************************************/ + +static u32 acpi_ev_global_lock_handler(void *context) +{ + acpi_status status; + acpi_cpu_flags flags; + + flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); + + /* + * If a request for the global lock is not actually pending, + * we are done. This handles "spurious" global lock interrupts + * which are possible (and have been seen) with bad BIOSs. + */ + if (!acpi_gbl_global_lock_pending) { + goto cleanup_and_exit; + } + + /* + * Send a unit to the global lock semaphore. The actual acquisition + * of the global lock will be performed by the waiting thread. + */ + status = acpi_os_signal_semaphore(acpi_gbl_global_lock_semaphore, 1); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Could not signal Global Lock semaphore")); + } + + acpi_gbl_global_lock_pending = FALSE; + +cleanup_and_exit: + + acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); + return (ACPI_INTERRUPT_HANDLED); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ev_acquire_global_lock + * + * PARAMETERS: timeout - Max time to wait for the lock, in millisec. + * + * RETURN: Status + * + * DESCRIPTION: Attempt to gain ownership of the Global Lock. + * + * MUTEX: Interpreter must be locked + * + * Note: The original implementation allowed multiple threads to "acquire" the + * Global Lock, and the OS would hold the lock until the last thread had + * released it. However, this could potentially starve the BIOS out of the + * lock, especially in the case where there is a tight handshake between the + * Embedded Controller driver and the BIOS. Therefore, this implementation + * allows only one thread to acquire the HW Global Lock at a time, and makes + * the global lock appear as a standard mutex on the OS side. + * + *****************************************************************************/ + +acpi_status acpi_ev_acquire_global_lock(u16 timeout) +{ + acpi_cpu_flags flags; + acpi_status status; + u8 acquired = FALSE; + + ACPI_FUNCTION_TRACE(ev_acquire_global_lock); + + /* + * Only one thread can acquire the GL at a time, the global_lock_mutex + * enforces this. This interface releases the interpreter if we must wait. + */ + status = + acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex->mutex. + os_mutex, timeout); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Update the global lock handle and check for wraparound. The handle is + * only used for the external global lock interfaces, but it is updated + * here to properly handle the case where a single thread may acquire the + * lock via both the AML and the acpi_acquire_global_lock interfaces. The + * handle is therefore updated on the first acquire from a given thread + * regardless of where the acquisition request originated. + */ + acpi_gbl_global_lock_handle++; + if (acpi_gbl_global_lock_handle == 0) { + acpi_gbl_global_lock_handle = 1; + } + + /* + * Make sure that a global lock actually exists. If not, just + * treat the lock as a standard mutex. + */ + if (!acpi_gbl_global_lock_present) { + acpi_gbl_global_lock_acquired = TRUE; + return_ACPI_STATUS(AE_OK); + } + + flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); + + do { + + /* Attempt to acquire the actual hardware lock */ + + ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); + if (acquired) { + acpi_gbl_global_lock_acquired = TRUE; + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Acquired hardware Global Lock\n")); + break; + } + + /* + * Did not get the lock. The pending bit was set above, and + * we must now wait until we receive the global lock + * released interrupt. + */ + acpi_gbl_global_lock_pending = TRUE; + acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Waiting for hardware Global Lock\n")); + + /* + * Wait for handshake with the global lock interrupt handler. + * This interface releases the interpreter if we must wait. + */ + status = + acpi_ex_system_wait_semaphore + (acpi_gbl_global_lock_semaphore, ACPI_WAIT_FOREVER); + + flags = acpi_os_acquire_lock(acpi_gbl_global_lock_pending_lock); + + } while (ACPI_SUCCESS(status)); + + acpi_gbl_global_lock_pending = FALSE; + acpi_os_release_lock(acpi_gbl_global_lock_pending_lock, flags); + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_release_global_lock + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Releases ownership of the Global Lock. + * + ******************************************************************************/ + +acpi_status acpi_ev_release_global_lock(void) +{ + u8 pending = FALSE; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ev_release_global_lock); + + /* Lock must be already acquired */ + + if (!acpi_gbl_global_lock_acquired) { + ACPI_WARNING((AE_INFO, + "Cannot release the ACPI Global Lock, it has not been acquired")); + return_ACPI_STATUS(AE_NOT_ACQUIRED); + } + + if (acpi_gbl_global_lock_present) { + + /* Allow any thread to release the lock */ + + ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending); + + /* + * If the pending bit was set, we must write GBL_RLS to the control + * register + */ + if (pending) { + status = + acpi_write_bit_register + (ACPI_BITREG_GLOBAL_LOCK_RELEASE, + ACPI_ENABLE_EVENT); + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Released hardware Global Lock\n")); + } + + acpi_gbl_global_lock_acquired = FALSE; + + /* Release the local GL mutex */ + + acpi_os_release_mutex(acpi_gbl_global_lock_mutex->mutex.os_mutex); + return_ACPI_STATUS(status); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evgpe.c b/kernel/drivers/acpi/acpica/evgpe.c new file mode 100644 index 000000000..ccf793247 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evgpe.c @@ -0,0 +1,798 @@ +/****************************************************************************** + * + * Module Name: evgpe - General Purpose Event handling and dispatch + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evgpe") +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/* Local prototypes */ +static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context); + +static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_update_gpe_enable_mask + * + * PARAMETERS: gpe_event_info - GPE to update + * + * RETURN: Status + * + * DESCRIPTION: Updates GPE register enable mask based upon whether there are + * runtime references to this GPE + * + ******************************************************************************/ + +acpi_status +acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info) +{ + struct acpi_gpe_register_info *gpe_register_info; + u32 register_bit; + + ACPI_FUNCTION_TRACE(ev_update_gpe_enable_mask); + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); + + /* Clear the run bit up front */ + + ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit); + + /* Set the mask bit only if there are references to this GPE */ + + if (gpe_event_info->runtime_count) { + ACPI_SET_BIT(gpe_register_info->enable_for_run, + (u8)register_bit); + } + gpe_register_info->enable_mask = gpe_register_info->enable_for_run; + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_enable_gpe + * + * PARAMETERS: gpe_event_info - GPE to enable + * + * RETURN: Status + * + * DESCRIPTION: Clear a GPE of stale events and enable it. + * + ******************************************************************************/ + +acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_enable_gpe); + + /* Clear the GPE (of stale events) */ + + status = acpi_hw_clear_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Enable the requested GPE */ + + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_add_gpe_reference + * + * PARAMETERS: gpe_event_info - Add a reference to this GPE + * + * RETURN: Status + * + * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is + * hardware-enabled. + * + ******************************************************************************/ + +acpi_status +acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ev_add_gpe_reference); + + if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) { + return_ACPI_STATUS(AE_LIMIT); + } + + gpe_event_info->runtime_count++; + if (gpe_event_info->runtime_count == 1) { + + /* Enable on first reference */ + + status = acpi_ev_update_gpe_enable_mask(gpe_event_info); + if (ACPI_SUCCESS(status)) { + status = acpi_ev_enable_gpe(gpe_event_info); + } + + if (ACPI_FAILURE(status)) { + gpe_event_info->runtime_count--; + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_remove_gpe_reference + * + * PARAMETERS: gpe_event_info - Remove a reference to this GPE + * + * RETURN: Status + * + * DESCRIPTION: Remove a reference to a GPE. When the last reference is + * removed, the GPE is hardware-disabled. + * + ******************************************************************************/ + +acpi_status +acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ev_remove_gpe_reference); + + if (!gpe_event_info->runtime_count) { + return_ACPI_STATUS(AE_LIMIT); + } + + gpe_event_info->runtime_count--; + if (!gpe_event_info->runtime_count) { + + /* Disable on last reference */ + + status = acpi_ev_update_gpe_enable_mask(gpe_event_info); + if (ACPI_SUCCESS(status)) { + status = + acpi_hw_low_set_gpe(gpe_event_info, + ACPI_GPE_DISABLE); + } + + if (ACPI_FAILURE(status)) { + gpe_event_info->runtime_count++; + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_low_get_gpe_info + * + * PARAMETERS: gpe_number - Raw GPE number + * gpe_block - A GPE info block + * + * RETURN: A GPE event_info struct. NULL if not a valid GPE (The gpe_number + * is not within the specified GPE block) + * + * DESCRIPTION: Returns the event_info struct associated with this GPE. This is + * the low-level implementation of ev_get_gpe_event_info. + * + ******************************************************************************/ + +struct acpi_gpe_event_info *acpi_ev_low_get_gpe_info(u32 gpe_number, + struct acpi_gpe_block_info + *gpe_block) +{ + u32 gpe_index; + + /* + * Validate that the gpe_number is within the specified gpe_block. + * (Two steps) + */ + if (!gpe_block || (gpe_number < gpe_block->block_base_number)) { + return (NULL); + } + + gpe_index = gpe_number - gpe_block->block_base_number; + if (gpe_index >= gpe_block->gpe_count) { + return (NULL); + } + + return (&gpe_block->event_info[gpe_index]); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_event_info + * + * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 + * gpe_number - Raw GPE number + * + * RETURN: A GPE event_info struct. NULL if not a valid GPE + * + * DESCRIPTION: Returns the event_info struct associated with this GPE. + * Validates the gpe_block and the gpe_number + * + * Should be called only when the GPE lists are semaphore locked + * and not subject to change. + * + ******************************************************************************/ + +struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, + u32 gpe_number) +{ + union acpi_operand_object *obj_desc; + struct acpi_gpe_event_info *gpe_info; + u32 i; + + ACPI_FUNCTION_ENTRY(); + + /* A NULL gpe_device means use the FADT-defined GPE block(s) */ + + if (!gpe_device) { + + /* Examine GPE Block 0 and 1 (These blocks are permanent) */ + + for (i = 0; i < ACPI_MAX_GPE_BLOCKS; i++) { + gpe_info = acpi_ev_low_get_gpe_info(gpe_number, + acpi_gbl_gpe_fadt_blocks + [i]); + if (gpe_info) { + return (gpe_info); + } + } + + /* The gpe_number was not in the range of either FADT GPE block */ + + return (NULL); + } + + /* A Non-NULL gpe_device means this is a GPE Block Device */ + + obj_desc = + acpi_ns_get_attached_object((struct acpi_namespace_node *) + gpe_device); + if (!obj_desc || !obj_desc->device.gpe_block) { + return (NULL); + } + + return (acpi_ev_low_get_gpe_info + (gpe_number, obj_desc->device.gpe_block)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_detect + * + * PARAMETERS: gpe_xrupt_list - Interrupt block for this interrupt. + * Can have multiple GPE blocks attached. + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Detect if any GP events have occurred. This function is + * executed at interrupt level. + * + ******************************************************************************/ + +u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list) +{ + acpi_status status; + struct acpi_gpe_block_info *gpe_block; + struct acpi_namespace_node *gpe_device; + struct acpi_gpe_register_info *gpe_register_info; + struct acpi_gpe_event_info *gpe_event_info; + u32 gpe_number; + struct acpi_gpe_handler_info *gpe_handler_info; + u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; + u8 enabled_status_byte; + u32 status_reg; + u32 enable_reg; + acpi_cpu_flags flags; + u32 i; + u32 j; + + ACPI_FUNCTION_NAME(ev_gpe_detect); + + /* Check for the case where there are no GPEs */ + + if (!gpe_xrupt_list) { + return (int_status); + } + + /* + * We need to obtain the GPE lock for both the data structs and registers + * Note: Not necessary to obtain the hardware lock, since the GPE + * registers are owned by the gpe_lock. + */ + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Examine all GPE blocks attached to this interrupt level */ + + gpe_block = gpe_xrupt_list->gpe_block_list_head; + while (gpe_block) { + gpe_device = gpe_block->node; + + /* + * Read all of the 8-bit GPE status and enable registers in this GPE + * block, saving all of them. Find all currently active GP events. + */ + for (i = 0; i < gpe_block->register_count; i++) { + + /* Get the next status/enable pair */ + + gpe_register_info = &gpe_block->register_info[i]; + + /* + * Optimization: If there are no GPEs enabled within this + * register, we can safely ignore the entire register. + */ + if (!(gpe_register_info->enable_for_run | + gpe_register_info->enable_for_wake)) { + ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, + "Ignore disabled registers for GPE %02X-%02X: " + "RunEnable=%02X, WakeEnable=%02X\n", + gpe_register_info-> + base_gpe_number, + gpe_register_info-> + base_gpe_number + + (ACPI_GPE_REGISTER_WIDTH - 1), + gpe_register_info-> + enable_for_run, + gpe_register_info-> + enable_for_wake)); + continue; + } + + /* Read the Status Register */ + + status = + acpi_hw_read(&status_reg, + &gpe_register_info->status_address); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* Read the Enable Register */ + + status = + acpi_hw_read(&enable_reg, + &gpe_register_info->enable_address); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, + "Read registers for GPE %02X-%02X: Status=%02X, Enable=%02X, " + "RunEnable=%02X, WakeEnable=%02X\n", + gpe_register_info->base_gpe_number, + gpe_register_info->base_gpe_number + + (ACPI_GPE_REGISTER_WIDTH - 1), + status_reg, enable_reg, + gpe_register_info->enable_for_run, + gpe_register_info->enable_for_wake)); + + /* Check if there is anything active at all in this register */ + + enabled_status_byte = (u8)(status_reg & enable_reg); + if (!enabled_status_byte) { + + /* No active GPEs in this register, move on */ + + continue; + } + + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + + /* Examine one GPE bit */ + + gpe_event_info = + &gpe_block-> + event_info[((acpi_size) i * + ACPI_GPE_REGISTER_WIDTH) + j]; + gpe_number = + j + gpe_register_info->base_gpe_number; + + if (enabled_status_byte & (1 << j)) { + + /* Invoke global event handler if present */ + + acpi_gpe_count++; + if (acpi_gbl_global_event_handler) { + acpi_gbl_global_event_handler + (ACPI_EVENT_TYPE_GPE, + gpe_device, gpe_number, + acpi_gbl_global_event_handler_context); + } + + /* Found an active GPE */ + + if (ACPI_GPE_DISPATCH_TYPE + (gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER) { + + /* Dispatch the event to a raw handler */ + + gpe_handler_info = + gpe_event_info->dispatch. + handler; + + /* + * There is no protection around the namespace node + * and the GPE handler to ensure a safe destruction + * because: + * 1. The namespace node is expected to always + * exist after loading a table. + * 2. The GPE handler is expected to be flushed by + * acpi_os_wait_events_complete() before the + * destruction. + */ + acpi_os_release_lock + (acpi_gbl_gpe_lock, flags); + int_status |= + gpe_handler_info-> + address(gpe_device, + gpe_number, + gpe_handler_info-> + context); + flags = + acpi_os_acquire_lock + (acpi_gbl_gpe_lock); + } else { + /* + * Dispatch the event to a standard handler or + * method. + */ + int_status |= + acpi_ev_gpe_dispatch + (gpe_device, gpe_event_info, + gpe_number); + } + } + } + } + + gpe_block = gpe_block->next; + } + +unlock_and_exit: + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return (int_status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_asynch_execute_gpe_method + * + * PARAMETERS: Context (gpe_event_info) - Info for this GPE + * + * RETURN: None + * + * DESCRIPTION: Perform the actual execution of a GPE control method. This + * function is called from an invocation of acpi_os_execute and + * therefore does NOT execute at interrupt level - so that + * the control method itself is not executed in the context of + * an interrupt handler. + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) +{ + struct acpi_gpe_event_info *gpe_event_info = context; + acpi_status status = AE_OK; + struct acpi_evaluate_info *info; + struct acpi_gpe_notify_info *notify; + + ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); + + /* Do the correct dispatch - normal method or implicit notify */ + + switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) { + case ACPI_GPE_DISPATCH_NOTIFY: + /* + * Implicit notify. + * Dispatch a DEVICE_WAKE notify to the appropriate handler. + * NOTE: the request is queued for execution after this method + * completes. The notify handlers are NOT invoked synchronously + * from this thread -- because handlers may in turn run other + * control methods. + * + * June 2012: Expand implicit notify mechanism to support + * notifies on multiple device objects. + */ + notify = gpe_event_info->dispatch.notify_list; + while (ACPI_SUCCESS(status) && notify) { + status = + acpi_ev_queue_notify_request(notify->device_node, + ACPI_NOTIFY_DEVICE_WAKE); + + notify = notify->next; + } + + break; + + case ACPI_GPE_DISPATCH_METHOD: + + /* Allocate the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + status = AE_NO_MEMORY; + } else { + /* + * Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the + * _Lxx/_Exx control method that corresponds to this GPE + */ + info->prefix_node = + gpe_event_info->dispatch.method_node; + info->flags = ACPI_IGNORE_RETURN_VALUE; + + status = acpi_ns_evaluate(info); + ACPI_FREE(info); + } + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "while evaluating GPE method [%4.4s]", + acpi_ut_get_node_name(gpe_event_info-> + dispatch. + method_node))); + } + break; + + default: + + goto error_exit; /* Should never happen */ + } + + /* Defer enabling of GPE until all notify handlers are done */ + + status = acpi_os_execute(OSL_NOTIFY_HANDLER, + acpi_ev_asynch_enable_gpe, gpe_event_info); + if (ACPI_SUCCESS(status)) { + return_VOID; + } + +error_exit: + acpi_ev_asynch_enable_gpe(gpe_event_info); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_asynch_enable_gpe + * + * PARAMETERS: Context (gpe_event_info) - Info for this GPE + * Callback from acpi_os_execute + * + * RETURN: None + * + * DESCRIPTION: Asynchronous clear/enable for GPE. This allows the GPE to + * complete (i.e., finish execution of Notify) + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context) +{ + struct acpi_gpe_event_info *gpe_event_info = context; + acpi_cpu_flags flags; + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + (void)acpi_ev_finish_gpe(gpe_event_info); + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_finish_gpe + * + * PARAMETERS: gpe_event_info - Info for this GPE + * + * RETURN: Status + * + * DESCRIPTION: Clear/Enable a GPE. Common code that is used after execution + * of a GPE method or a synchronous or asynchronous GPE handler. + * + ******************************************************************************/ + +acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info * gpe_event_info) +{ + acpi_status status; + + if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == + ACPI_GPE_LEVEL_TRIGGERED) { + /* + * GPE is level-triggered, we clear the GPE status bit after + * handling the event. + */ + status = acpi_hw_clear_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + /* + * Enable this GPE, conditionally. This means that the GPE will + * only be physically enabled if the enable_mask bit is set + * in the event_info. + */ + (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_dispatch + * + * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 + * gpe_event_info - Info for this GPE + * gpe_number - Number relative to the parent GPE block + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Dispatch a General Purpose Event to either a function (e.g. EC) + * or method (e.g. _Lxx/_Exx) handler. + * + * This function executes at interrupt level. + * + ******************************************************************************/ + +u32 +acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, + struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) +{ + acpi_status status; + u32 return_value; + + ACPI_FUNCTION_TRACE(ev_gpe_dispatch); + + /* + * Always disable the GPE so that it does not keep firing before + * any asynchronous activity completes (either from the execution + * of a GPE method or an asynchronous GPE handler.) + * + * If there is no handler or method to run, just disable the + * GPE and leave it disabled permanently to prevent further such + * pointless events from firing. + */ + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to disable GPE %02X", gpe_number)); + return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); + } + + /* + * If edge-triggered, clear the GPE status bit now. Note that + * level-triggered events are cleared after the GPE is serviced. + */ + if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == + ACPI_GPE_EDGE_TRIGGERED) { + status = acpi_hw_clear_gpe(gpe_event_info); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to clear GPE %02X", + gpe_number)); + (void)acpi_hw_low_set_gpe(gpe_event_info, + ACPI_GPE_CONDITIONAL_ENABLE); + return_UINT32(ACPI_INTERRUPT_NOT_HANDLED); + } + } + + /* + * Dispatch the GPE to either an installed handler or the control + * method associated with this GPE (_Lxx or _Exx). If a handler + * exists, we invoke it and do not attempt to run the method. + * If there is neither a handler nor a method, leave the GPE + * disabled. + */ + switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) { + case ACPI_GPE_DISPATCH_HANDLER: + + /* Invoke the installed handler (at interrupt level) */ + + return_value = + gpe_event_info->dispatch.handler->address(gpe_device, + gpe_number, + gpe_event_info-> + dispatch.handler-> + context); + + /* If requested, clear (if level-triggered) and reenable the GPE */ + + if (return_value & ACPI_REENABLE_GPE) { + (void)acpi_ev_finish_gpe(gpe_event_info); + } + break; + + case ACPI_GPE_DISPATCH_METHOD: + case ACPI_GPE_DISPATCH_NOTIFY: + /* + * Execute the method associated with the GPE + * NOTE: Level-triggered GPEs are cleared after the method completes. + */ + status = acpi_os_execute(OSL_GPE_HANDLER, + acpi_ev_asynch_execute_gpe_method, + gpe_event_info); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Unable to queue handler for GPE %02X - event disabled", + gpe_number)); + } + break; + + default: + /* + * No handler or method to run! + * 03/2010: This case should no longer be possible. We will not allow + * a GPE to be enabled if it has no handler or method. + */ + ACPI_ERROR((AE_INFO, + "No handler or method for GPE %02X, disabling event", + gpe_number)); + + break; + } + + return_UINT32(ACPI_INTERRUPT_HANDLED); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evgpeblk.c b/kernel/drivers/acpi/acpica/evgpeblk.c new file mode 100644 index 000000000..e0f24c504 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evgpeblk.c @@ -0,0 +1,513 @@ +/****************************************************************************** + * + * Module Name: evgpeblk - GPE block creation and initialization. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evgpeblk") +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/* Local prototypes */ +static acpi_status +acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block, + u32 interrupt_number); + +static acpi_status +acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_gpe_block + * + * PARAMETERS: gpe_block - New GPE block + * interrupt_number - Xrupt to be associated with this + * GPE block + * + * RETURN: Status + * + * DESCRIPTION: Install new GPE block with mutex support + * + ******************************************************************************/ + +static acpi_status +acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block, + u32 interrupt_number) +{ + struct acpi_gpe_block_info *next_gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_block; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_install_gpe_block); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = + acpi_ev_get_gpe_xrupt_block(interrupt_number, &gpe_xrupt_block); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* Install the new block at the end of the list with lock */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + if (gpe_xrupt_block->gpe_block_list_head) { + next_gpe_block = gpe_xrupt_block->gpe_block_list_head; + while (next_gpe_block->next) { + next_gpe_block = next_gpe_block->next; + } + + next_gpe_block->next = gpe_block; + gpe_block->previous = next_gpe_block; + } else { + gpe_xrupt_block->gpe_block_list_head = gpe_block; + } + + gpe_block->xrupt_block = gpe_xrupt_block; + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_delete_gpe_block + * + * PARAMETERS: gpe_block - Existing GPE block + * + * RETURN: Status + * + * DESCRIPTION: Remove a GPE block + * + ******************************************************************************/ + +acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block) +{ + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_install_gpe_block); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Disable all GPEs in this block */ + + status = + acpi_hw_disable_gpe_block(gpe_block->xrupt_block, gpe_block, NULL); + + if (!gpe_block->previous && !gpe_block->next) { + + /* This is the last gpe_block on this interrupt */ + + status = acpi_ev_delete_gpe_xrupt(gpe_block->xrupt_block); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } else { + /* Remove the block on this interrupt with lock */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + if (gpe_block->previous) { + gpe_block->previous->next = gpe_block->next; + } else { + gpe_block->xrupt_block->gpe_block_list_head = + gpe_block->next; + } + + if (gpe_block->next) { + gpe_block->next->previous = gpe_block->previous; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + } + + acpi_current_gpe_count -= gpe_block->gpe_count; + + /* Free the gpe_block */ + + ACPI_FREE(gpe_block->register_info); + ACPI_FREE(gpe_block->event_info); + ACPI_FREE(gpe_block); + +unlock_and_exit: + status = acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_create_gpe_info_blocks + * + * PARAMETERS: gpe_block - New GPE block + * + * RETURN: Status + * + * DESCRIPTION: Create the register_info and event_info blocks for this GPE block + * + ******************************************************************************/ + +static acpi_status +acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block) +{ + struct acpi_gpe_register_info *gpe_register_info = NULL; + struct acpi_gpe_event_info *gpe_event_info = NULL; + struct acpi_gpe_event_info *this_event; + struct acpi_gpe_register_info *this_register; + u32 i; + u32 j; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_create_gpe_info_blocks); + + /* Allocate the GPE register information block */ + + gpe_register_info = ACPI_ALLOCATE_ZEROED((acpi_size) gpe_block-> + register_count * + sizeof(struct + acpi_gpe_register_info)); + if (!gpe_register_info) { + ACPI_ERROR((AE_INFO, + "Could not allocate the GpeRegisterInfo table")); + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* + * Allocate the GPE event_info block. There are eight distinct GPEs + * per register. Initialization to zeros is sufficient. + */ + gpe_event_info = ACPI_ALLOCATE_ZEROED((acpi_size) gpe_block->gpe_count * + sizeof(struct + acpi_gpe_event_info)); + if (!gpe_event_info) { + ACPI_ERROR((AE_INFO, + "Could not allocate the GpeEventInfo table")); + status = AE_NO_MEMORY; + goto error_exit; + } + + /* Save the new Info arrays in the GPE block */ + + gpe_block->register_info = gpe_register_info; + gpe_block->event_info = gpe_event_info; + + /* + * Initialize the GPE Register and Event structures. A goal of these + * tables is to hide the fact that there are two separate GPE register + * sets in a given GPE hardware block, the status registers occupy the + * first half, and the enable registers occupy the second half. + */ + this_register = gpe_register_info; + this_event = gpe_event_info; + + for (i = 0; i < gpe_block->register_count; i++) { + + /* Init the register_info for this GPE register (8 GPEs) */ + + this_register->base_gpe_number = (u16) + (gpe_block->block_base_number + + (i * ACPI_GPE_REGISTER_WIDTH)); + + this_register->status_address.address = gpe_block->address + i; + + this_register->enable_address.address = + gpe_block->address + i + gpe_block->register_count; + + this_register->status_address.space_id = gpe_block->space_id; + this_register->enable_address.space_id = gpe_block->space_id; + this_register->status_address.bit_width = + ACPI_GPE_REGISTER_WIDTH; + this_register->enable_address.bit_width = + ACPI_GPE_REGISTER_WIDTH; + this_register->status_address.bit_offset = 0; + this_register->enable_address.bit_offset = 0; + + /* Init the event_info for each GPE within this register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + this_event->gpe_number = + (u8) (this_register->base_gpe_number + j); + this_event->register_info = this_register; + this_event++; + } + + /* Disable all GPEs within this register */ + + status = acpi_hw_write(0x00, &this_register->enable_address); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + /* Clear any pending GPE events within this register */ + + status = acpi_hw_write(0xFF, &this_register->status_address); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + this_register++; + } + + return_ACPI_STATUS(AE_OK); + +error_exit: + if (gpe_register_info) { + ACPI_FREE(gpe_register_info); + } + if (gpe_event_info) { + ACPI_FREE(gpe_event_info); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_create_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE block + * gpe_block_address - Address and space_ID + * register_count - Number of GPE register pairs in the block + * gpe_block_base_number - Starting GPE number for the block + * interrupt_number - H/W interrupt for the block + * return_gpe_block - Where the new block descriptor is returned + * + * RETURN: Status + * + * DESCRIPTION: Create and Install a block of GPE registers. All GPEs within + * the block are disabled at exit. + * Note: Assumes namespace is locked. + * + ******************************************************************************/ + +acpi_status +acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, + u64 address, + u8 space_id, + u32 register_count, + u16 gpe_block_base_number, + u32 interrupt_number, + struct acpi_gpe_block_info **return_gpe_block) +{ + acpi_status status; + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_walk_info walk_info; + + ACPI_FUNCTION_TRACE(ev_create_gpe_block); + + if (!register_count) { + return_ACPI_STATUS(AE_OK); + } + + /* Allocate a new GPE block */ + + gpe_block = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_block_info)); + if (!gpe_block) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Initialize the new GPE block */ + + gpe_block->address = address; + gpe_block->space_id = space_id; + gpe_block->node = gpe_device; + gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH); + gpe_block->initialized = FALSE; + gpe_block->register_count = register_count; + gpe_block->block_base_number = gpe_block_base_number; + + /* + * Create the register_info and event_info sub-structures + * Note: disables and clears all GPEs in the block + */ + status = acpi_ev_create_gpe_info_blocks(gpe_block); + if (ACPI_FAILURE(status)) { + ACPI_FREE(gpe_block); + return_ACPI_STATUS(status); + } + + /* Install the new block in the global lists */ + + status = acpi_ev_install_gpe_block(gpe_block, interrupt_number); + if (ACPI_FAILURE(status)) { + ACPI_FREE(gpe_block->register_info); + ACPI_FREE(gpe_block->event_info); + ACPI_FREE(gpe_block); + return_ACPI_STATUS(status); + } + + acpi_gbl_all_gpes_initialized = FALSE; + + /* Find all GPE methods (_Lxx or_Exx) for this block */ + + walk_info.gpe_block = gpe_block; + walk_info.gpe_device = gpe_device; + walk_info.execute_by_owner_id = FALSE; + + status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device, + ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, + acpi_ev_match_gpe_method, NULL, + &walk_info, NULL); + + /* Return the new block */ + + if (return_gpe_block) { + (*return_gpe_block) = gpe_block; + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + " Initialized GPE %02X to %02X [%4.4s] %u regs on interrupt 0x%X%s\n", + (u32)gpe_block->block_base_number, + (u32)(gpe_block->block_base_number + + (gpe_block->gpe_count - 1)), + gpe_device->name.ascii, gpe_block->register_count, + interrupt_number, + interrupt_number == + acpi_gbl_FADT.sci_interrupt ? " (SCI)" : "")); + + /* Update global count of currently available GPEs */ + + acpi_current_gpe_count += gpe_block->gpe_count; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_gpe_block + * + * PARAMETERS: acpi_gpe_callback + * + * RETURN: Status + * + * DESCRIPTION: Initialize and enable a GPE block. Enable GPEs that have + * associated methods. + * Note: Assumes namespace is locked. + * + ******************************************************************************/ + +acpi_status +acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *ignored) +{ + acpi_status status; + struct acpi_gpe_event_info *gpe_event_info; + u32 gpe_enabled_count; + u32 gpe_index; + u32 i; + u32 j; + + ACPI_FUNCTION_TRACE(ev_initialize_gpe_block); + + /* + * Ignore a null GPE block (e.g., if no GPE block 1 exists), and + * any GPE blocks that have been initialized already. + */ + if (!gpe_block || gpe_block->initialized) { + return_ACPI_STATUS(AE_OK); + } + + /* + * Enable all GPEs that have a corresponding method and have the + * ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block + * must be enabled via the acpi_enable_gpe() interface. + */ + gpe_enabled_count = 0; + + for (i = 0; i < gpe_block->register_count; i++) { + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + + /* Get the info block for this particular GPE */ + + gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j; + gpe_event_info = &gpe_block->event_info[gpe_index]; + + /* + * Ignore GPEs that have no corresponding _Lxx/_Exx method + * and GPEs that are used to wake the system + */ + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_NONE) + || (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) + || (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER) + || (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { + continue; + } + + status = acpi_ev_add_gpe_reference(gpe_event_info); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not enable GPE 0x%02X", + gpe_index + + gpe_block->block_base_number)); + continue; + } + + gpe_enabled_count++; + } + } + + if (gpe_enabled_count) { + ACPI_INFO((AE_INFO, + "Enabled %u GPEs in block %02X to %02X", + gpe_enabled_count, (u32)gpe_block->block_base_number, + (u32)(gpe_block->block_base_number + + (gpe_block->gpe_count - 1)))); + } + + gpe_block->initialized = TRUE; + + return_ACPI_STATUS(AE_OK); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evgpeinit.c b/kernel/drivers/acpi/acpica/evgpeinit.c new file mode 100644 index 000000000..8840296d5 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evgpeinit.c @@ -0,0 +1,446 @@ +/****************************************************************************** + * + * Module Name: evgpeinit - System GPE initialization and update + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evgpeinit") +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/* + * Note: History of _PRW support in ACPICA + * + * Originally (2000 - 2010), the GPE initialization code performed a walk of + * the entire namespace to execute the _PRW methods and detect all GPEs + * capable of waking the system. + * + * As of 10/2010, the _PRW method execution has been removed since it is + * actually unnecessary. The host OS must in fact execute all _PRW methods + * in order to identify the device/power-resource dependencies. We now put + * the onus on the host OS to identify the wake GPEs as part of this process + * and to inform ACPICA of these GPEs via the acpi_setup_gpe_for_wake interface. This + * not only reduces the complexity of the ACPICA initialization code, but in + * some cases (on systems with very large namespaces) it should reduce the + * kernel boot time as well. + */ + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize the GPE data structures and the FADT GPE 0/1 blocks + * + ******************************************************************************/ +acpi_status acpi_ev_gpe_initialize(void) +{ + u32 register_count0 = 0; + u32 register_count1 = 0; + u32 gpe_number_max = 0; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_gpe_initialize); + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + "Initializing General Purpose Events (GPEs):\n")); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Initialize the GPE Block(s) defined in the FADT + * + * Why the GPE register block lengths are divided by 2: From the ACPI + * Spec, section "General-Purpose Event Registers", we have: + * + * "Each register block contains two registers of equal length + * GPEx_STS and GPEx_EN (where x is 0 or 1). The length of the + * GPE0_STS and GPE0_EN registers is equal to half the GPE0_LEN + * The length of the GPE1_STS and GPE1_EN registers is equal to + * half the GPE1_LEN. If a generic register block is not supported + * then its respective block pointer and block length values in the + * FADT table contain zeros. The GPE0_LEN and GPE1_LEN do not need + * to be the same size." + */ + + /* + * Determine the maximum GPE number for this machine. + * + * Note: both GPE0 and GPE1 are optional, and either can exist without + * the other. + * + * If EITHER the register length OR the block address are zero, then that + * particular block is not supported. + */ + if (acpi_gbl_FADT.gpe0_block_length && + acpi_gbl_FADT.xgpe0_block.address) { + + /* GPE block 0 exists (has both length and address > 0) */ + + register_count0 = (u16)(acpi_gbl_FADT.gpe0_block_length / 2); + gpe_number_max = + (register_count0 * ACPI_GPE_REGISTER_WIDTH) - 1; + + /* Install GPE Block 0 */ + + status = acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device, + acpi_gbl_FADT.xgpe0_block. + address, + acpi_gbl_FADT.xgpe0_block. + space_id, register_count0, 0, + acpi_gbl_FADT.sci_interrupt, + &acpi_gbl_gpe_fadt_blocks[0]); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not create GPE Block 0")); + } + } + + if (acpi_gbl_FADT.gpe1_block_length && + acpi_gbl_FADT.xgpe1_block.address) { + + /* GPE block 1 exists (has both length and address > 0) */ + + register_count1 = (u16)(acpi_gbl_FADT.gpe1_block_length / 2); + + /* Check for GPE0/GPE1 overlap (if both banks exist) */ + + if ((register_count0) && + (gpe_number_max >= acpi_gbl_FADT.gpe1_base)) { + ACPI_ERROR((AE_INFO, + "GPE0 block (GPE 0 to %u) overlaps the GPE1 block " + "(GPE %u to %u) - Ignoring GPE1", + gpe_number_max, acpi_gbl_FADT.gpe1_base, + acpi_gbl_FADT.gpe1_base + + ((register_count1 * + ACPI_GPE_REGISTER_WIDTH) - 1))); + + /* Ignore GPE1 block by setting the register count to zero */ + + register_count1 = 0; + } else { + /* Install GPE Block 1 */ + + status = + acpi_ev_create_gpe_block(acpi_gbl_fadt_gpe_device, + acpi_gbl_FADT.xgpe1_block. + address, + acpi_gbl_FADT.xgpe1_block. + space_id, register_count1, + acpi_gbl_FADT.gpe1_base, + acpi_gbl_FADT. + sci_interrupt, + &acpi_gbl_gpe_fadt_blocks + [1]); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not create GPE Block 1")); + } + + /* + * GPE0 and GPE1 do not have to be contiguous in the GPE number + * space. However, GPE0 always starts at GPE number zero. + */ + gpe_number_max = acpi_gbl_FADT.gpe1_base + + ((register_count1 * ACPI_GPE_REGISTER_WIDTH) - 1); + } + } + + /* Exit if there are no GPE registers */ + + if ((register_count0 + register_count1) == 0) { + + /* GPEs are not required by ACPI, this is OK */ + + ACPI_DEBUG_PRINT((ACPI_DB_INIT, + "There are no GPE blocks defined in the FADT\n")); + status = AE_OK; + goto cleanup; + } + +cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_update_gpes + * + * PARAMETERS: table_owner_id - ID of the newly-loaded ACPI table + * + * RETURN: None + * + * DESCRIPTION: Check for new GPE methods (_Lxx/_Exx) made available as a + * result of a Load() or load_table() operation. If new GPE + * methods have been installed, register the new methods. + * + ******************************************************************************/ + +void acpi_ev_update_gpes(acpi_owner_id table_owner_id) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_walk_info walk_info; + acpi_status status = AE_OK; + + /* + * Find any _Lxx/_Exx GPE methods that have just been loaded. + * + * Any GPEs that correspond to new _Lxx/_Exx methods are immediately + * enabled. + * + * Examine the namespace underneath each gpe_device within the + * gpe_block lists. + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return; + } + + walk_info.count = 0; + walk_info.owner_id = table_owner_id; + walk_info.execute_by_owner_id = TRUE; + + /* Walk the interrupt level descriptor list */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + + /* Walk all Gpe Blocks attached to this interrupt level */ + + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + walk_info.gpe_block = gpe_block; + walk_info.gpe_device = gpe_block->node; + + status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, + walk_info.gpe_device, + ACPI_UINT32_MAX, + ACPI_NS_WALK_NO_UNLOCK, + acpi_ev_match_gpe_method, + NULL, &walk_info, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While decoding _Lxx/_Exx methods")); + } + + gpe_block = gpe_block->next; + } + + gpe_xrupt_info = gpe_xrupt_info->next; + } + + if (walk_info.count) { + ACPI_INFO((AE_INFO, "Enabled %u new GPEs", walk_info.count)); + } + + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_match_gpe_method + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a + * control method under the _GPE portion of the namespace. + * Extract the name and GPE type from the object, saving this + * information for quick lookup during GPE dispatch. Allows a + * per-owner_id evaluation if execute_by_owner_id is TRUE in the + * walk_info parameter block. + * + * The name of each GPE control method is of the form: + * "_Lxx" or "_Exx", where: + * L - means that the GPE is level triggered + * E - means that the GPE is edge triggered + * xx - is the GPE number [in HEX] + * + * If walk_info->execute_by_owner_id is TRUE, we only execute examine GPE methods + * with that owner. + * + ******************************************************************************/ + +acpi_status +acpi_ev_match_gpe_method(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + struct acpi_namespace_node *method_node = + ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + struct acpi_gpe_walk_info *walk_info = + ACPI_CAST_PTR(struct acpi_gpe_walk_info, context); + struct acpi_gpe_event_info *gpe_event_info; + u32 gpe_number; + char name[ACPI_NAME_SIZE + 1]; + u8 type; + + ACPI_FUNCTION_TRACE(ev_match_gpe_method); + + /* Check if requested owner_id matches this owner_id */ + + if ((walk_info->execute_by_owner_id) && + (method_node->owner_id != walk_info->owner_id)) { + return_ACPI_STATUS(AE_OK); + } + + /* + * Match and decode the _Lxx and _Exx GPE method names + * + * 1) Extract the method name and null terminate it + */ + ACPI_MOVE_32_TO_32(name, &method_node->name.integer); + name[ACPI_NAME_SIZE] = 0; + + /* 2) Name must begin with an underscore */ + + if (name[0] != '_') { + return_ACPI_STATUS(AE_OK); /* Ignore this method */ + } + + /* + * 3) Edge/Level determination is based on the 2nd character + * of the method name + */ + switch (name[1]) { + case 'L': + + type = ACPI_GPE_LEVEL_TRIGGERED; + break; + + case 'E': + + type = ACPI_GPE_EDGE_TRIGGERED; + break; + + default: + + /* Unknown method type, just ignore it */ + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "Ignoring unknown GPE method type: %s " + "(name not of form _Lxx or _Exx)", name)); + return_ACPI_STATUS(AE_OK); + } + + /* 4) The last two characters of the name are the hex GPE Number */ + + gpe_number = ACPI_STRTOUL(&name[2], NULL, 16); + if (gpe_number == ACPI_UINT32_MAX) { + + /* Conversion failed; invalid method, just ignore it */ + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "Could not extract GPE number from name: %s " + "(name is not of form _Lxx or _Exx)", name)); + return_ACPI_STATUS(AE_OK); + } + + /* Ensure that we have a valid GPE number for this GPE block */ + + gpe_event_info = + acpi_ev_low_get_gpe_info(gpe_number, walk_info->gpe_block); + if (!gpe_event_info) { + /* + * This gpe_number is not valid for this GPE block, just ignore it. + * However, it may be valid for a different GPE block, since GPE0 + * and GPE1 methods both appear under \_GPE. + */ + return_ACPI_STATUS(AE_OK); + } + + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) || + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER)) { + + /* If there is already a handler, ignore this GPE method */ + + return_ACPI_STATUS(AE_OK); + } + + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_METHOD) { + /* + * If there is already a method, ignore this method. But check + * for a type mismatch (if both the _Lxx AND _Exx exist) + */ + if (type != (gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK)) { + ACPI_ERROR((AE_INFO, + "For GPE 0x%.2X, found both _L%2.2X and _E%2.2X methods", + gpe_number, gpe_number, gpe_number)); + } + return_ACPI_STATUS(AE_OK); + } + + /* Disable the GPE in case it's been enabled already. */ + + (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); + + /* + * Add the GPE information from above to the gpe_event_info block for + * use during dispatch of this GPE. + */ + gpe_event_info->flags &= ~(ACPI_GPE_DISPATCH_MASK); + gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_METHOD); + gpe_event_info->dispatch.method_node = method_node; + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "Registered GPE method %s as GPE number 0x%.2X\n", + name, gpe_number)); + return_ACPI_STATUS(AE_OK); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evgpeutil.c b/kernel/drivers/acpi/acpica/evgpeutil.c new file mode 100644 index 000000000..3a958f361 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evgpeutil.c @@ -0,0 +1,359 @@ +/****************************************************************************** + * + * Module Name: evgpeutil - GPE utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evgpeutil") + +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/******************************************************************************* + * + * FUNCTION: acpi_ev_walk_gpe_list + * + * PARAMETERS: gpe_walk_callback - Routine called for each GPE block + * context - Value passed to callback + * + * RETURN: Status + * + * DESCRIPTION: Walk the GPE lists. + * + ******************************************************************************/ +acpi_status +acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + acpi_status status = AE_OK; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_walk_gpe_list); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Walk the interrupt level descriptor list */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + + /* Walk all Gpe Blocks attached to this interrupt level */ + + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + + /* One callback per GPE block */ + + status = + gpe_walk_callback(gpe_xrupt_info, gpe_block, + context); + if (ACPI_FAILURE(status)) { + if (status == AE_CTRL_END) { /* Callback abort */ + status = AE_OK; + } + goto unlock_and_exit; + } + + gpe_block = gpe_block->next; + } + + gpe_xrupt_info = gpe_xrupt_info->next; + } + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_device + * + * PARAMETERS: GPE_WALK_CALLBACK + * + * RETURN: Status + * + * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE + * block device. NULL if the GPE is one of the FADT-defined GPEs. + * + ******************************************************************************/ + +acpi_status +acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) +{ + struct acpi_gpe_device_info *info = context; + + /* Increment Index by the number of GPEs in this block */ + + info->next_block_base_index += gpe_block->gpe_count; + + if (info->index < info->next_block_base_index) { + /* + * The GPE index is within this block, get the node. Leave the node + * NULL for the FADT-defined GPEs + */ + if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) { + info->gpe_device = gpe_block->node; + } + + info->status = AE_OK; + return (AE_CTRL_END); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_xrupt_block + * + * PARAMETERS: interrupt_number - Interrupt for a GPE block + * gpe_xrupt_block - Where the block is returned + * + * RETURN: Status + * + * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt + * block per unique interrupt level used for GPEs. Should be + * called only when the GPE lists are semaphore locked and not + * subject to change. + * + ******************************************************************************/ + +acpi_status +acpi_ev_get_gpe_xrupt_block(u32 interrupt_number, + struct acpi_gpe_xrupt_info ** gpe_xrupt_block) +{ + struct acpi_gpe_xrupt_info *next_gpe_xrupt; + struct acpi_gpe_xrupt_info *gpe_xrupt; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_get_gpe_xrupt_block); + + /* No need for lock since we are not changing any list elements here */ + + next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; + while (next_gpe_xrupt) { + if (next_gpe_xrupt->interrupt_number == interrupt_number) { + *gpe_xrupt_block = next_gpe_xrupt; + return_ACPI_STATUS(AE_OK); + } + + next_gpe_xrupt = next_gpe_xrupt->next; + } + + /* Not found, must allocate a new xrupt descriptor */ + + gpe_xrupt = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_xrupt_info)); + if (!gpe_xrupt) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + gpe_xrupt->interrupt_number = interrupt_number; + + /* Install new interrupt descriptor with spin lock */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + if (acpi_gbl_gpe_xrupt_list_head) { + next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; + while (next_gpe_xrupt->next) { + next_gpe_xrupt = next_gpe_xrupt->next; + } + + next_gpe_xrupt->next = gpe_xrupt; + gpe_xrupt->previous = next_gpe_xrupt; + } else { + acpi_gbl_gpe_xrupt_list_head = gpe_xrupt; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + /* Install new interrupt handler if not SCI_INT */ + + if (interrupt_number != acpi_gbl_FADT.sci_interrupt) { + status = acpi_os_install_interrupt_handler(interrupt_number, + acpi_ev_gpe_xrupt_handler, + gpe_xrupt); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not install GPE interrupt handler at level 0x%X", + interrupt_number)); + return_ACPI_STATUS(status); + } + } + + *gpe_xrupt_block = gpe_xrupt; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_delete_gpe_xrupt + * + * PARAMETERS: gpe_xrupt - A GPE interrupt info block + * + * RETURN: Status + * + * DESCRIPTION: Remove and free a gpe_xrupt block. Remove an associated + * interrupt handler if not the SCI interrupt. + * + ******************************************************************************/ + +acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) +{ + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_delete_gpe_xrupt); + + /* We never want to remove the SCI interrupt handler */ + + if (gpe_xrupt->interrupt_number == acpi_gbl_FADT.sci_interrupt) { + gpe_xrupt->gpe_block_list_head = NULL; + return_ACPI_STATUS(AE_OK); + } + + /* Disable this interrupt */ + + status = + acpi_os_remove_interrupt_handler(gpe_xrupt->interrupt_number, + acpi_ev_gpe_xrupt_handler); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Unlink the interrupt block with lock */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + if (gpe_xrupt->previous) { + gpe_xrupt->previous->next = gpe_xrupt->next; + } else { + /* No previous, update list head */ + + acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next; + } + + if (gpe_xrupt->next) { + gpe_xrupt->next->previous = gpe_xrupt->previous; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + /* Free the block */ + + ACPI_FREE(gpe_xrupt); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_delete_gpe_handlers + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Delete all Handler objects found in the GPE data structs. + * Used only prior to termination. + * + ******************************************************************************/ + +acpi_status +acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context) +{ + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_gpe_notify_info *notify; + struct acpi_gpe_notify_info *next; + u32 i; + u32 j; + + ACPI_FUNCTION_TRACE(ev_delete_gpe_handlers); + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + gpe_event_info = &gpe_block->event_info[((acpi_size) i * + ACPI_GPE_REGISTER_WIDTH) + + j]; + + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) || + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER)) { + + /* Delete an installed handler block */ + + ACPI_FREE(gpe_event_info->dispatch.handler); + gpe_event_info->dispatch.handler = NULL; + gpe_event_info->flags &= + ~ACPI_GPE_DISPATCH_MASK; + } else if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) + == ACPI_GPE_DISPATCH_NOTIFY) { + + /* Delete the implicit notification device list */ + + notify = gpe_event_info->dispatch.notify_list; + while (notify) { + next = notify->next; + ACPI_FREE(notify); + notify = next; + } + gpe_event_info->dispatch.notify_list = NULL; + gpe_event_info->flags &= + ~ACPI_GPE_DISPATCH_MASK; + } + } + } + + return_ACPI_STATUS(AE_OK); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evhandler.c b/kernel/drivers/acpi/acpica/evhandler.c new file mode 100644 index 000000000..74e8595f5 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evhandler.c @@ -0,0 +1,536 @@ +/****************************************************************************** + * + * Module Name: evhandler - Support for Address Space handlers + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evhandler") + +/* Local prototypes */ +static acpi_status +acpi_ev_install_handler(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +/* These are the address spaces that will get default handlers */ + +u8 acpi_gbl_default_address_spaces[ACPI_NUM_DEFAULT_SPACES] = { + ACPI_ADR_SPACE_SYSTEM_MEMORY, + ACPI_ADR_SPACE_SYSTEM_IO, + ACPI_ADR_SPACE_PCI_CONFIG, + ACPI_ADR_SPACE_DATA_TABLE +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_region_handlers + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Installs the core subsystem default address space handlers. + * + ******************************************************************************/ + +acpi_status acpi_ev_install_region_handlers(void) +{ + acpi_status status; + u32 i; + + ACPI_FUNCTION_TRACE(ev_install_region_handlers); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * All address spaces (PCI Config, EC, SMBus) are scope dependent and + * registration must occur for a specific device. + * + * In the case of the system memory and IO address spaces there is + * currently no device associated with the address space. For these we + * use the root. + * + * We install the default PCI config space handler at the root so that + * this space is immediately available even though the we have not + * enumerated all the PCI Root Buses yet. This is to conform to the ACPI + * specification which states that the PCI config space must be always + * available -- even though we are nowhere near ready to find the PCI root + * buses at this point. + * + * NOTE: We ignore AE_ALREADY_EXISTS because this means that a handler + * has already been installed (via acpi_install_address_space_handler). + * Similar for AE_SAME_HANDLER. + */ + for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { + status = acpi_ev_install_space_handler(acpi_gbl_root_node, + acpi_gbl_default_address_spaces + [i], + ACPI_DEFAULT_HANDLER, + NULL, NULL); + switch (status) { + case AE_OK: + case AE_SAME_HANDLER: + case AE_ALREADY_EXISTS: + + /* These exceptions are all OK */ + + status = AE_OK; + break; + + default: + + goto unlock_and_exit; + } + } + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_has_default_handler + * + * PARAMETERS: node - Namespace node for the device + * space_id - The address space ID + * + * RETURN: TRUE if default handler is installed, FALSE otherwise + * + * DESCRIPTION: Check if the default handler is installed for the requested + * space ID. + * + ******************************************************************************/ + +u8 +acpi_ev_has_default_handler(struct acpi_namespace_node *node, + acpi_adr_space_type space_id) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + + /* Must have an existing internal object */ + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + handler_obj = obj_desc->device.handler; + + /* Walk the linked list of handlers for this object */ + + while (handler_obj) { + if (handler_obj->address_space.space_id == space_id) { + if (handler_obj->address_space.handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) { + return (TRUE); + } + } + + handler_obj = handler_obj->address_space.next; + } + } + + return (FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_handler + * + * PARAMETERS: walk_namespace callback + * + * DESCRIPTION: This routine installs an address handler into objects that are + * of type Region or Device. + * + * If the Object is a Device, and the device has a handler of + * the same type then the search is terminated in that branch. + * + * This is because the existing handler is closer in proximity + * to any more regions than the one we are trying to install. + * + ******************************************************************************/ + +static acpi_status +acpi_ev_install_handler(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + union acpi_operand_object *handler_obj; + union acpi_operand_object *next_handler_obj; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_NAME(ev_install_handler); + + handler_obj = (union acpi_operand_object *)context; + + /* Parameter validation */ + + if (!handler_obj) { + return (AE_OK); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_validate_handle(obj_handle); + if (!node) { + return (AE_BAD_PARAMETER); + } + + /* + * We only care about regions and objects that are allowed to have + * address space handlers + */ + if ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) { + return (AE_OK); + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + + /* No object, just exit */ + + return (AE_OK); + } + + /* Devices are handled different than regions */ + + if (obj_desc->common.type == ACPI_TYPE_DEVICE) { + + /* Check if this Device already has a handler for this address space */ + + next_handler_obj = obj_desc->device.handler; + while (next_handler_obj) { + + /* Found a handler, is it for the same address space? */ + + if (next_handler_obj->address_space.space_id == + handler_obj->address_space.space_id) { + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Found handler for region [%s] in device %p(%p) " + "handler %p\n", + acpi_ut_get_region_name + (handler_obj->address_space. + space_id), obj_desc, + next_handler_obj, + handler_obj)); + + /* + * Since the object we found it on was a device, then it + * means that someone has already installed a handler for + * the branch of the namespace from this device on. Just + * bail out telling the walk routine to not traverse this + * branch. This preserves the scoping rule for handlers. + */ + return (AE_CTRL_DEPTH); + } + + /* Walk the linked list of handlers attached to this device */ + + next_handler_obj = next_handler_obj->address_space.next; + } + + /* + * As long as the device didn't have a handler for this space we + * don't care about it. We just ignore it and proceed. + */ + return (AE_OK); + } + + /* Object is a Region */ + + if (obj_desc->region.space_id != handler_obj->address_space.space_id) { + + /* This region is for a different address space, just ignore it */ + + return (AE_OK); + } + + /* + * Now we have a region and it is for the handler's address space type. + * + * First disconnect region for any previous handler (if any) + */ + acpi_ev_detach_region(obj_desc, FALSE); + + /* Connect the region to the new handler */ + + status = acpi_ev_attach_region(handler_obj, obj_desc, FALSE); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_space_handler + * + * PARAMETERS: node - Namespace node for the device + * space_id - The address space ID + * handler - Address of the handler + * setup - Address of the setup function + * context - Value passed to the handler on each access + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for all op_regions of a given space_id. + * Assumes namespace is locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_install_space_handler(struct acpi_namespace_node * node, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler, + acpi_adr_space_setup setup, void *context) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + acpi_status status; + acpi_object_type type; + u8 flags = 0; + + ACPI_FUNCTION_TRACE(ev_install_space_handler); + + /* + * This registration is valid for only the types below and the root. This + * is where the default handlers get placed. + */ + if ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_PROCESSOR) && + (node->type != ACPI_TYPE_THERMAL) && (node != acpi_gbl_root_node)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + if (handler == ACPI_DEFAULT_HANDLER) { + flags = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED; + + switch (space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + + handler = acpi_ex_system_memory_space_handler; + setup = acpi_ev_system_memory_region_setup; + break; + + case ACPI_ADR_SPACE_SYSTEM_IO: + + handler = acpi_ex_system_io_space_handler; + setup = acpi_ev_io_space_region_setup; + break; + + case ACPI_ADR_SPACE_PCI_CONFIG: + + handler = acpi_ex_pci_config_space_handler; + setup = acpi_ev_pci_config_region_setup; + break; + + case ACPI_ADR_SPACE_CMOS: + + handler = acpi_ex_cmos_space_handler; + setup = acpi_ev_cmos_region_setup; + break; + + case ACPI_ADR_SPACE_PCI_BAR_TARGET: + + handler = acpi_ex_pci_bar_space_handler; + setup = acpi_ev_pci_bar_region_setup; + break; + + case ACPI_ADR_SPACE_DATA_TABLE: + + handler = acpi_ex_data_table_space_handler; + setup = NULL; + break; + + default: + + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + /* If the caller hasn't specified a setup routine, use the default */ + + if (!setup) { + setup = acpi_ev_default_region_setup; + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + /* + * The attached device object already exists. Make sure the handler + * is not already installed. + */ + handler_obj = obj_desc->device.handler; + + /* Walk the handler list for this device */ + + while (handler_obj) { + + /* Same space_id indicates a handler already installed */ + + if (handler_obj->address_space.space_id == space_id) { + if (handler_obj->address_space.handler == + handler) { + /* + * It is (relatively) OK to attempt to install the SAME + * handler twice. This can easily happen with the + * PCI_Config space. + */ + status = AE_SAME_HANDLER; + goto unlock_and_exit; + } else { + /* A handler is already installed */ + + status = AE_ALREADY_EXISTS; + } + goto unlock_and_exit; + } + + /* Walk the linked list of handlers */ + + handler_obj = handler_obj->address_space.next; + } + } else { + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Creating object on Device %p while installing handler\n", + node)); + + /* obj_desc does not exist, create one */ + + if (node->type == ACPI_TYPE_ANY) { + type = ACPI_TYPE_DEVICE; + } else { + type = node->type; + } + + obj_desc = acpi_ut_create_internal_object(type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Init new descriptor */ + + obj_desc->common.type = (u8)type; + + /* Attach the new object to the Node */ + + status = acpi_ns_attach_object(node, obj_desc, type); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Installing address handler for region %s(%X) on Device %4.4s %p(%p)\n", + acpi_ut_get_region_name(space_id), space_id, + acpi_ut_get_node_name(node), node, obj_desc)); + + /* + * Install the handler + * + * At this point there is no existing handler. Just allocate the object + * for the handler and link it into the list. + */ + handler_obj = + acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_ADDRESS_HANDLER); + if (!handler_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Init handler obj */ + + handler_obj->address_space.space_id = (u8)space_id; + handler_obj->address_space.handler_flags = flags; + handler_obj->address_space.region_list = NULL; + handler_obj->address_space.node = node; + handler_obj->address_space.handler = handler; + handler_obj->address_space.context = context; + handler_obj->address_space.setup = setup; + + /* Install at head of Device.address_space list */ + + handler_obj->address_space.next = obj_desc->device.handler; + + /* + * The Device object is the first reference on the handler_obj. + * Each region that uses the handler adds a reference. + */ + obj_desc->device.handler = handler_obj; + + /* + * Walk the namespace finding all of the regions this + * handler will manage. + * + * Start at the device and search the branch toward + * the leaf nodes until either the leaf is encountered or + * a device is detected that has an address handler of the + * same type. + * + * In either case, back up and search down the remainder + * of the branch + */ + status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, + acpi_ev_install_handler, NULL, + handler_obj, NULL); + +unlock_and_exit: + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/evmisc.c b/kernel/drivers/acpi/acpica/evmisc.c new file mode 100644 index 000000000..f7c9dfe7b --- /dev/null +++ b/kernel/drivers/acpi/acpica/evmisc.c @@ -0,0 +1,299 @@ +/****************************************************************************** + * + * Module Name: evmisc - Miscellaneous event manager support functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evmisc") + +/* Local prototypes */ +static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_is_notify_object + * + * PARAMETERS: node - Node to check + * + * RETURN: TRUE if notifies allowed on this object + * + * DESCRIPTION: Check type of node for a object that supports notifies. + * + * TBD: This could be replaced by a flag bit in the node. + * + ******************************************************************************/ + +u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node) +{ + switch (node->type) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + /* + * These are the ONLY objects that can receive ACPI notifications + */ + return (TRUE); + + default: + + return (FALSE); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_queue_notify_request + * + * PARAMETERS: node - NS node for the notified object + * notify_value - Value from the Notify() request + * + * RETURN: Status + * + * DESCRIPTION: Dispatch a device notification event to a previously + * installed handler. + * + ******************************************************************************/ + +acpi_status +acpi_ev_queue_notify_request(struct acpi_namespace_node * node, + u32 notify_value) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_list_head = NULL; + union acpi_generic_state *info; + u8 handler_list_id = 0; + acpi_status status = AE_OK; + + ACPI_FUNCTION_NAME(ev_queue_notify_request); + + /* Are Notifies allowed on this object? */ + + if (!acpi_ev_is_notify_object(node)) { + return (AE_TYPE); + } + + /* Get the correct notify list type (System or Device) */ + + if (notify_value <= ACPI_MAX_SYS_NOTIFY) { + handler_list_id = ACPI_SYSTEM_HANDLER_LIST; + } else { + handler_list_id = ACPI_DEVICE_HANDLER_LIST; + } + + /* Get the notify object attached to the namespace Node */ + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + + /* We have an attached object, Get the correct handler list */ + + handler_list_head = + obj_desc->common_notify.notify_list[handler_list_id]; + } + + /* + * If there is no notify handler (Global or Local) + * for this object, just ignore the notify + */ + if (!acpi_gbl_global_notify[handler_list_id].handler + && !handler_list_head) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No notify handler for Notify, ignoring (%4.4s, %X) node %p\n", + acpi_ut_get_node_name(node), notify_value, + node)); + + return (AE_OK); + } + + /* Setup notify info and schedule the notify dispatcher */ + + info = acpi_ut_create_generic_state(); + if (!info) { + return (AE_NO_MEMORY); + } + + info->common.descriptor_type = ACPI_DESC_TYPE_STATE_NOTIFY; + + info->notify.node = node; + info->notify.value = (u16)notify_value; + info->notify.handler_list_id = handler_list_id; + info->notify.handler_list_head = handler_list_head; + info->notify.global = &acpi_gbl_global_notify[handler_list_id]; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type), notify_value, + acpi_ut_get_notify_name(notify_value, ACPI_TYPE_ANY), + node)); + + status = acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch, + info); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_generic_state(info); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_notify_dispatch + * + * PARAMETERS: context - To be passed to the notify handler + * + * RETURN: None. + * + * DESCRIPTION: Dispatch a device notification event to a previously + * installed handler. + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) +{ + union acpi_generic_state *info = (union acpi_generic_state *)context; + union acpi_operand_object *handler_obj; + + ACPI_FUNCTION_ENTRY(); + + /* Invoke a global notify handler if installed */ + + if (info->notify.global->handler) { + info->notify.global->handler(info->notify.node, + info->notify.value, + info->notify.global->context); + } + + /* Now invoke the local notify handler(s) if any are installed */ + + handler_obj = info->notify.handler_list_head; + while (handler_obj) { + handler_obj->notify.handler(info->notify.node, + info->notify.value, + handler_obj->notify.context); + + handler_obj = + handler_obj->notify.next[info->notify.handler_list_id]; + } + + /* All done with the info object */ + + acpi_ut_delete_generic_state(info); +} + +#if (!ACPI_REDUCED_HARDWARE) +/****************************************************************************** + * + * FUNCTION: acpi_ev_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Disable events and free memory allocated for table storage. + * + ******************************************************************************/ + +void acpi_ev_terminate(void) +{ + u32 i; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_terminate); + + if (acpi_gbl_events_initialized) { + /* + * Disable all event-related functionality. In all cases, on error, + * print a message but obviously we don't abort. + */ + + /* Disable all fixed events */ + + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + status = acpi_disable_event(i, 0); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not disable fixed event %u", + (u32) i)); + } + } + + /* Disable all GPEs in all GPE blocks */ + + status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL); + + status = acpi_ev_remove_global_lock_handler(); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not remove Global Lock handler")); + } + + acpi_gbl_events_initialized = FALSE; + } + + /* Remove SCI handlers */ + + status = acpi_ev_remove_all_sci_handlers(); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Could not remove SCI handler")); + } + + /* Deallocate all handler objects installed within GPE info structs */ + + status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers, NULL); + + /* Return to original mode if necessary */ + + if (acpi_gbl_original_mode == ACPI_SYS_MODE_LEGACY) { + status = acpi_disable(); + if (ACPI_FAILURE(status)) { + ACPI_WARNING((AE_INFO, "AcpiDisable failed")); + } + } + return_VOID; +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evregion.c b/kernel/drivers/acpi/acpica/evregion.c new file mode 100644 index 000000000..2ba28a63f --- /dev/null +++ b/kernel/drivers/acpi/acpica/evregion.c @@ -0,0 +1,795 @@ +/****************************************************************************** + * + * Module Name: evregion - Operation Region support + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evregion") + +extern u8 acpi_gbl_default_address_spaces[]; + +/* Local prototypes */ + +static void +acpi_ev_orphan_ec_reg_method(struct acpi_namespace_node *ec_device_node); + +static acpi_status +acpi_ev_reg_run(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_op_regions + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Execute _REG methods for all Operation Regions that have + * an installed default region handler. + * + ******************************************************************************/ + +acpi_status acpi_ev_initialize_op_regions(void) +{ + acpi_status status; + u32 i; + + ACPI_FUNCTION_TRACE(ev_initialize_op_regions); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Run the _REG methods for op_regions in each default address space */ + + for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { + /* + * Make sure the installed handler is the DEFAULT handler. If not the + * default, the _REG methods will have already been run (when the + * handler was installed) + */ + if (acpi_ev_has_default_handler(acpi_gbl_root_node, + acpi_gbl_default_address_spaces + [i])) { + status = + acpi_ev_execute_reg_methods(acpi_gbl_root_node, + acpi_gbl_default_address_spaces + [i]); + } + } + + acpi_gbl_reg_methods_executed = TRUE; + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_address_space_dispatch + * + * PARAMETERS: region_obj - Internal region object + * field_obj - Corresponding field. Can be NULL. + * function - Read or Write operation + * region_offset - Where in the region to read or write + * bit_width - Field width in bits (8, 16, 32, or 64) + * value - Pointer to in or out value, must be + * a full 64-bit integer + * + * RETURN: Status + * + * DESCRIPTION: Dispatch an address space or operation region access to + * a previously installed handler. + * + ******************************************************************************/ + +acpi_status +acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + union acpi_operand_object *field_obj, + u32 function, + u32 region_offset, u32 bit_width, u64 *value) +{ + acpi_status status; + acpi_adr_space_handler handler; + acpi_adr_space_setup region_setup; + union acpi_operand_object *handler_desc; + union acpi_operand_object *region_obj2; + void *region_context = NULL; + struct acpi_connection_info *context; + acpi_physical_address address; + + ACPI_FUNCTION_TRACE(ev_address_space_dispatch); + + region_obj2 = acpi_ns_get_secondary_object(region_obj); + if (!region_obj2) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Ensure that there is a handler associated with this region */ + + handler_desc = region_obj->region.handler; + if (!handler_desc) { + ACPI_ERROR((AE_INFO, + "No handler for Region [%4.4s] (%p) [%s]", + acpi_ut_get_node_name(region_obj->region.node), + region_obj, + acpi_ut_get_region_name(region_obj->region. + space_id))); + + return_ACPI_STATUS(AE_NOT_EXIST); + } + + context = handler_desc->address_space.context; + + /* + * It may be the case that the region has never been initialized. + * Some types of regions require special init code + */ + if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { + + /* This region has not been initialized yet, do it */ + + region_setup = handler_desc->address_space.setup; + if (!region_setup) { + + /* No initialization routine, exit with error */ + + ACPI_ERROR((AE_INFO, + "No init routine for region(%p) [%s]", + region_obj, + acpi_ut_get_region_name(region_obj->region. + space_id))); + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* + * We must exit the interpreter because the region setup will + * potentially execute control methods (for example, the _REG method + * for this region) + */ + acpi_ex_exit_interpreter(); + + status = region_setup(region_obj, ACPI_REGION_ACTIVATE, + context, ®ion_context); + + /* Re-enter the interpreter */ + + acpi_ex_enter_interpreter(); + + /* Check for failure of the Region Setup */ + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During region initialization: [%s]", + acpi_ut_get_region_name(region_obj-> + region. + space_id))); + return_ACPI_STATUS(status); + } + + /* Region initialization may have been completed by region_setup */ + + if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { + region_obj->region.flags |= AOPOBJ_SETUP_COMPLETE; + + /* + * Save the returned context for use in all accesses to + * the handler for this particular region + */ + if (!(region_obj2->extra.region_context)) { + region_obj2->extra.region_context = + region_context; + } + } + } + + /* We have everything we need, we can invoke the address space handler */ + + handler = handler_desc->address_space.handler; + address = (region_obj->region.address + region_offset); + + /* + * Special handling for generic_serial_bus and general_purpose_io: + * There are three extra parameters that must be passed to the + * handler via the context: + * 1) Connection buffer, a resource template from Connection() op + * 2) Length of the above buffer + * 3) Actual access length from the access_as() op + * + * In addition, for general_purpose_io, the Address and bit_width fields + * are defined as follows: + * 1) Address is the pin number index of the field (bit offset from + * the previous Connection) + * 2) bit_width is the actual bit length of the field (number of pins) + */ + if ((region_obj->region.space_id == ACPI_ADR_SPACE_GSBUS) && + context && field_obj) { + + /* Get the Connection (resource_template) buffer */ + + context->connection = field_obj->field.resource_buffer; + context->length = field_obj->field.resource_length; + context->access_length = field_obj->field.access_length; + } + if ((region_obj->region.space_id == ACPI_ADR_SPACE_GPIO) && + context && field_obj) { + + /* Get the Connection (resource_template) buffer */ + + context->connection = field_obj->field.resource_buffer; + context->length = field_obj->field.resource_length; + context->access_length = field_obj->field.access_length; + address = field_obj->field.pin_number_index; + bit_width = field_obj->field.bit_length; + } + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Handler %p (@%p) Address %8.8X%8.8X [%s]\n", + ®ion_obj->region.handler->address_space, handler, + ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(region_obj->region. + space_id))); + + if (!(handler_desc->address_space.handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { + /* + * For handlers other than the default (supplied) handlers, we must + * exit the interpreter because the handler *might* block -- we don't + * know what it will do, so we can't hold the lock on the intepreter. + */ + acpi_ex_exit_interpreter(); + } + + /* Call the handler */ + + status = handler(function, address, bit_width, value, context, + region_obj2->extra.region_context); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Returned by Handler for [%s]", + acpi_ut_get_region_name(region_obj->region. + space_id))); + } + + if (!(handler_desc->address_space.handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { + /* + * We just returned from a non-default handler, we must re-enter the + * interpreter + */ + acpi_ex_enter_interpreter(); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_detach_region + * + * PARAMETERS: region_obj - Region Object + * acpi_ns_is_locked - Namespace Region Already Locked? + * + * RETURN: None + * + * DESCRIPTION: Break the association between the handler and the region + * this is a two way association. + * + ******************************************************************************/ + +void +acpi_ev_detach_region(union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked) +{ + union acpi_operand_object *handler_obj; + union acpi_operand_object *obj_desc; + union acpi_operand_object *start_desc; + union acpi_operand_object **last_obj_ptr; + acpi_adr_space_setup region_setup; + void **region_context; + union acpi_operand_object *region_obj2; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_detach_region); + + region_obj2 = acpi_ns_get_secondary_object(region_obj); + if (!region_obj2) { + return_VOID; + } + region_context = ®ion_obj2->extra.region_context; + + /* Get the address handler from the region object */ + + handler_obj = region_obj->region.handler; + if (!handler_obj) { + + /* This region has no handler, all done */ + + return_VOID; + } + + /* Find this region in the handler's list */ + + obj_desc = handler_obj->address_space.region_list; + start_desc = obj_desc; + last_obj_ptr = &handler_obj->address_space.region_list; + + while (obj_desc) { + + /* Is this the correct Region? */ + + if (obj_desc == region_obj) { + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Removing Region %p from address handler %p\n", + region_obj, handler_obj)); + + /* This is it, remove it from the handler's list */ + + *last_obj_ptr = obj_desc->region.next; + obj_desc->region.next = NULL; /* Must clear field */ + + if (acpi_ns_is_locked) { + status = + acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_VOID; + } + } + + /* Now stop region accesses by executing the _REG method */ + + status = + acpi_ev_execute_reg_method(region_obj, + ACPI_REG_DISCONNECT); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "from region _REG, [%s]", + acpi_ut_get_region_name + (region_obj->region.space_id))); + } + + if (acpi_ns_is_locked) { + status = + acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_VOID; + } + } + + /* + * If the region has been activated, call the setup handler with + * the deactivate notification + */ + if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) { + region_setup = handler_obj->address_space.setup; + status = + region_setup(region_obj, + ACPI_REGION_DEACTIVATE, + handler_obj->address_space. + context, region_context); + + /* + * region_context should have been released by the deactivate + * operation. We don't need access to it anymore here. + */ + if (region_context) { + *region_context = NULL; + } + + /* Init routine may fail, Just ignore errors */ + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "from region handler - deactivate, [%s]", + acpi_ut_get_region_name + (region_obj->region. + space_id))); + } + + region_obj->region.flags &= + ~(AOPOBJ_SETUP_COMPLETE); + } + + /* + * Remove handler reference in the region + * + * NOTE: this doesn't mean that the region goes away, the region + * is just inaccessible as indicated to the _REG method + * + * If the region is on the handler's list, this must be the + * region's handler + */ + region_obj->region.handler = NULL; + acpi_ut_remove_reference(handler_obj); + + return_VOID; + } + + /* Walk the linked list of handlers */ + + last_obj_ptr = &obj_desc->region.next; + obj_desc = obj_desc->region.next; + + /* Prevent infinite loop if list is corrupted */ + + if (obj_desc == start_desc) { + ACPI_ERROR((AE_INFO, + "Circular handler list in region object %p", + region_obj)); + return_VOID; + } + } + + /* If we get here, the region was not in the handler's region list */ + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Cannot remove region %p from address handler %p\n", + region_obj, handler_obj)); + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_attach_region + * + * PARAMETERS: handler_obj - Handler Object + * region_obj - Region Object + * acpi_ns_is_locked - Namespace Region Already Locked? + * + * RETURN: None + * + * DESCRIPTION: Create the association between the handler and the region + * this is a two way association. + * + ******************************************************************************/ + +acpi_status +acpi_ev_attach_region(union acpi_operand_object *handler_obj, + union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked) +{ + + ACPI_FUNCTION_TRACE(ev_attach_region); + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Adding Region [%4.4s] %p to address handler %p [%s]\n", + acpi_ut_get_node_name(region_obj->region.node), + region_obj, handler_obj, + acpi_ut_get_region_name(region_obj->region. + space_id))); + + /* Link this region to the front of the handler's list */ + + region_obj->region.next = handler_obj->address_space.region_list; + handler_obj->address_space.region_list = region_obj; + + /* Install the region's handler */ + + if (region_obj->region.handler) { + return_ACPI_STATUS(AE_ALREADY_EXISTS); + } + + region_obj->region.handler = handler_obj; + acpi_ut_add_reference(handler_obj); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_execute_reg_method + * + * PARAMETERS: region_obj - Region object + * function - Passed to _REG: On (1) or Off (0) + * + * RETURN: Status + * + * DESCRIPTION: Execute _REG method for a region + * + ******************************************************************************/ + +acpi_status +acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) +{ + struct acpi_evaluate_info *info; + union acpi_operand_object *args[3]; + union acpi_operand_object *region_obj2; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_execute_reg_method); + + region_obj2 = acpi_ns_get_secondary_object(region_obj); + if (!region_obj2) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + if (region_obj2->extra.method_REG == NULL) { + return_ACPI_STATUS(AE_OK); + } + + /* Allocate and initialize the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + info->prefix_node = region_obj2->extra.method_REG; + info->relative_pathname = NULL; + info->parameters = args; + info->flags = ACPI_IGNORE_RETURN_VALUE; + + /* + * The _REG method has two arguments: + * + * arg0 - Integer: + * Operation region space ID Same value as region_obj->Region.space_id + * + * arg1 - Integer: + * connection status 1 for connecting the handler, 0 for disconnecting + * the handler (Passed as a parameter) + */ + args[0] = + acpi_ut_create_integer_object((u64)region_obj->region.space_id); + if (!args[0]) { + status = AE_NO_MEMORY; + goto cleanup1; + } + + args[1] = acpi_ut_create_integer_object((u64)function); + if (!args[1]) { + status = AE_NO_MEMORY; + goto cleanup2; + } + + args[2] = NULL; /* Terminate list */ + + /* Execute the method, no return value */ + + ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname + (ACPI_TYPE_METHOD, info->prefix_node, NULL)); + + status = acpi_ns_evaluate(info); + acpi_ut_remove_reference(args[1]); + +cleanup2: + acpi_ut_remove_reference(args[0]); + +cleanup1: + ACPI_FREE(info); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_execute_reg_methods + * + * PARAMETERS: node - Namespace node for the device + * space_id - The address space ID + * + * RETURN: Status + * + * DESCRIPTION: Run all _REG methods for the input Space ID; + * Note: assumes namespace is locked, or system init time. + * + ******************************************************************************/ + +acpi_status +acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, + acpi_adr_space_type space_id) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_execute_reg_methods); + + /* + * Run all _REG methods for all Operation Regions for this space ID. This + * is a separate walk in order to handle any interdependencies between + * regions and _REG methods. (i.e. handlers must be installed for all + * regions of this Space ID before we can run any _REG methods) + */ + status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, + NULL, &space_id, NULL); + + /* Special case for EC: handle "orphan" _REG methods with no region */ + + if (space_id == ACPI_ADR_SPACE_EC) { + acpi_ev_orphan_ec_reg_method(node); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_reg_run + * + * PARAMETERS: walk_namespace callback + * + * DESCRIPTION: Run _REG method for region objects of the requested spaceID + * + ******************************************************************************/ + +static acpi_status +acpi_ev_reg_run(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + acpi_adr_space_type space_id; + acpi_status status; + + space_id = *ACPI_CAST_PTR(acpi_adr_space_type, context); + + /* Convert and validate the device handle */ + + node = acpi_ns_validate_handle(obj_handle); + if (!node) { + return (AE_BAD_PARAMETER); + } + + /* + * We only care about regions.and objects that are allowed to have address + * space handlers + */ + if ((node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) { + return (AE_OK); + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + + /* No object, just exit */ + + return (AE_OK); + } + + /* Object is a Region */ + + if (obj_desc->region.space_id != space_id) { + + /* This region is for a different address space, just ignore it */ + + return (AE_OK); + } + + status = acpi_ev_execute_reg_method(obj_desc, ACPI_REG_CONNECT); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_orphan_ec_reg_method + * + * PARAMETERS: ec_device_node - Namespace node for an EC device + * + * RETURN: None + * + * DESCRIPTION: Execute an "orphan" _REG method that appears under the EC + * device. This is a _REG method that has no corresponding region + * within the EC device scope. The orphan _REG method appears to + * have been enabled by the description of the ECDT in the ACPI + * specification: "The availability of the region space can be + * detected by providing a _REG method object underneath the + * Embedded Controller device." + * + * To quickly access the EC device, we use the ec_device_node used + * during EC handler installation. Otherwise, we would need to + * perform a time consuming namespace walk, executing _HID + * methods to find the EC device. + * + * MUTEX: Assumes the namespace is locked + * + ******************************************************************************/ + +static void +acpi_ev_orphan_ec_reg_method(struct acpi_namespace_node *ec_device_node) +{ + acpi_handle reg_method; + struct acpi_namespace_node *next_node; + acpi_status status; + struct acpi_object_list args; + union acpi_object objects[2]; + + ACPI_FUNCTION_TRACE(ev_orphan_ec_reg_method); + + if (!ec_device_node) { + return_VOID; + } + + /* Namespace is currently locked, must release */ + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + /* Get a handle to a _REG method immediately under the EC device */ + + status = acpi_get_handle(ec_device_node, METHOD_NAME__REG, ®_method); + if (ACPI_FAILURE(status)) { + goto exit; /* There is no _REG method present */ + } + + /* + * Execute the _REG method only if there is no Operation Region in + * this scope with the Embedded Controller space ID. Otherwise, it + * will already have been executed. Note, this allows for Regions + * with other space IDs to be present; but the code below will then + * execute the _REG method with the embedded_control space_ID argument. + */ + next_node = acpi_ns_get_next_node(ec_device_node, NULL); + while (next_node) { + if ((next_node->type == ACPI_TYPE_REGION) && + (next_node->object) && + (next_node->object->region.space_id == ACPI_ADR_SPACE_EC)) { + goto exit; /* Do not execute the _REG */ + } + + next_node = acpi_ns_get_next_node(ec_device_node, next_node); + } + + /* Evaluate the _REG(embedded_control,Connect) method */ + + args.count = 2; + args.pointer = objects; + objects[0].type = ACPI_TYPE_INTEGER; + objects[0].integer.value = ACPI_ADR_SPACE_EC; + objects[1].type = ACPI_TYPE_INTEGER; + objects[1].integer.value = ACPI_REG_CONNECT; + + status = acpi_evaluate_object(reg_method, NULL, &args, NULL); + +exit: + /* We ignore all errors from above, don't care */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/evrgnini.c b/kernel/drivers/acpi/acpica/evrgnini.c new file mode 100644 index 000000000..da323390b --- /dev/null +++ b/kernel/drivers/acpi/acpica/evrgnini.c @@ -0,0 +1,675 @@ +/****************************************************************************** + * + * Module Name: evrgnini- ACPI address_space (op_region) init + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evrgnini") + +/* Local prototypes */ +static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_system_memory_region_setup + * + * PARAMETERS: handle - Region we are interested in + * function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Setup a system_memory operation region + * + ******************************************************************************/ + +acpi_status +acpi_ev_system_memory_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context) +{ + union acpi_operand_object *region_desc = + (union acpi_operand_object *)handle; + struct acpi_mem_space_context *local_region_context; + + ACPI_FUNCTION_TRACE(ev_system_memory_region_setup); + + if (function == ACPI_REGION_DEACTIVATE) { + if (*region_context) { + local_region_context = + (struct acpi_mem_space_context *)*region_context; + + /* Delete a cached mapping if present */ + + if (local_region_context->mapped_length) { + acpi_os_unmap_memory(local_region_context-> + mapped_logical_address, + local_region_context-> + mapped_length); + } + ACPI_FREE(local_region_context); + *region_context = NULL; + } + return_ACPI_STATUS(AE_OK); + } + + /* Create a new context */ + + local_region_context = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_mem_space_context)); + if (!(local_region_context)) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Save the region length and address for use in the handler */ + + local_region_context->length = region_desc->region.length; + local_region_context->address = region_desc->region.address; + + *region_context = local_region_context; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_io_space_region_setup + * + * PARAMETERS: handle - Region we are interested in + * function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Setup a IO operation region + * + ******************************************************************************/ + +acpi_status +acpi_ev_io_space_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context) +{ + ACPI_FUNCTION_TRACE(ev_io_space_region_setup); + + if (function == ACPI_REGION_DEACTIVATE) { + *region_context = NULL; + } else { + *region_context = handler_context; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_pci_config_region_setup + * + * PARAMETERS: handle - Region we are interested in + * function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Setup a PCI_Config operation region + * + * MUTEX: Assumes namespace is not locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_pci_config_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context) +{ + acpi_status status = AE_OK; + u64 pci_value; + struct acpi_pci_id *pci_id = *region_context; + union acpi_operand_object *handler_obj; + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *pci_root_node; + struct acpi_namespace_node *pci_device_node; + union acpi_operand_object *region_obj = + (union acpi_operand_object *)handle; + + ACPI_FUNCTION_TRACE(ev_pci_config_region_setup); + + handler_obj = region_obj->region.handler; + if (!handler_obj) { + /* + * No installed handler. This shouldn't happen because the dispatch + * routine checks before we get here, but we check again just in case. + */ + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Attempting to init a region %p, with no handler\n", + region_obj)); + return_ACPI_STATUS(AE_NOT_EXIST); + } + + *region_context = NULL; + if (function == ACPI_REGION_DEACTIVATE) { + if (pci_id) { + ACPI_FREE(pci_id); + } + return_ACPI_STATUS(status); + } + + parent_node = region_obj->region.node->parent; + + /* + * Get the _SEG and _BBN values from the device upon which the handler + * is installed. + * + * We need to get the _SEG and _BBN objects relative to the PCI BUS device. + * This is the device the handler has been registered to handle. + */ + + /* + * If the address_space.Node is still pointing to the root, we need + * to scan upward for a PCI Root bridge and re-associate the op_region + * handlers with that device. + */ + if (handler_obj->address_space.node == acpi_gbl_root_node) { + + /* Start search from the parent object */ + + pci_root_node = parent_node; + while (pci_root_node != acpi_gbl_root_node) { + + /* Get the _HID/_CID in order to detect a root_bridge */ + + if (acpi_ev_is_pci_root_bridge(pci_root_node)) { + + /* Install a handler for this PCI root bridge */ + + status = acpi_install_address_space_handler((acpi_handle) pci_root_node, ACPI_ADR_SPACE_PCI_CONFIG, ACPI_DEFAULT_HANDLER, NULL, NULL); + if (ACPI_FAILURE(status)) { + if (status == AE_SAME_HANDLER) { + /* + * It is OK if the handler is already installed on the + * root bridge. Still need to return a context object + * for the new PCI_Config operation region, however. + */ + status = AE_OK; + } else { + ACPI_EXCEPTION((AE_INFO, status, + "Could not install PciConfig handler " + "for Root Bridge %4.4s", + acpi_ut_get_node_name + (pci_root_node))); + } + } + break; + } + + pci_root_node = pci_root_node->parent; + } + + /* PCI root bridge not found, use namespace root node */ + } else { + pci_root_node = handler_obj->address_space.node; + } + + /* + * If this region is now initialized, we are done. + * (install_address_space_handler could have initialized it) + */ + if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) { + return_ACPI_STATUS(AE_OK); + } + + /* Region is still not initialized. Create a new context */ + + pci_id = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pci_id)); + if (!pci_id) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* + * For PCI_Config space access, we need the segment, bus, device and + * function numbers. Acquire them here. + * + * Find the parent device object. (This allows the operation region to be + * within a subscope under the device, such as a control method.) + */ + pci_device_node = region_obj->region.node; + while (pci_device_node && (pci_device_node->type != ACPI_TYPE_DEVICE)) { + pci_device_node = pci_device_node->parent; + } + + if (!pci_device_node) { + ACPI_FREE(pci_id); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* + * Get the PCI device and function numbers from the _ADR object + * contained in the parent's scope. + */ + status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, + pci_device_node, &pci_value); + + /* + * The default is zero, and since the allocation above zeroed the data, + * just do nothing on failure. + */ + if (ACPI_SUCCESS(status)) { + pci_id->device = ACPI_HIWORD(ACPI_LODWORD(pci_value)); + pci_id->function = ACPI_LOWORD(ACPI_LODWORD(pci_value)); + } + + /* The PCI segment number comes from the _SEG method */ + + status = acpi_ut_evaluate_numeric_object(METHOD_NAME__SEG, + pci_root_node, &pci_value); + if (ACPI_SUCCESS(status)) { + pci_id->segment = ACPI_LOWORD(pci_value); + } + + /* The PCI bus number comes from the _BBN method */ + + status = acpi_ut_evaluate_numeric_object(METHOD_NAME__BBN, + pci_root_node, &pci_value); + if (ACPI_SUCCESS(status)) { + pci_id->bus = ACPI_LOWORD(pci_value); + } + + /* Complete/update the PCI ID for this device */ + + status = + acpi_hw_derive_pci_id(pci_id, pci_root_node, + region_obj->region.node); + if (ACPI_FAILURE(status)) { + ACPI_FREE(pci_id); + return_ACPI_STATUS(status); + } + + *region_context = pci_id; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_is_pci_root_bridge + * + * PARAMETERS: node - Device node being examined + * + * RETURN: TRUE if device is a PCI/PCI-Express Root Bridge + * + * DESCRIPTION: Determine if the input device represents a PCI Root Bridge by + * examining the _HID and _CID for the device. + * + ******************************************************************************/ + +static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) +{ + acpi_status status; + struct acpi_pnp_device_id *hid; + struct acpi_pnp_device_id_list *cid; + u32 i; + u8 match; + + /* Get the _HID and check for a PCI Root Bridge */ + + status = acpi_ut_execute_HID(node, &hid); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + + match = acpi_ut_is_pci_root_bridge(hid->string); + ACPI_FREE(hid); + + if (match) { + return (TRUE); + } + + /* The _HID did not match. Get the _CID and check for a PCI Root Bridge */ + + status = acpi_ut_execute_CID(node, &cid); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + + /* Check all _CIDs in the returned list */ + + for (i = 0; i < cid->count; i++) { + if (acpi_ut_is_pci_root_bridge(cid->ids[i].string)) { + ACPI_FREE(cid); + return (TRUE); + } + } + + ACPI_FREE(cid); + return (FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_pci_bar_region_setup + * + * PARAMETERS: handle - Region we are interested in + * function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Setup a pci_BAR operation region + * + * MUTEX: Assumes namespace is not locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_pci_bar_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context) +{ + ACPI_FUNCTION_TRACE(ev_pci_bar_region_setup); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_cmos_region_setup + * + * PARAMETERS: handle - Region we are interested in + * function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Setup a CMOS operation region + * + * MUTEX: Assumes namespace is not locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_cmos_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context) +{ + ACPI_FUNCTION_TRACE(ev_cmos_region_setup); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_default_region_setup + * + * PARAMETERS: handle - Region we are interested in + * function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Default region initialization + * + ******************************************************************************/ + +acpi_status +acpi_ev_default_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context) +{ + ACPI_FUNCTION_TRACE(ev_default_region_setup); + + if (function == ACPI_REGION_DEACTIVATE) { + *region_context = NULL; + } else { + *region_context = handler_context; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_region + * + * PARAMETERS: region_obj - Region we are initializing + * acpi_ns_locked - Is namespace locked? + * + * RETURN: Status + * + * DESCRIPTION: Initializes the region, finds any _REG methods and saves them + * for execution at a later time + * + * Get the appropriate address space handler for a newly + * created region. + * + * This also performs address space specific initialization. For + * example, PCI regions must have an _ADR object that contains + * a PCI address in the scope of the definition. This address is + * required to perform an access to PCI config space. + * + * MUTEX: Interpreter should be unlocked, because we may run the _REG + * method for this region. + * + ******************************************************************************/ + +acpi_status +acpi_ev_initialize_region(union acpi_operand_object *region_obj, + u8 acpi_ns_locked) +{ + union acpi_operand_object *handler_obj; + union acpi_operand_object *obj_desc; + acpi_adr_space_type space_id; + struct acpi_namespace_node *node; + acpi_status status; + struct acpi_namespace_node *method_node; + acpi_name *reg_name_ptr = (acpi_name *) METHOD_NAME__REG; + union acpi_operand_object *region_obj2; + + ACPI_FUNCTION_TRACE_U32(ev_initialize_region, acpi_ns_locked); + + if (!region_obj) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (region_obj->common.flags & AOPOBJ_OBJECT_INITIALIZED) { + return_ACPI_STATUS(AE_OK); + } + + region_obj2 = acpi_ns_get_secondary_object(region_obj); + if (!region_obj2) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + node = region_obj->region.node->parent; + space_id = region_obj->region.space_id; + + /* Setup defaults */ + + region_obj->region.handler = NULL; + region_obj2->extra.method_REG = NULL; + region_obj->common.flags &= ~(AOPOBJ_SETUP_COMPLETE); + region_obj->common.flags |= AOPOBJ_OBJECT_INITIALIZED; + + /* Find any "_REG" method associated with this region definition */ + + status = + acpi_ns_search_one_scope(*reg_name_ptr, node, ACPI_TYPE_METHOD, + &method_node); + if (ACPI_SUCCESS(status)) { + /* + * The _REG method is optional and there can be only one per region + * definition. This will be executed when the handler is attached + * or removed + */ + region_obj2->extra.method_REG = method_node; + } + + /* + * The following loop depends upon the root Node having no parent + * ie: acpi_gbl_root_node->parent_entry being set to NULL + */ + while (node) { + + /* Check to see if a handler exists */ + + handler_obj = NULL; + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + + /* Can only be a handler if the object exists */ + + switch (node->type) { + case ACPI_TYPE_DEVICE: + + handler_obj = obj_desc->device.handler; + break; + + case ACPI_TYPE_PROCESSOR: + + handler_obj = obj_desc->processor.handler; + break; + + case ACPI_TYPE_THERMAL: + + handler_obj = obj_desc->thermal_zone.handler; + break; + + case ACPI_TYPE_METHOD: + /* + * If we are executing module level code, the original + * Node's object was replaced by this Method object and we + * saved the handler in the method object. + * + * See acpi_ns_exec_module_code + */ + if (obj_desc->method. + info_flags & ACPI_METHOD_MODULE_LEVEL) { + handler_obj = + obj_desc->method.dispatch.handler; + } + break; + + default: + + /* Ignore other objects */ + + break; + } + + while (handler_obj) { + + /* Is this handler of the correct type? */ + + if (handler_obj->address_space.space_id == + space_id) { + + /* Found correct handler */ + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Found handler %p for region %p in obj %p\n", + handler_obj, + region_obj, + obj_desc)); + + status = + acpi_ev_attach_region(handler_obj, + region_obj, + acpi_ns_locked); + + /* + * Tell all users that this region is usable by + * running the _REG method + */ + if (acpi_ns_locked) { + status = + acpi_ut_release_mutex + (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS + (status); + } + } + + status = + acpi_ev_execute_reg_method + (region_obj, ACPI_REG_CONNECT); + + if (acpi_ns_locked) { + status = + acpi_ut_acquire_mutex + (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS + (status); + } + } + + return_ACPI_STATUS(AE_OK); + } + + /* Try next handler in the list */ + + handler_obj = handler_obj->address_space.next; + } + } + + /* This node does not have the handler we need; Pop up one level */ + + node = node->parent; + } + + /* If we get here, there is no handler for this region */ + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "No handler for RegionType %s(%X) (RegionObj %p)\n", + acpi_ut_get_region_name(space_id), space_id, + region_obj)); + + return_ACPI_STATUS(AE_NOT_EXIST); +} diff --git a/kernel/drivers/acpi/acpica/evsci.c b/kernel/drivers/acpi/acpica/evsci.c new file mode 100644 index 000000000..0366703d2 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evsci.c @@ -0,0 +1,250 @@ +/******************************************************************************* + * + * Module Name: evsci - System Control Interrupt configuration and + * legacy to ACPI mode state transition functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evsci") +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/* Local prototypes */ +static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context); + +/******************************************************************************* + * + * FUNCTION: acpi_ev_sci_dispatch + * + * PARAMETERS: None + * + * RETURN: Status code indicates whether interrupt was handled. + * + * DESCRIPTION: Dispatch the SCI to all host-installed SCI handlers. + * + ******************************************************************************/ + +u32 acpi_ev_sci_dispatch(void) +{ + struct acpi_sci_handler_info *sci_handler; + acpi_cpu_flags flags; + u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; + + ACPI_FUNCTION_NAME(ev_sci_dispatch); + + /* Are there any host-installed SCI handlers? */ + + if (!acpi_gbl_sci_handler_list) { + return (int_status); + } + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Invoke all host-installed SCI handlers */ + + sci_handler = acpi_gbl_sci_handler_list; + while (sci_handler) { + + /* Invoke the installed handler (at interrupt level) */ + + int_status |= sci_handler->address(sci_handler->context); + + sci_handler = sci_handler->next; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return (int_status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_sci_xrupt_handler + * + * PARAMETERS: context - Calling Context + * + * RETURN: Status code indicates whether interrupt was handled. + * + * DESCRIPTION: Interrupt handler that will figure out what function or + * control method to call to deal with a SCI. + * + ******************************************************************************/ + +static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_list = context; + u32 interrupt_handled = ACPI_INTERRUPT_NOT_HANDLED; + + ACPI_FUNCTION_TRACE(ev_sci_xrupt_handler); + + /* + * We are guaranteed by the ACPICA initialization/shutdown code that + * if this interrupt handler is installed, ACPI is enabled. + */ + + /* + * Fixed Events: + * Check for and dispatch any Fixed Events that have occurred + */ + interrupt_handled |= acpi_ev_fixed_event_detect(); + + /* + * General Purpose Events: + * Check for and dispatch any GPEs that have occurred + */ + interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list); + + /* Invoke all host-installed SCI handlers */ + + interrupt_handled |= acpi_ev_sci_dispatch(); + + acpi_sci_count++; + return_UINT32(interrupt_handled); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_xrupt_handler + * + * PARAMETERS: context - Calling Context + * + * RETURN: Status code indicates whether interrupt was handled. + * + * DESCRIPTION: Handler for GPE Block Device interrupts + * + ******************************************************************************/ + +u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_list = context; + u32 interrupt_handled = ACPI_INTERRUPT_NOT_HANDLED; + + ACPI_FUNCTION_TRACE(ev_gpe_xrupt_handler); + + /* + * We are guaranteed by the ACPICA initialization/shutdown code that + * if this interrupt handler is installed, ACPI is enabled. + */ + + /* GPEs: Check for and dispatch any GPEs that have occurred */ + + interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list); + return_UINT32(interrupt_handled); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ev_install_sci_handler + * + * PARAMETERS: none + * + * RETURN: Status + * + * DESCRIPTION: Installs SCI handler. + * + ******************************************************************************/ + +u32 acpi_ev_install_sci_handler(void) +{ + u32 status = AE_OK; + + ACPI_FUNCTION_TRACE(ev_install_sci_handler); + + status = + acpi_os_install_interrupt_handler((u32) acpi_gbl_FADT.sci_interrupt, + acpi_ev_sci_xrupt_handler, + acpi_gbl_gpe_xrupt_list_head); + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ev_remove_all_sci_handlers + * + * PARAMETERS: none + * + * RETURN: AE_OK if handler uninstalled, AE_ERROR if handler was not + * installed to begin with + * + * DESCRIPTION: Remove the SCI interrupt handler. No further SCIs will be + * taken. Remove all host-installed SCI handlers. + * + * Note: It doesn't seem important to disable all events or set the event + * enable registers to their original values. The OS should disable + * the SCI interrupt level when the handler is removed, so no more + * events will come in. + * + ******************************************************************************/ + +acpi_status acpi_ev_remove_all_sci_handlers(void) +{ + struct acpi_sci_handler_info *sci_handler; + acpi_cpu_flags flags; + acpi_status status; + + ACPI_FUNCTION_TRACE(ev_remove_all_sci_handlers); + + /* Just let the OS remove the handler and disable the level */ + + status = + acpi_os_remove_interrupt_handler((u32) acpi_gbl_FADT.sci_interrupt, + acpi_ev_sci_xrupt_handler); + + if (!acpi_gbl_sci_handler_list) { + return (status); + } + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Free all host-installed SCI handlers */ + + while (acpi_gbl_sci_handler_list) { + sci_handler = acpi_gbl_sci_handler_list; + acpi_gbl_sci_handler_list = sci_handler->next; + ACPI_FREE(sci_handler); + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evxface.c b/kernel/drivers/acpi/acpica/evxface.c new file mode 100644 index 000000000..81f2d9e87 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evxface.c @@ -0,0 +1,1105 @@ +/****************************************************************************** + * + * Module Name: evxface - External interfaces for ACPI events + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acevents.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evxface") +#if (!ACPI_REDUCED_HARDWARE) +/* Local prototypes */ +static acpi_status +acpi_ev_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, + u8 is_raw_handler, + acpi_gpe_handler address, void *context); + +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_install_notify_handler + * + * PARAMETERS: device - The device for which notifies will be handled + * handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: System Handler (00-7F) + * ACPI_DEVICE_NOTIFY: Device Handler (80-FF) + * ACPI_ALL_NOTIFY: Both System and Device + * handler - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for notifications on an ACPI Device, + * thermal_zone, or Processor object. + * + * NOTES: The Root namespace object may have only one handler for each + * type of notify (System/Device). Device/Thermal/Processor objects + * may have one device notify handler, and multiple system notify + * handlers. + * + ******************************************************************************/ + +acpi_status +acpi_install_notify_handler(acpi_handle device, + u32 handler_type, + acpi_notify_handler handler, void *context) +{ + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, device); + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + acpi_status status; + u32 i; + + ACPI_FUNCTION_TRACE(acpi_install_notify_handler); + + /* Parameter validation */ + + if ((!device) || (!handler) || (!handler_type) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Root Object: + * Registering a notify handler on the root object indicates that the + * caller wishes to receive notifications for all objects. Note that + * only one global handler can be registered per notify type. + * Ensure that a handler is not already installed. + */ + if (device == ACPI_ROOT_OBJECT) { + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + if (acpi_gbl_global_notify[i].handler) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + acpi_gbl_global_notify[i].handler = handler; + acpi_gbl_global_notify[i].context = context; + } + } + + goto unlock_and_exit; /* Global notify handler installed, all done */ + } + + /* + * All Other Objects: + * Caller will only receive notifications specific to the target + * object. Note that only certain object types are allowed to + * receive notifications. + */ + + /* Are Notifies allowed on this object? */ + + if (!acpi_ev_is_notify_object(node)) { + status = AE_TYPE; + goto unlock_and_exit; + } + + /* Check for an existing internal object, might not exist */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + + /* Create a new object */ + + obj_desc = acpi_ut_create_internal_object(node->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Attach new object to the Node, remove local reference */ + + status = acpi_ns_attach_object(device, obj_desc, node->type); + acpi_ut_remove_reference(obj_desc); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } + + /* Ensure that the handler is not already installed in the lists */ + + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj = obj_desc->common_notify.notify_list[i]; + while (handler_obj) { + if (handler_obj->notify.handler == handler) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + handler_obj = handler_obj->notify.next[i]; + } + } + } + + /* Create and populate a new notify handler object */ + + handler_obj = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_NOTIFY); + if (!handler_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + handler_obj->notify.node = node; + handler_obj->notify.handler_type = handler_type; + handler_obj->notify.handler = handler; + handler_obj->notify.context = context; + + /* Install the handler at the list head(s) */ + + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + handler_obj->notify.next[i] = + obj_desc->common_notify.notify_list[i]; + + obj_desc->common_notify.notify_list[i] = handler_obj; + } + } + + /* Add an extra reference if handler was installed in both lists */ + + if (handler_type == ACPI_ALL_NOTIFY) { + acpi_ut_add_reference(handler_obj); + } + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_notify_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_notify_handler + * + * PARAMETERS: device - The device for which the handler is installed + * handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: System Handler (00-7F) + * ACPI_DEVICE_NOTIFY: Device Handler (80-FF) + * ACPI_ALL_NOTIFY: Both System and Device + * handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a handler for notifies on an ACPI device + * + ******************************************************************************/ +acpi_status +acpi_remove_notify_handler(acpi_handle device, + u32 handler_type, acpi_notify_handler handler) +{ + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, device); + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + union acpi_operand_object *previous_handler_obj; + acpi_status status = AE_OK; + u32 i; + + ACPI_FUNCTION_TRACE(acpi_remove_notify_handler); + + /* Parameter validation */ + + if ((!device) || (!handler) || (!handler_type) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Root Object. Global handlers are removed here */ + + if (device == ACPI_ROOT_OBJECT) { + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + status = + acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (!acpi_gbl_global_notify[i].handler || + (acpi_gbl_global_notify[i].handler != + handler)) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Removing global notify handler\n")); + + acpi_gbl_global_notify[i].handler = NULL; + acpi_gbl_global_notify[i].context = NULL; + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + /* Make sure all deferred notify tasks are completed */ + + acpi_os_wait_events_complete(); + } + } + + return_ACPI_STATUS(AE_OK); + } + + /* All other objects: Are Notifies allowed on this object? */ + + if (!acpi_ev_is_notify_object(node)) { + return_ACPI_STATUS(AE_TYPE); + } + + /* Must have an existing internal object */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Internal object exists. Find the handler and remove it */ + + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + if (handler_type & (i + 1)) { + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + handler_obj = obj_desc->common_notify.notify_list[i]; + previous_handler_obj = NULL; + + /* Attempt to find the handler in the handler list */ + + while (handler_obj && + (handler_obj->notify.handler != handler)) { + previous_handler_obj = handler_obj; + handler_obj = handler_obj->notify.next[i]; + } + + if (!handler_obj) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* Remove the handler object from the list */ + + if (previous_handler_obj) { /* Handler is not at the list head */ + previous_handler_obj->notify.next[i] = + handler_obj->notify.next[i]; + } else { /* Handler is at the list head */ + + obj_desc->common_notify.notify_list[i] = + handler_obj->notify.next[i]; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + /* Make sure all deferred notify tasks are completed */ + + acpi_os_wait_events_complete(); + acpi_ut_remove_reference(handler_obj); + } + } + + return_ACPI_STATUS(status); + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_notify_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_install_exception_handler + * + * PARAMETERS: handler - Pointer to the handler function for the + * event + * + * RETURN: Status + * + * DESCRIPTION: Saves the pointer to the handler function + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status acpi_install_exception_handler(acpi_exception_handler handler) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_exception_handler); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Don't allow two handlers. */ + + if (acpi_gbl_exception_handler) { + status = AE_ALREADY_EXISTS; + goto cleanup; + } + + /* Install the handler */ + + acpi_gbl_exception_handler = handler; + +cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_exception_handler) +#endif /* ACPI_FUTURE_USAGE */ + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_install_sci_handler + * + * PARAMETERS: address - Address of the handler + * context - Value passed to the handler on each SCI + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for a System Control Interrupt. + * + ******************************************************************************/ +acpi_status acpi_install_sci_handler(acpi_sci_handler address, void *context) +{ + struct acpi_sci_handler_info *new_sci_handler; + struct acpi_sci_handler_info *sci_handler; + acpi_cpu_flags flags; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_sci_handler); + + if (!address) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Allocate and init a handler object */ + + new_sci_handler = ACPI_ALLOCATE(sizeof(struct acpi_sci_handler_info)); + if (!new_sci_handler) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + new_sci_handler->address = address; + new_sci_handler->context = context; + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Lock list during installation */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + sci_handler = acpi_gbl_sci_handler_list; + + /* Ensure handler does not already exist */ + + while (sci_handler) { + if (address == sci_handler->address) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + sci_handler = sci_handler->next; + } + + /* Install the new handler into the global list (at head) */ + + new_sci_handler->next = acpi_gbl_sci_handler_list; + acpi_gbl_sci_handler_list = new_sci_handler; + +unlock_and_exit: + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + +exit: + if (ACPI_FAILURE(status)) { + ACPI_FREE(new_sci_handler); + } + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_sci_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_sci_handler + * + * PARAMETERS: address - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a handler for a System Control Interrupt. + * + ******************************************************************************/ +acpi_status acpi_remove_sci_handler(acpi_sci_handler address) +{ + struct acpi_sci_handler_info *prev_sci_handler; + struct acpi_sci_handler_info *next_sci_handler; + acpi_cpu_flags flags; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_remove_sci_handler); + + if (!address) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Remove the SCI handler with lock */ + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + prev_sci_handler = NULL; + next_sci_handler = acpi_gbl_sci_handler_list; + while (next_sci_handler) { + if (next_sci_handler->address == address) { + + /* Unlink and free the SCI handler info block */ + + if (prev_sci_handler) { + prev_sci_handler->next = next_sci_handler->next; + } else { + acpi_gbl_sci_handler_list = + next_sci_handler->next; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + ACPI_FREE(next_sci_handler); + goto unlock_and_exit; + } + + prev_sci_handler = next_sci_handler; + next_sci_handler = next_sci_handler->next; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + status = AE_NOT_EXIST; + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_sci_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_install_global_event_handler + * + * PARAMETERS: handler - Pointer to the global event handler function + * context - Value passed to the handler on each event + * + * RETURN: Status + * + * DESCRIPTION: Saves the pointer to the handler function. The global handler + * is invoked upon each incoming GPE and Fixed Event. It is + * invoked at interrupt level at the time of the event dispatch. + * Can be used to update event counters, etc. + * + ******************************************************************************/ +acpi_status +acpi_install_global_event_handler(acpi_gbl_event_handler handler, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_global_event_handler); + + /* Parameter validation */ + + if (!handler) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Don't allow two handlers. */ + + if (acpi_gbl_global_event_handler) { + status = AE_ALREADY_EXISTS; + goto cleanup; + } + + acpi_gbl_global_event_handler = handler; + acpi_gbl_global_event_handler_context = context; + +cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_global_event_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_install_fixed_event_handler + * + * PARAMETERS: event - Event type to enable. + * handler - Pointer to the handler function for the + * event + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Saves the pointer to the handler function and then enables the + * event. + * + ******************************************************************************/ +acpi_status +acpi_install_fixed_event_handler(u32 event, + acpi_event_handler handler, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_fixed_event_handler); + + /* Parameter validation */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Do not allow multiple handlers */ + + if (acpi_gbl_fixed_event_handlers[event].handler) { + status = AE_ALREADY_EXISTS; + goto cleanup; + } + + /* Install the handler before enabling the event */ + + acpi_gbl_fixed_event_handlers[event].handler = handler; + acpi_gbl_fixed_event_handlers[event].context = context; + + status = acpi_clear_event(event); + if (ACPI_SUCCESS(status)) + status = acpi_enable_event(event, 0); + if (ACPI_FAILURE(status)) { + ACPI_WARNING((AE_INFO, + "Could not enable fixed event - %s (%u)", + acpi_ut_get_event_name(event), event)); + + /* Remove the handler */ + + acpi_gbl_fixed_event_handlers[event].handler = NULL; + acpi_gbl_fixed_event_handlers[event].context = NULL; + } else { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Enabled fixed event %s (%X), Handler=%p\n", + acpi_ut_get_event_name(event), event, + handler)); + } + +cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_fixed_event_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_fixed_event_handler + * + * PARAMETERS: event - Event type to disable. + * handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Disables the event and unregisters the event handler. + * + ******************************************************************************/ +acpi_status +acpi_remove_fixed_event_handler(u32 event, acpi_event_handler handler) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(acpi_remove_fixed_event_handler); + + /* Parameter validation */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Disable the event before removing the handler */ + + status = acpi_disable_event(event, 0); + + /* Always Remove the handler */ + + acpi_gbl_fixed_event_handlers[event].handler = NULL; + acpi_gbl_fixed_event_handlers[event].context = NULL; + + if (ACPI_FAILURE(status)) { + ACPI_WARNING((AE_INFO, + "Could not disable fixed event - %s (%u)", + acpi_ut_get_event_name(event), event)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Disabled fixed event - %s (%X)\n", + acpi_ut_get_event_name(event), event)); + } + + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_gpe_handler + * + * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT + * defined GPEs) + * gpe_number - The GPE number within the GPE block + * type - Whether this GPE should be treated as an + * edge- or level-triggered interrupt. + * is_raw_handler - Whether this GPE should be handled using + * the special GPE handler mode. + * address - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Internal function to install a handler for a General Purpose + * Event. + * + ******************************************************************************/ +static acpi_status +acpi_ev_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, + u8 is_raw_handler, + acpi_gpe_handler address, void *context) +{ + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_gpe_handler_info *handler; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(ev_install_gpe_handler); + + /* Parameter validation */ + + if ((!address) || (type & ~ACPI_GPE_XRUPT_TYPE_MASK)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Allocate and init handler object (before lock) */ + + handler = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_handler_info)); + if (!handler) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto free_and_exit; + } + + /* Make sure that there isn't a handler there already */ + + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) || + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER)) { + status = AE_ALREADY_EXISTS; + goto free_and_exit; + } + + handler->address = address; + handler->context = context; + handler->method_node = gpe_event_info->dispatch.method_node; + handler->original_flags = (u8)(gpe_event_info->flags & + (ACPI_GPE_XRUPT_TYPE_MASK | + ACPI_GPE_DISPATCH_MASK)); + + /* + * If the GPE is associated with a method, it may have been enabled + * automatically during initialization, in which case it has to be + * disabled now to avoid spurious execution of the handler. + */ + if (((ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_METHOD) || + (ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_NOTIFY)) && gpe_event_info->runtime_count) { + handler->originally_enabled = TRUE; + (void)acpi_ev_remove_gpe_reference(gpe_event_info); + + /* Sanity check of original type against new type */ + + if (type != + (u32)(gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK)) { + ACPI_WARNING((AE_INFO, + "GPE type mismatch (level/edge)")); + } + } + + /* Install the handler */ + + gpe_event_info->dispatch.handler = handler; + + /* Setup up dispatch flags to indicate handler (vs. method/notify) */ + + gpe_event_info->flags &= + ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); + gpe_event_info->flags |= + (u8)(type | + (is_raw_handler ? ACPI_GPE_DISPATCH_RAW_HANDLER : + ACPI_GPE_DISPATCH_HANDLER)); + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); + +free_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + ACPI_FREE(handler); + goto unlock_and_exit; +} + +/******************************************************************************* + * + * FUNCTION: acpi_install_gpe_handler + * + * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT + * defined GPEs) + * gpe_number - The GPE number within the GPE block + * type - Whether this GPE should be treated as an + * edge- or level-triggered interrupt. + * address - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for a General Purpose Event. + * + ******************************************************************************/ + +acpi_status +acpi_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, acpi_gpe_handler address, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_gpe_handler); + + status = + acpi_ev_install_gpe_handler(gpe_device, gpe_number, type, FALSE, + address, context); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_gpe_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_install_gpe_raw_handler + * + * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT + * defined GPEs) + * gpe_number - The GPE number within the GPE block + * type - Whether this GPE should be treated as an + * edge- or level-triggered interrupt. + * address - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for a General Purpose Event. + * + ******************************************************************************/ +acpi_status +acpi_install_gpe_raw_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, acpi_gpe_handler address, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_gpe_raw_handler); + + status = acpi_ev_install_gpe_handler(gpe_device, gpe_number, type, TRUE, + address, context); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_gpe_raw_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_gpe_handler + * + * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT + * defined GPEs) + * gpe_number - The event to remove a handler + * address - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a handler for a General Purpose acpi_event. + * + ******************************************************************************/ +acpi_status +acpi_remove_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, acpi_gpe_handler address) +{ + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_gpe_handler_info *handler; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_remove_gpe_handler); + + /* Parameter validation */ + + if (!address) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Make sure that a handler is indeed installed */ + + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != + ACPI_GPE_DISPATCH_HANDLER) && + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != + ACPI_GPE_DISPATCH_RAW_HANDLER)) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* Make sure that the installed handler is the same */ + + if (gpe_event_info->dispatch.handler->address != address) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Remove the handler */ + + handler = gpe_event_info->dispatch.handler; + gpe_event_info->dispatch.handler = NULL; + + /* Restore Method node (if any), set dispatch flags */ + + gpe_event_info->dispatch.method_node = handler->method_node; + gpe_event_info->flags &= + ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); + gpe_event_info->flags |= handler->original_flags; + + /* + * If the GPE was previously associated with a method and it was + * enabled, it should be enabled at this point to restore the + * post-initialization configuration. + */ + if (((ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_METHOD) || + (ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_NOTIFY)) && handler->originally_enabled) { + (void)acpi_ev_add_gpe_reference(gpe_event_info); + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + /* Make sure all deferred GPE tasks are completed */ + + acpi_os_wait_events_complete(); + + /* Now we can free the handler object */ + + ACPI_FREE(handler); + return_ACPI_STATUS(status); + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_gpe_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_acquire_global_lock + * + * PARAMETERS: timeout - How long the caller is willing to wait + * handle - Where the handle to the lock is returned + * (if acquired) + * + * RETURN: Status + * + * DESCRIPTION: Acquire the ACPI Global Lock + * + * Note: Allows callers with the same thread ID to acquire the global lock + * multiple times. In other words, externally, the behavior of the global lock + * is identical to an AML mutex. On the first acquire, a new handle is + * returned. On any subsequent calls to acquire by the same thread, the same + * handle is returned. + * + ******************************************************************************/ +acpi_status acpi_acquire_global_lock(u16 timeout, u32 *handle) +{ + acpi_status status; + + if (!handle) { + return (AE_BAD_PARAMETER); + } + + /* Must lock interpreter to prevent race conditions */ + + acpi_ex_enter_interpreter(); + + status = acpi_ex_acquire_mutex_object(timeout, + acpi_gbl_global_lock_mutex, + acpi_os_get_thread_id()); + + if (ACPI_SUCCESS(status)) { + + /* Return the global lock handle (updated in acpi_ev_acquire_global_lock) */ + + *handle = acpi_gbl_global_lock_handle; + } + + acpi_ex_exit_interpreter(); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_acquire_global_lock) + +/******************************************************************************* + * + * FUNCTION: acpi_release_global_lock + * + * PARAMETERS: handle - Returned from acpi_acquire_global_lock + * + * RETURN: Status + * + * DESCRIPTION: Release the ACPI Global Lock. The handle must be valid. + * + ******************************************************************************/ +acpi_status acpi_release_global_lock(u32 handle) +{ + acpi_status status; + + if (!handle || (handle != acpi_gbl_global_lock_handle)) { + return (AE_NOT_ACQUIRED); + } + + status = acpi_ex_release_mutex_object(acpi_gbl_global_lock_mutex); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_release_global_lock) +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evxfevnt.c b/kernel/drivers/acpi/acpica/evxfevnt.c new file mode 100644 index 000000000..faad911d4 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evxfevnt.c @@ -0,0 +1,381 @@ +/****************************************************************************** + * + * Module Name: evxfevnt - External Interfaces, ACPI event disable/enable + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evxfevnt") + +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/******************************************************************************* + * + * FUNCTION: acpi_enable + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Transfers the system into ACPI mode. + * + ******************************************************************************/ +acpi_status acpi_enable(void) +{ + acpi_status status; + int retry; + + ACPI_FUNCTION_TRACE(acpi_enable); + + /* ACPI tables must be present */ + + if (!acpi_tb_tables_loaded()) { + return_ACPI_STATUS(AE_NO_ACPI_TABLES); + } + + /* If the Hardware Reduced flag is set, machine is always in acpi mode */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + + /* Check current mode */ + + if (acpi_hw_get_mode() == ACPI_SYS_MODE_ACPI) { + ACPI_DEBUG_PRINT((ACPI_DB_INIT, + "System is already in ACPI mode\n")); + return_ACPI_STATUS(AE_OK); + } + + /* Transition to ACPI mode */ + + status = acpi_hw_set_mode(ACPI_SYS_MODE_ACPI); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not transition to ACPI mode")); + return_ACPI_STATUS(status); + } + + /* Sanity check that transition succeeded */ + + for (retry = 0; retry < 30000; ++retry) { + if (acpi_hw_get_mode() == ACPI_SYS_MODE_ACPI) { + if (retry != 0) + ACPI_WARNING((AE_INFO, + "Platform took > %d00 usec to enter ACPI mode", retry)); + return_ACPI_STATUS(AE_OK); + } + acpi_os_stall(100); /* 100 usec */ + } + + ACPI_ERROR((AE_INFO, "Hardware did not enter ACPI mode")); + return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE); +} + +ACPI_EXPORT_SYMBOL(acpi_enable) + +/******************************************************************************* + * + * FUNCTION: acpi_disable + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Transfers the system into LEGACY (non-ACPI) mode. + * + ******************************************************************************/ +acpi_status acpi_disable(void) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(acpi_disable); + + /* If the Hardware Reduced flag is set, machine is always in acpi mode */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + + if (acpi_hw_get_mode() == ACPI_SYS_MODE_LEGACY) { + ACPI_DEBUG_PRINT((ACPI_DB_INIT, + "System is already in legacy (non-ACPI) mode\n")); + } else { + /* Transition to LEGACY mode */ + + status = acpi_hw_set_mode(ACPI_SYS_MODE_LEGACY); + + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not exit ACPI mode to legacy mode")); + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INIT, "ACPI mode disabled\n")); + } + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_disable) + +/******************************************************************************* + * + * FUNCTION: acpi_enable_event + * + * PARAMETERS: event - The fixed eventto be enabled + * flags - Reserved + * + * RETURN: Status + * + * DESCRIPTION: Enable an ACPI event (fixed) + * + ******************************************************************************/ +acpi_status acpi_enable_event(u32 event, u32 flags) +{ + acpi_status status = AE_OK; + u32 value; + + ACPI_FUNCTION_TRACE(acpi_enable_event); + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * Enable the requested fixed event (by writing a one to the enable + * register bit) + */ + status = + acpi_write_bit_register(acpi_gbl_fixed_event_info[event]. + enable_register_id, ACPI_ENABLE_EVENT); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Make sure that the hardware responded */ + + status = + acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. + enable_register_id, &value); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (value != 1) { + ACPI_ERROR((AE_INFO, + "Could not enable %s event", + acpi_ut_get_event_name(event))); + return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE); + } + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_enable_event) + +/******************************************************************************* + * + * FUNCTION: acpi_disable_event + * + * PARAMETERS: event - The fixed event to be disabled + * flags - Reserved + * + * RETURN: Status + * + * DESCRIPTION: Disable an ACPI event (fixed) + * + ******************************************************************************/ +acpi_status acpi_disable_event(u32 event, u32 flags) +{ + acpi_status status = AE_OK; + u32 value; + + ACPI_FUNCTION_TRACE(acpi_disable_event); + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * Disable the requested fixed event (by writing a zero to the enable + * register bit) + */ + status = + acpi_write_bit_register(acpi_gbl_fixed_event_info[event]. + enable_register_id, ACPI_DISABLE_EVENT); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = + acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. + enable_register_id, &value); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (value != 0) { + ACPI_ERROR((AE_INFO, + "Could not disable %s events", + acpi_ut_get_event_name(event))); + return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE); + } + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_disable_event) + +/******************************************************************************* + * + * FUNCTION: acpi_clear_event + * + * PARAMETERS: event - The fixed event to be cleared + * + * RETURN: Status + * + * DESCRIPTION: Clear an ACPI event (fixed) + * + ******************************************************************************/ +acpi_status acpi_clear_event(u32 event) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(acpi_clear_event); + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * Clear the requested fixed event (By writing a one to the status + * register bit) + */ + status = + acpi_write_bit_register(acpi_gbl_fixed_event_info[event]. + status_register_id, ACPI_CLEAR_STATUS); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_clear_event) + +/******************************************************************************* + * + * FUNCTION: acpi_get_event_status + * + * PARAMETERS: event - The fixed event + * event_status - Where the current status of the event will + * be returned + * + * RETURN: Status + * + * DESCRIPTION: Obtains and returns the current status of the event + * + ******************************************************************************/ +acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) +{ + acpi_status status; + acpi_event_status local_event_status = 0; + u32 in_byte; + + ACPI_FUNCTION_TRACE(acpi_get_event_status); + + if (!event_status) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Fixed event currently can be dispatched? */ + + if (acpi_gbl_fixed_event_handlers[event].handler) { + local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; + } + + /* Fixed event currently enabled? */ + + status = + acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. + enable_register_id, &in_byte); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (in_byte) { + local_event_status |= + (ACPI_EVENT_FLAG_ENABLED | ACPI_EVENT_FLAG_ENABLE_SET); + } + + /* Fixed event currently active? */ + + status = + acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. + status_register_id, &in_byte); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (in_byte) { + local_event_status |= ACPI_EVENT_FLAG_STATUS_SET; + } + + (*event_status) = local_event_status; + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_get_event_status) +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evxfgpe.c b/kernel/drivers/acpi/acpica/evxfgpe.c new file mode 100644 index 000000000..70eb47e3d --- /dev/null +++ b/kernel/drivers/acpi/acpica/evxfgpe.c @@ -0,0 +1,952 @@ +/****************************************************************************** + * + * Module Name: evxfgpe - External Interfaces for General Purpose Events (GPEs) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evxfgpe") + +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/******************************************************************************* + * + * FUNCTION: acpi_update_all_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Complete GPE initialization and enable all GPEs that have + * associated _Lxx or _Exx methods and are not pointed to by any + * device _PRW methods (this indicates that these GPEs are + * generally intended for system or device wakeup. Such GPEs + * have to be enabled directly when the devices whose _PRW + * methods point to them are set up for wakeup signaling.) + * + * NOTE: Should be called after any GPEs are added to the system. Primarily, + * after the system _PRW methods have been run, but also after a GPE Block + * Device has been added or if any new GPE methods have been added via a + * dynamic table load. + * + ******************************************************************************/ + +acpi_status acpi_update_all_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_update_all_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (acpi_gbl_all_gpes_initialized) { + goto unlock_and_exit; + } + + status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block, NULL); + if (ACPI_SUCCESS(status)) { + acpi_gbl_all_gpes_initialized = TRUE; + } + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_update_all_gpes) + +/******************************************************************************* + * + * FUNCTION: acpi_enable_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is + * hardware-enabled. + * + ******************************************************************************/ +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status = AE_BAD_PARAMETER; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_enable_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* + * Ensure that we have a valid GPE number and that there is some way + * of handling the GPE (handler or a GPE method). In other words, we + * won't allow a valid GPE to be enabled if there is no way to handle it. + */ + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (gpe_event_info) { + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != + ACPI_GPE_DISPATCH_NONE) { + status = acpi_ev_add_gpe_reference(gpe_event_info); + } else { + status = AE_NO_HANDLER; + } + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} +ACPI_EXPORT_SYMBOL(acpi_enable_gpe) + +/******************************************************************************* + * + * FUNCTION: acpi_disable_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Remove a reference to a GPE. When the last reference is + * removed, only then is the GPE disabled (for runtime GPEs), or + * the GPE mask bit disabled (for wake GPEs) + * + ******************************************************************************/ + +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status = AE_BAD_PARAMETER; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_disable_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (gpe_event_info) { + status = acpi_ev_remove_gpe_reference(gpe_event_info) ; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_disable_gpe) + +/******************************************************************************* + * + * FUNCTION: acpi_set_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * action - ACPI_GPE_ENABLE or ACPI_GPE_DISABLE + * + * RETURN: Status + * + * DESCRIPTION: Enable or disable an individual GPE. This function bypasses + * the reference count mechanism used in the acpi_enable_gpe(), + * acpi_disable_gpe() interfaces. + * This API is typically used by the GPE raw handler mode driver + * to switch between the polling mode and the interrupt mode after + * the driver has enabled the GPE. + * The APIs should be invoked in this order: + * acpi_enable_gpe() <- Ensure the reference count > 0 + * acpi_set_gpe(ACPI_GPE_DISABLE) <- Enter polling mode + * acpi_set_gpe(ACPI_GPE_ENABLE) <- Leave polling mode + * acpi_disable_gpe() <- Decrease the reference count + * + * Note: If a GPE is shared by 2 silicon components, then both the drivers + * should support GPE polling mode or disabling the GPE for long period + * for one driver may break the other. So use it with care since all + * firmware _Lxx/_Exx handlers currently rely on the GPE interrupt mode. + * + ******************************************************************************/ +acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_set_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Perform the action */ + + switch (action) { + case ACPI_GPE_ENABLE: + + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); + break; + + case ACPI_GPE_DISABLE: + + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); + break; + + default: + + status = AE_BAD_PARAMETER; + break; + } + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_set_gpe) + +/******************************************************************************* + * + * FUNCTION: acpi_mark_gpe_for_wake + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Mark a GPE as having the ability to wake the system. Simply + * sets the ACPI_GPE_CAN_WAKE flag. + * + * Some potential callers of acpi_setup_gpe_for_wake may know in advance that + * there won't be any notify handlers installed for device wake notifications + * from the given GPE (one example is a button GPE in Linux). For these cases, + * acpi_mark_gpe_for_wake should be used instead of acpi_setup_gpe_for_wake. + * This will set the ACPI_GPE_CAN_WAKE flag for the GPE without trying to + * setup implicit wake notification for it (since there's no handler method). + * + ******************************************************************************/ +acpi_status acpi_mark_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_status status = AE_BAD_PARAMETER; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_mark_gpe_for_wake); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (gpe_event_info) { + + /* Mark the GPE as a possible wake event */ + + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; + status = AE_OK; + } + + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_mark_gpe_for_wake) + +/******************************************************************************* + * + * FUNCTION: acpi_setup_gpe_for_wake + * + * PARAMETERS: wake_device - Device associated with the GPE (via _PRW) + * gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Mark a GPE as having the ability to wake the system. This + * interface is intended to be used as the host executes the + * _PRW methods (Power Resources for Wake) in the system tables. + * Each _PRW appears under a Device Object (The wake_device), and + * contains the info for the wake GPE associated with the + * wake_device. + * + ******************************************************************************/ +acpi_status +acpi_setup_gpe_for_wake(acpi_handle wake_device, + acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status; + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_namespace_node *device_node; + struct acpi_gpe_notify_info *notify; + struct acpi_gpe_notify_info *new_notify; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); + + /* Parameter Validation */ + + if (!wake_device) { + /* + * By forcing wake_device to be valid, we automatically enable the + * implicit notify feature on all hosts. + */ + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Handle root object case */ + + if (wake_device == ACPI_ROOT_OBJECT) { + device_node = acpi_gbl_root_node; + } else { + device_node = + ACPI_CAST_PTR(struct acpi_namespace_node, wake_device); + } + + /* Validate wake_device is of type Device */ + + if (device_node->type != ACPI_TYPE_DEVICE) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Allocate a new notify object up front, in case it is needed. + * Memory allocation while holding a spinlock is a big no-no + * on some hosts. + */ + new_notify = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_notify_info)); + if (!new_notify) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * If there is no method or handler for this GPE, then the + * wake_device will be notified whenever this GPE fires. This is + * known as an "implicit notify". Note: The GPE is assumed to be + * level-triggered (for windows compatibility). + */ + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_NONE) { + /* + * This is the first device for implicit notify on this GPE. + * Just set the flags here, and enter the NOTIFY block below. + */ + gpe_event_info->flags = + (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED); + } + + /* + * If we already have an implicit notify on this GPE, add + * this device to the notify list. + */ + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_NOTIFY) { + + /* Ensure that the device is not already in the list */ + + notify = gpe_event_info->dispatch.notify_list; + while (notify) { + if (notify->device_node == device_node) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + notify = notify->next; + } + + /* Add this device to the notify list for this GPE */ + + new_notify->device_node = device_node; + new_notify->next = gpe_event_info->dispatch.notify_list; + gpe_event_info->dispatch.notify_list = new_notify; + new_notify = NULL; + } + + /* Mark the GPE as a possible wake event */ + + gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; + status = AE_OK; + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + /* Delete the notify object if it was not used above */ + + if (new_notify) { + ACPI_FREE(new_notify); + } + return_ACPI_STATUS(status); +} +ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake) + +/******************************************************************************* + * + * FUNCTION: acpi_set_gpe_wake_mask + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * action - Enable or Disable + * + * RETURN: Status + * + * DESCRIPTION: Set or clear the GPE's wakeup enable mask bit. The GPE must + * already be marked as a WAKE GPE. + * + ******************************************************************************/ + +acpi_status +acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_gpe_register_info *gpe_register_info; + acpi_cpu_flags flags; + u32 register_bit; + + ACPI_FUNCTION_TRACE(acpi_set_gpe_wake_mask); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* + * Ensure that we have a valid GPE number and that this GPE is in + * fact a wake GPE + */ + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { + status = AE_TYPE; + goto unlock_and_exit; + } + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); + + /* Perform the action */ + + switch (action) { + case ACPI_GPE_ENABLE: + + ACPI_SET_BIT(gpe_register_info->enable_for_wake, + (u8)register_bit); + break; + + case ACPI_GPE_DISABLE: + + ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, + (u8)register_bit); + break; + + default: + + ACPI_ERROR((AE_INFO, "%u, Invalid action", action)); + status = AE_BAD_PARAMETER; + break; + } + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_set_gpe_wake_mask) + +/******************************************************************************* + * + * FUNCTION: acpi_clear_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Clear an ACPI event (general purpose) + * + ******************************************************************************/ +acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_clear_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_hw_clear_gpe(gpe_event_info); + + unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_clear_gpe) + +/******************************************************************************* + * + * FUNCTION: acpi_get_gpe_status + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * event_status - Where the current status of the event + * will be returned + * + * RETURN: Status + * + * DESCRIPTION: Get the current status of a GPE (signalled/not_signalled) + * + ******************************************************************************/ +acpi_status +acpi_get_gpe_status(acpi_handle gpe_device, + u32 gpe_number, acpi_event_status *event_status) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_get_gpe_status); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Obtain status on the requested GPE number */ + + status = acpi_hw_get_gpe_status(gpe_event_info, event_status); + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_gpe_status) + +/******************************************************************************* + * + * FUNCTION: acpi_finish_gpe + * + * PARAMETERS: gpe_device - Namespace node for the GPE Block + * (NULL for FADT defined GPEs) + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Clear and conditionally reenable a GPE. This completes the GPE + * processing. Intended for use by asynchronous host-installed + * GPE handlers. The GPE is only reenabled if the enable_for_run bit + * is set in the GPE info. + * + ******************************************************************************/ +acpi_status acpi_finish_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_finish_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ev_finish_gpe(gpe_event_info); + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_finish_gpe) + +/****************************************************************************** + * + * FUNCTION: acpi_disable_all_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Disable and clear all GPEs in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_disable_all_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_disable_all_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_disable_all_gpes(); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_disable_all_gpes) + +/****************************************************************************** + * + * FUNCTION: acpi_enable_all_runtime_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_enable_all_runtime_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_enable_all_runtime_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_enable_all_runtime_gpes(); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes) + +/****************************************************************************** + * + * FUNCTION: acpi_enable_all_wakeup_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Enable all "wakeup" GPEs and disable all of the other GPEs, in + * all GPE blocks. + * + ******************************************************************************/ +acpi_status acpi_enable_all_wakeup_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_enable_all_wakeup_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_enable_all_wakeup_gpes(); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_enable_all_wakeup_gpes) + +/******************************************************************************* + * + * FUNCTION: acpi_install_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE Block Device + * gpe_block_address - Address and space_ID + * register_count - Number of GPE register pairs in the block + * interrupt_number - H/W interrupt for the block + * + * RETURN: Status + * + * DESCRIPTION: Create and Install a block of GPE registers. The GPEs are not + * enabled here. + * + ******************************************************************************/ +acpi_status +acpi_install_gpe_block(acpi_handle gpe_device, + struct acpi_generic_address *gpe_block_address, + u32 register_count, u32 interrupt_number) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + struct acpi_gpe_block_info *gpe_block; + + ACPI_FUNCTION_TRACE(acpi_install_gpe_block); + + if ((!gpe_device) || (!gpe_block_address) || (!register_count)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + node = acpi_ns_validate_handle(gpe_device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Validate the parent device */ + + if (node->type != ACPI_TYPE_DEVICE) { + status = AE_TYPE; + goto unlock_and_exit; + } + + if (node->object) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + /* + * For user-installed GPE Block Devices, the gpe_block_base_number + * is always zero + */ + status = acpi_ev_create_gpe_block(node, gpe_block_address->address, + gpe_block_address->space_id, + register_count, 0, interrupt_number, + &gpe_block); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* Install block in the device_object attached to the node */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + + /* + * No object, create a new one (Device nodes do not always have + * an attached object) + */ + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_DEVICE); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + status = + acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_DEVICE); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } + + /* Now install the GPE block in the device_object */ + + obj_desc->device.gpe_block = gpe_block; + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_gpe_block) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE Block Device + * + * RETURN: Status + * + * DESCRIPTION: Remove a previously installed block of GPE registers + * + ******************************************************************************/ +acpi_status acpi_remove_gpe_block(acpi_handle gpe_device) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_remove_gpe_block); + + if (!gpe_device) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + node = acpi_ns_validate_handle(gpe_device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Validate the parent device */ + + if (node->type != ACPI_TYPE_DEVICE) { + status = AE_TYPE; + goto unlock_and_exit; + } + + /* Get the device_object attached to the node */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc || !obj_desc->device.gpe_block) { + return_ACPI_STATUS(AE_NULL_OBJECT); + } + + /* Delete the GPE block (but not the device_object) */ + + status = acpi_ev_delete_gpe_block(obj_desc->device.gpe_block); + if (ACPI_SUCCESS(status)) { + obj_desc->device.gpe_block = NULL; + } + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_gpe_block) + +/******************************************************************************* + * + * FUNCTION: acpi_get_gpe_device + * + * PARAMETERS: index - System GPE index (0-current_gpe_count) + * gpe_device - Where the parent GPE Device is returned + * + * RETURN: Status + * + * DESCRIPTION: Obtain the GPE device associated with the input index. A NULL + * gpe device indicates that the gpe number is contained in one of + * the FADT-defined gpe blocks. Otherwise, the GPE block device. + * + ******************************************************************************/ +acpi_status acpi_get_gpe_device(u32 index, acpi_handle * gpe_device) +{ + struct acpi_gpe_device_info info; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_get_gpe_device); + + if (!gpe_device) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (index >= acpi_current_gpe_count) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Setup and walk the GPE list */ + + info.index = index; + info.status = AE_NOT_EXIST; + info.gpe_device = NULL; + info.next_block_base_index = 0; + + status = acpi_ev_walk_gpe_list(acpi_ev_get_gpe_device, &info); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + *gpe_device = ACPI_CAST_PTR(acpi_handle, info.gpe_device); + return_ACPI_STATUS(info.status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_gpe_device) +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/evxfregn.c b/kernel/drivers/acpi/acpica/evxfregn.c new file mode 100644 index 000000000..f21afbab0 --- /dev/null +++ b/kernel/drivers/acpi/acpica/evxfregn.c @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * Module Name: evxfregn - External Interfaces, ACPI Operation Regions and + * Address Spaces. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acevents.h" + +#define _COMPONENT ACPI_EVENTS +ACPI_MODULE_NAME("evxfregn") + +/******************************************************************************* + * + * FUNCTION: acpi_install_address_space_handler + * + * PARAMETERS: device - Handle for the device + * space_id - The address space ID + * handler - Address of the handler + * setup - Address of the setup function + * context - Value passed to the handler on each access + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for all op_regions of a given space_id. + * + * NOTE: This function should only be called after acpi_enable_subsystem has + * been called. This is because any _REG methods associated with the Space ID + * are executed here, and these methods can only be safely executed after + * the default handlers have been installed and the hardware has been + * initialized (via acpi_enable_subsystem.) + * + ******************************************************************************/ +acpi_status +acpi_install_address_space_handler(acpi_handle device, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler, + acpi_adr_space_setup setup, void *context) +{ + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_address_space_handler); + + /* Parameter validation */ + + if (!device) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_validate_handle(device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Install the handler for all Regions for this Space ID */ + + status = + acpi_ev_install_space_handler(node, space_id, handler, setup, + context); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* + * For the default space_IDs, (the IDs for which there are default region handlers + * installed) Only execute the _REG methods if the global initialization _REG + * methods have already been run (via acpi_initialize_objects). In other words, + * we will defer the execution of the _REG methods for these space_IDs until + * execution of acpi_initialize_objects. This is done because we need the handlers + * for the default spaces (mem/io/pci/table) to be installed before we can run + * any control methods (or _REG methods). There is known BIOS code that depends + * on this. + * + * For all other space_IDs, we can safely execute the _REG methods immediately. + * This means that for IDs like embedded_controller, this function should be called + * only after acpi_enable_subsystem has been called. + */ + switch (space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + case ACPI_ADR_SPACE_SYSTEM_IO: + case ACPI_ADR_SPACE_PCI_CONFIG: + case ACPI_ADR_SPACE_DATA_TABLE: + + if (!acpi_gbl_reg_methods_executed) { + + /* We will defer execution of the _REG methods for this space */ + goto unlock_and_exit; + } + break; + + default: + + break; + } + + /* Run all _REG methods for this address space */ + + status = acpi_ev_execute_reg_methods(node, space_id); + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_address_space_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_address_space_handler + * + * PARAMETERS: device - Handle for the device + * space_id - The address space ID + * handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a previously installed handler. + * + ******************************************************************************/ +acpi_status +acpi_remove_address_space_handler(acpi_handle device, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + union acpi_operand_object *region_obj; + union acpi_operand_object **last_obj_ptr; + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_remove_address_space_handler); + + /* Parameter validation */ + + if (!device) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_validate_handle(device); + if (!node || + ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_PROCESSOR) && + (node->type != ACPI_TYPE_THERMAL) && + (node != acpi_gbl_root_node))) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Make sure the internal object exists */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* Find the address handler the user requested */ + + handler_obj = obj_desc->device.handler; + last_obj_ptr = &obj_desc->device.handler; + while (handler_obj) { + + /* We have a handler, see if user requested this one */ + + if (handler_obj->address_space.space_id == space_id) { + + /* Handler must be the same as the installed handler */ + + if (handler_obj->address_space.handler != handler) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Matched space_id, first dereference this in the Regions */ + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Removing address handler %p(%p) for region %s " + "on Device %p(%p)\n", + handler_obj, handler, + acpi_ut_get_region_name(space_id), + node, obj_desc)); + + region_obj = handler_obj->address_space.region_list; + + /* Walk the handler's region list */ + + while (region_obj) { + /* + * First disassociate the handler from the region. + * + * NOTE: this doesn't mean that the region goes away + * The region is just inaccessible as indicated to + * the _REG method + */ + acpi_ev_detach_region(region_obj, TRUE); + + /* + * Walk the list: Just grab the head because the + * detach_region removed the previous head. + */ + region_obj = + handler_obj->address_space.region_list; + + } + + /* Remove this Handler object from the list */ + + *last_obj_ptr = handler_obj->address_space.next; + + /* Now we can delete the handler object */ + + acpi_ut_remove_reference(handler_obj); + goto unlock_and_exit; + } + + /* Walk the linked list of handlers */ + + last_obj_ptr = &handler_obj->address_space.next; + handler_obj = handler_obj->address_space.next; + } + + /* The handler does not exist */ + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Unable to remove address handler %p for %s(%X), DevNode %p, obj %p\n", + handler, acpi_ut_get_region_name(space_id), space_id, + node, obj_desc)); + + status = AE_NOT_EXIST; + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_address_space_handler) diff --git a/kernel/drivers/acpi/acpica/exconfig.c b/kernel/drivers/acpi/acpica/exconfig.c new file mode 100644 index 000000000..6e0df2b9d --- /dev/null +++ b/kernel/drivers/acpi/acpica/exconfig.c @@ -0,0 +1,634 @@ +/****************************************************************************** + * + * Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "actables.h" +#include "acdispat.h" +#include "acevents.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exconfig") + +/* Local prototypes */ +static acpi_status +acpi_ex_add_table(u32 table_index, + struct acpi_namespace_node *parent_node, + union acpi_operand_object **ddb_handle); + +static acpi_status +acpi_ex_region_read(union acpi_operand_object *obj_desc, + u32 length, u8 *buffer); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_add_table + * + * PARAMETERS: table - Pointer to raw table + * parent_node - Where to load the table (scope) + * ddb_handle - Where to return the table handle. + * + * RETURN: Status + * + * DESCRIPTION: Common function to Install and Load an ACPI table with a + * returned table handle. + * + ******************************************************************************/ + +static acpi_status +acpi_ex_add_table(u32 table_index, + struct acpi_namespace_node *parent_node, + union acpi_operand_object **ddb_handle) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + acpi_owner_id owner_id; + + ACPI_FUNCTION_TRACE(ex_add_table); + + /* Create an object to be the table handle */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE); + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Init the table handle */ + + obj_desc->common.flags |= AOPOBJ_DATA_VALID; + obj_desc->reference.class = ACPI_REFCLASS_TABLE; + *ddb_handle = obj_desc; + + /* Install the new table into the local data structures */ + + obj_desc->reference.value = table_index; + + /* Add the table to the namespace */ + + status = acpi_ns_load_table(table_index, parent_node); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(obj_desc); + *ddb_handle = NULL; + return_ACPI_STATUS(status); + } + + /* Execute any module-level code that was found in the table */ + + acpi_ex_exit_interpreter(); + acpi_ns_exec_module_code_list(); + acpi_ex_enter_interpreter(); + + /* + * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is + * responsible for discovering any new wake GPEs by running _PRW methods + * that may have been loaded by this table. + */ + status = acpi_tb_get_owner_id(table_index, &owner_id); + if (ACPI_SUCCESS(status)) { + acpi_ev_update_gpes(owner_id); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_load_table_op + * + * PARAMETERS: walk_state - Current state with operands + * return_desc - Where to store the return object + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table from the RSDT/XSDT + * + ******************************************************************************/ + +acpi_status +acpi_ex_load_table_op(struct acpi_walk_state *walk_state, + union acpi_operand_object **return_desc) +{ + acpi_status status; + union acpi_operand_object **operand = &walk_state->operands[0]; + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *start_node; + struct acpi_namespace_node *parameter_node = NULL; + union acpi_operand_object *ddb_handle; + struct acpi_table_header *table; + u32 table_index; + + ACPI_FUNCTION_TRACE(ex_load_table_op); + + /* Validate lengths for the Signature, oem_id, and oem_table_id strings */ + + if ((operand[0]->string.length > ACPI_NAME_SIZE) || + (operand[1]->string.length > ACPI_OEM_ID_SIZE) || + (operand[2]->string.length > ACPI_OEM_TABLE_ID_SIZE)) { + return_ACPI_STATUS(AE_AML_STRING_LIMIT); + } + + /* Find the ACPI table in the RSDT/XSDT */ + + status = acpi_tb_find_table(operand[0]->string.pointer, + operand[1]->string.pointer, + operand[2]->string.pointer, &table_index); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + return_ACPI_STATUS(status); + } + + /* Table not found, return an Integer=0 and AE_OK */ + + ddb_handle = acpi_ut_create_integer_object((u64) 0); + if (!ddb_handle) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + *return_desc = ddb_handle; + return_ACPI_STATUS(AE_OK); + } + + /* Default nodes */ + + start_node = walk_state->scope_info->scope.node; + parent_node = acpi_gbl_root_node; + + /* root_path (optional parameter) */ + + if (operand[3]->string.length > 0) { + /* + * Find the node referenced by the root_path_string. This is the + * location within the namespace where the table will be loaded. + */ + status = + acpi_ns_get_node(start_node, operand[3]->string.pointer, + ACPI_NS_SEARCH_PARENT, &parent_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* parameter_path (optional parameter) */ + + if (operand[4]->string.length > 0) { + if ((operand[4]->string.pointer[0] != AML_ROOT_PREFIX) && + (operand[4]->string.pointer[0] != AML_PARENT_PREFIX)) { + /* + * Path is not absolute, so it will be relative to the node + * referenced by the root_path_string (or the NS root if omitted) + */ + start_node = parent_node; + } + + /* Find the node referenced by the parameter_path_string */ + + status = + acpi_ns_get_node(start_node, operand[4]->string.pointer, + ACPI_NS_SEARCH_PARENT, ¶meter_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* Load the table into the namespace */ + + status = acpi_ex_add_table(table_index, parent_node, &ddb_handle); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Parameter Data (optional) */ + + if (parameter_node) { + + /* Store the parameter data into the optional parameter object */ + + status = acpi_ex_store(operand[5], + ACPI_CAST_PTR(union acpi_operand_object, + parameter_node), + walk_state); + if (ACPI_FAILURE(status)) { + (void)acpi_ex_unload_table(ddb_handle); + + acpi_ut_remove_reference(ddb_handle); + return_ACPI_STATUS(status); + } + } + + status = acpi_get_table_by_index(table_index, &table); + if (ACPI_SUCCESS(status)) { + ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:")); + acpi_tb_print_table_header(0, table); + } + + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, + acpi_gbl_table_handler_context); + } + + *return_desc = ddb_handle; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_region_read + * + * PARAMETERS: obj_desc - Region descriptor + * length - Number of bytes to read + * buffer - Pointer to where to put the data + * + * RETURN: Status + * + * DESCRIPTION: Read data from an operation region. The read starts from the + * beginning of the region. + * + ******************************************************************************/ + +static acpi_status +acpi_ex_region_read(union acpi_operand_object *obj_desc, u32 length, u8 *buffer) +{ + acpi_status status; + u64 value; + u32 region_offset = 0; + u32 i; + + /* Bytewise reads */ + + for (i = 0; i < length; i++) { + status = + acpi_ev_address_space_dispatch(obj_desc, NULL, ACPI_READ, + region_offset, 8, &value); + if (ACPI_FAILURE(status)) { + return (status); + } + + *buffer = (u8)value; + buffer++; + region_offset++; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_load_op + * + * PARAMETERS: obj_desc - Region or Buffer/Field where the table will be + * obtained + * target - Where a handle to the table will be stored + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table from a field or operation region + * + * NOTE: Region Fields (Field, bank_field, index_fields) are resolved to buffer + * objects before this code is reached. + * + * If source is an operation region, it must refer to system_memory, as + * per the ACPI specification. + * + ******************************************************************************/ + +acpi_status +acpi_ex_load_op(union acpi_operand_object *obj_desc, + union acpi_operand_object *target, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *ddb_handle; + struct acpi_table_header *table_header; + struct acpi_table_header *table; + u32 table_index; + acpi_status status; + u32 length; + + ACPI_FUNCTION_TRACE(ex_load_op); + + /* Source Object can be either an op_region or a Buffer/Field */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_REGION: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Load table from Region %p\n", obj_desc)); + + /* Region must be system_memory (from ACPI spec) */ + + if (obj_desc->region.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) { + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* + * If the Region Address and Length have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_region_arguments(obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* Get the table header first so we can get the table length */ + + table_header = ACPI_ALLOCATE(sizeof(struct acpi_table_header)); + if (!table_header) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + status = + acpi_ex_region_read(obj_desc, + sizeof(struct acpi_table_header), + ACPI_CAST_PTR(u8, table_header)); + length = table_header->length; + ACPI_FREE(table_header); + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Must have at least an ACPI table header */ + + if (length < sizeof(struct acpi_table_header)) { + return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); + } + + /* + * The original implementation simply mapped the table, with no copy. + * However, the memory region is not guaranteed to remain stable and + * we must copy the table to a local buffer. For example, the memory + * region is corrupted after suspend on some machines. Dynamically + * loaded tables are usually small, so this overhead is minimal. + * + * The latest implementation (5/2009) does not use a mapping at all. + * We use the low-level operation region interface to read the table + * instead of the obvious optimization of using a direct mapping. + * This maintains a consistent use of operation regions across the + * entire subsystem. This is important if additional processing must + * be performed in the (possibly user-installed) operation region + * handler. For example, acpi_exec and ASLTS depend on this. + */ + + /* Allocate a buffer for the table */ + + table = ACPI_ALLOCATE(length); + if (!table) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Read the entire table */ + + status = acpi_ex_region_read(obj_desc, length, + ACPI_CAST_PTR(u8, table)); + if (ACPI_FAILURE(status)) { + ACPI_FREE(table); + return_ACPI_STATUS(status); + } + break; + + case ACPI_TYPE_BUFFER: /* Buffer or resolved region_field */ + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Load table from Buffer or Field %p\n", + obj_desc)); + + /* Must have at least an ACPI table header */ + + if (obj_desc->buffer.length < sizeof(struct acpi_table_header)) { + return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); + } + + /* Get the actual table length from the table header */ + + table_header = + ACPI_CAST_PTR(struct acpi_table_header, + obj_desc->buffer.pointer); + length = table_header->length; + + /* Table cannot extend beyond the buffer */ + + if (length > obj_desc->buffer.length) { + return_ACPI_STATUS(AE_AML_BUFFER_LIMIT); + } + if (length < sizeof(struct acpi_table_header)) { + return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); + } + + /* + * Copy the table from the buffer because the buffer could be modified + * or even deleted in the future + */ + table = ACPI_ALLOCATE(length); + if (!table) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + ACPI_MEMCPY(table, table_header, length); + break; + + default: + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* Install the new table into the local data structures */ + + ACPI_INFO((AE_INFO, "Dynamic OEM Table Load:")); + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + + status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, + TRUE, TRUE, &table_index); + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + if (ACPI_FAILURE(status)) { + + /* Delete allocated table buffer */ + + ACPI_FREE(table); + return_ACPI_STATUS(status); + } + + /* + * Note: Now table is "INSTALLED", it must be validated before + * loading. + */ + status = + acpi_tb_validate_table(&acpi_gbl_root_table_list. + tables[table_index]); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Add the table to the namespace. + * + * Note: Load the table objects relative to the root of the namespace. + * This appears to go against the ACPI specification, but we do it for + * compatibility with other ACPI implementations. + */ + status = + acpi_ex_add_table(table_index, acpi_gbl_root_node, &ddb_handle); + if (ACPI_FAILURE(status)) { + + /* On error, table_ptr was deallocated above */ + + return_ACPI_STATUS(status); + } + + /* Store the ddb_handle into the Target operand */ + + status = acpi_ex_store(ddb_handle, target, walk_state); + if (ACPI_FAILURE(status)) { + (void)acpi_ex_unload_table(ddb_handle); + + /* table_ptr was deallocated above */ + + acpi_ut_remove_reference(ddb_handle); + return_ACPI_STATUS(status); + } + + /* Remove the reference by added by acpi_ex_store above */ + + acpi_ut_remove_reference(ddb_handle); + + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, + acpi_gbl_table_handler_context); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_unload_table + * + * PARAMETERS: ddb_handle - Handle to a previously loaded table + * + * RETURN: Status + * + * DESCRIPTION: Unload an ACPI table + * + ******************************************************************************/ + +acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) +{ + acpi_status status = AE_OK; + union acpi_operand_object *table_desc = ddb_handle; + u32 table_index; + struct acpi_table_header *table; + + ACPI_FUNCTION_TRACE(ex_unload_table); + + /* + * Temporarily emit a warning so that the ASL for the machine can be + * hopefully obtained. This is to say that the Unload() operator is + * extremely rare if not completely unused. + */ + ACPI_WARNING((AE_INFO, "Received request to unload an ACPI table")); + + /* + * Validate the handle + * Although the handle is partially validated in acpi_ex_reconfiguration() + * when it calls acpi_ex_resolve_operands(), the handle is more completely + * validated here. + * + * Handle must be a valid operand object of type reference. Also, the + * ddb_handle must still be marked valid (table has not been previously + * unloaded) + */ + if ((!ddb_handle) || + (ACPI_GET_DESCRIPTOR_TYPE(ddb_handle) != ACPI_DESC_TYPE_OPERAND) || + (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE) || + (!(ddb_handle->common.flags & AOPOBJ_DATA_VALID))) { + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* Get the table index from the ddb_handle */ + + table_index = table_desc->reference.value; + + /* Ensure the table is still loaded */ + + if (!acpi_tb_is_table_loaded(table_index)) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + status = acpi_get_table_by_index(table_index, &table); + if (ACPI_SUCCESS(status)) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_UNLOAD, + table, + acpi_gbl_table_handler_context); + } + } + + /* Delete the portion of the namespace owned by this table */ + + status = acpi_tb_delete_namespace_by_owner(table_index); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + (void)acpi_tb_release_owner_id(table_index); + acpi_tb_set_table_loaded_flag(table_index, FALSE); + + /* + * Invalidate the handle. We do this because the handle may be stored + * in a named object and may not be actually deleted until much later. + */ + ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID; + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/exconvrt.c b/kernel/drivers/acpi/acpica/exconvrt.c new file mode 100644 index 000000000..89a976b4c --- /dev/null +++ b/kernel/drivers/acpi/acpica/exconvrt.c @@ -0,0 +1,694 @@ +/****************************************************************************** + * + * Module Name: exconvrt - Object conversion routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exconvrt") + +/* Local prototypes */ +static u32 +acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 max_length); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_integer + * + * PARAMETERS: obj_desc - Object to be converted. Must be an + * Integer, Buffer, or String + * result_desc - Where the new Integer object is returned + * flags - Used for string conversion + * + * RETURN: Status + * + * DESCRIPTION: Convert an ACPI Object to an integer. + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc, u32 flags) +{ + union acpi_operand_object *return_desc; + u8 *pointer; + u64 result; + u32 i; + u32 count; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ex_convert_to_integer, obj_desc); + + switch (obj_desc->common.type) { + case ACPI_TYPE_INTEGER: + + /* No conversion necessary */ + + *result_desc = obj_desc; + return_ACPI_STATUS(AE_OK); + + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + + /* Note: Takes advantage of common buffer/string fields */ + + pointer = obj_desc->buffer.pointer; + count = obj_desc->buffer.length; + break; + + default: + + return_ACPI_STATUS(AE_TYPE); + } + + /* + * Convert the buffer/string to an integer. Note that both buffers and + * strings are treated as raw data - we don't convert ascii to hex for + * strings. + * + * There are two terminating conditions for the loop: + * 1) The size of an integer has been reached, or + * 2) The end of the buffer or string has been reached + */ + result = 0; + + /* String conversion is different than Buffer conversion */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_STRING: + /* + * Convert string to an integer - for most cases, the string must be + * hexadecimal as per the ACPI specification. The only exception (as + * of ACPI 3.0) is that the to_integer() operator allows both decimal + * and hexadecimal strings (hex prefixed with "0x"). + */ + status = acpi_ut_strtoul64((char *)pointer, flags, &result); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + break; + + case ACPI_TYPE_BUFFER: + + /* Check for zero-length buffer */ + + if (!count) { + return_ACPI_STATUS(AE_AML_BUFFER_LIMIT); + } + + /* Transfer no more than an integer's worth of data */ + + if (count > acpi_gbl_integer_byte_width) { + count = acpi_gbl_integer_byte_width; + } + + /* + * Convert buffer to an integer - we simply grab enough raw data + * from the buffer to fill an integer + */ + for (i = 0; i < count; i++) { + /* + * Get next byte and shift it into the Result. + * Little endian is used, meaning that the first byte of the buffer + * is the LSB of the integer + */ + result |= (((u64) pointer[i]) << (i * 8)); + } + break; + + default: + + /* No other types can get here */ + + break; + } + + /* Create a new integer */ + + return_desc = acpi_ut_create_integer_object(result); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(result))); + + /* Save the Result */ + + (void)acpi_ex_truncate_for32bit_table(return_desc); + *result_desc = return_desc; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_buffer + * + * PARAMETERS: obj_desc - Object to be converted. Must be an + * Integer, Buffer, or String + * result_desc - Where the new buffer object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert an ACPI Object to a Buffer + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc) +{ + union acpi_operand_object *return_desc; + u8 *new_buf; + + ACPI_FUNCTION_TRACE_PTR(ex_convert_to_buffer, obj_desc); + + switch (obj_desc->common.type) { + case ACPI_TYPE_BUFFER: + + /* No conversion necessary */ + + *result_desc = obj_desc; + return_ACPI_STATUS(AE_OK); + + case ACPI_TYPE_INTEGER: + /* + * Create a new Buffer object. + * Need enough space for one integer + */ + return_desc = + acpi_ut_create_buffer_object(acpi_gbl_integer_byte_width); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Copy the integer to the buffer, LSB first */ + + new_buf = return_desc->buffer.pointer; + ACPI_MEMCPY(new_buf, + &obj_desc->integer.value, + acpi_gbl_integer_byte_width); + break; + + case ACPI_TYPE_STRING: + /* + * Create a new Buffer object + * Size will be the string length + * + * NOTE: Add one to the string length to include the null terminator. + * The ACPI spec is unclear on this subject, but there is existing + * ASL/AML code that depends on the null being transferred to the new + * buffer. + */ + return_desc = acpi_ut_create_buffer_object((acpi_size) + obj_desc->string. + length + 1); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Copy the string to the buffer */ + + new_buf = return_desc->buffer.pointer; + ACPI_STRNCPY((char *)new_buf, (char *)obj_desc->string.pointer, + obj_desc->string.length); + break; + + default: + + return_ACPI_STATUS(AE_TYPE); + } + + /* Mark buffer initialized */ + + return_desc->common.flags |= AOPOBJ_DATA_VALID; + *result_desc = return_desc; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_ascii + * + * PARAMETERS: integer - Value to be converted + * base - ACPI_STRING_DECIMAL or ACPI_STRING_HEX + * string - Where the string is returned + * data_width - Size of data item to be converted, in bytes + * + * RETURN: Actual string length + * + * DESCRIPTION: Convert an ACPI Integer to a hex or decimal string + * + ******************************************************************************/ + +static u32 +acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width) +{ + u64 digit; + u32 i; + u32 j; + u32 k = 0; + u32 hex_length; + u32 decimal_length; + u32 remainder; + u8 supress_zeros; + + ACPI_FUNCTION_ENTRY(); + + switch (base) { + case 10: + + /* Setup max length for the decimal number */ + + switch (data_width) { + case 1: + + decimal_length = ACPI_MAX8_DECIMAL_DIGITS; + break; + + case 4: + + decimal_length = ACPI_MAX32_DECIMAL_DIGITS; + break; + + case 8: + default: + + decimal_length = ACPI_MAX64_DECIMAL_DIGITS; + break; + } + + supress_zeros = TRUE; /* No leading zeros */ + remainder = 0; + + for (i = decimal_length; i > 0; i--) { + + /* Divide by nth factor of 10 */ + + digit = integer; + for (j = 0; j < i; j++) { + (void)acpi_ut_short_divide(digit, 10, &digit, + &remainder); + } + + /* Handle leading zeros */ + + if (remainder != 0) { + supress_zeros = FALSE; + } + + if (!supress_zeros) { + string[k] = (u8) (ACPI_ASCII_ZERO + remainder); + k++; + } + } + break; + + case 16: + + /* hex_length: 2 ascii hex chars per data byte */ + + hex_length = ACPI_MUL_2(data_width); + for (i = 0, j = (hex_length - 1); i < hex_length; i++, j--) { + + /* Get one hex digit, most significant digits first */ + + string[k] = + (u8) acpi_ut_hex_to_ascii_char(integer, + ACPI_MUL_4(j)); + k++; + } + break; + + default: + return (0); + } + + /* + * Since leading zeros are suppressed, we must check for the case where + * the integer equals 0 + * + * Finally, null terminate the string and return the length + */ + if (!k) { + string[0] = ACPI_ASCII_ZERO; + k = 1; + } + + string[k] = 0; + return ((u32) k); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_string + * + * PARAMETERS: obj_desc - Object to be converted. Must be an + * Integer, Buffer, or String + * result_desc - Where the string object is returned + * type - String flags (base and conversion type) + * + * RETURN: Status + * + * DESCRIPTION: Convert an ACPI Object to a string + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_string(union acpi_operand_object * obj_desc, + union acpi_operand_object ** result_desc, u32 type) +{ + union acpi_operand_object *return_desc; + u8 *new_buf; + u32 i; + u32 string_length = 0; + u16 base = 16; + u8 separator = ','; + + ACPI_FUNCTION_TRACE_PTR(ex_convert_to_string, obj_desc); + + switch (obj_desc->common.type) { + case ACPI_TYPE_STRING: + + /* No conversion necessary */ + + *result_desc = obj_desc; + return_ACPI_STATUS(AE_OK); + + case ACPI_TYPE_INTEGER: + + switch (type) { + case ACPI_EXPLICIT_CONVERT_DECIMAL: + + /* Make room for maximum decimal number */ + + string_length = ACPI_MAX_DECIMAL_DIGITS; + base = 10; + break; + + default: + + /* Two hex string characters for each integer byte */ + + string_length = ACPI_MUL_2(acpi_gbl_integer_byte_width); + break; + } + + /* + * Create a new String + * Need enough space for one ASCII integer (plus null terminator) + */ + return_desc = + acpi_ut_create_string_object((acpi_size) string_length); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + new_buf = return_desc->buffer.pointer; + + /* Convert integer to string */ + + string_length = + acpi_ex_convert_to_ascii(obj_desc->integer.value, base, + new_buf, + acpi_gbl_integer_byte_width); + + /* Null terminate at the correct place */ + + return_desc->string.length = string_length; + new_buf[string_length] = 0; + break; + + case ACPI_TYPE_BUFFER: + + /* Setup string length, base, and separator */ + + switch (type) { + case ACPI_EXPLICIT_CONVERT_DECIMAL: /* Used by to_decimal_string */ + /* + * From ACPI: "If Data is a buffer, it is converted to a string of + * decimal values separated by commas." + */ + base = 10; + + /* + * Calculate the final string length. Individual string values + * are variable length (include separator for each) + */ + for (i = 0; i < obj_desc->buffer.length; i++) { + if (obj_desc->buffer.pointer[i] >= 100) { + string_length += 4; + } else if (obj_desc->buffer.pointer[i] >= 10) { + string_length += 3; + } else { + string_length += 2; + } + } + break; + + case ACPI_IMPLICIT_CONVERT_HEX: + /* + * From the ACPI spec: + *"The entire contents of the buffer are converted to a string of + * two-character hexadecimal numbers, each separated by a space." + */ + separator = ' '; + string_length = (obj_desc->buffer.length * 3); + break; + + case ACPI_EXPLICIT_CONVERT_HEX: /* Used by to_hex_string */ + /* + * From ACPI: "If Data is a buffer, it is converted to a string of + * hexadecimal values separated by commas." + */ + string_length = (obj_desc->buffer.length * 3); + break; + + default: + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * Create a new string object and string buffer + * (-1 because of extra separator included in string_length from above) + * Allow creation of zero-length strings from zero-length buffers. + */ + if (string_length) { + string_length--; + } + + return_desc = + acpi_ut_create_string_object((acpi_size) string_length); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + new_buf = return_desc->buffer.pointer; + + /* + * Convert buffer bytes to hex or decimal values + * (separated by commas or spaces) + */ + for (i = 0; i < obj_desc->buffer.length; i++) { + new_buf += acpi_ex_convert_to_ascii((u64) obj_desc-> + buffer.pointer[i], + base, new_buf, 1); + *new_buf++ = separator; /* each separated by a comma or space */ + } + + /* + * Null terminate the string + * (overwrites final comma/space from above) + */ + if (obj_desc->buffer.length) { + new_buf--; + } + *new_buf = 0; + break; + + default: + + return_ACPI_STATUS(AE_TYPE); + } + + *result_desc = return_desc; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_target_type + * + * PARAMETERS: destination_type - Current type of the destination + * source_desc - Source object to be converted. + * result_desc - Where the converted object is returned + * walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Implements "implicit conversion" rules for storing an object. + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_target_type(acpi_object_type destination_type, + union acpi_operand_object *source_desc, + union acpi_operand_object **result_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_convert_to_target_type); + + /* Default behavior */ + + *result_desc = source_desc; + + /* + * If required by the target, + * perform implicit conversion on the source before we store it. + */ + switch (GET_CURRENT_ARG_TYPE(walk_state->op_info->runtime_args)) { + case ARGI_SIMPLE_TARGET: + case ARGI_FIXED_TARGET: + case ARGI_INTEGER_REF: /* Handles Increment, Decrement cases */ + + switch (destination_type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + /* + * Named field can always handle conversions + */ + break; + + default: + + /* No conversion allowed for these types */ + + if (destination_type != source_desc->common.type) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Explicit operator, will store (%s) over existing type (%s)\n", + acpi_ut_get_object_type_name + (source_desc), + acpi_ut_get_type_name + (destination_type))); + status = AE_TYPE; + } + } + break; + + case ARGI_TARGETREF: + + switch (destination_type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + /* + * These types require an Integer operand. We can convert + * a Buffer or a String to an Integer if necessary. + */ + status = + acpi_ex_convert_to_integer(source_desc, result_desc, + 16); + break; + + case ACPI_TYPE_STRING: + /* + * The operand must be a String. We can convert an + * Integer or Buffer if necessary + */ + status = + acpi_ex_convert_to_string(source_desc, result_desc, + ACPI_IMPLICIT_CONVERT_HEX); + break; + + case ACPI_TYPE_BUFFER: + /* + * The operand must be a Buffer. We can convert an + * Integer or String if necessary + */ + status = + acpi_ex_convert_to_buffer(source_desc, result_desc); + break; + + default: + + ACPI_ERROR((AE_INFO, + "Bad destination type during conversion: 0x%X", + destination_type)); + status = AE_AML_INTERNAL; + break; + } + break; + + case ARGI_REFERENCE: + /* + * create_xxxx_field cases - we are storing the field object into the name + */ + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown Target type ID 0x%X AmlOpcode 0x%X DestType %s", + GET_CURRENT_ARG_TYPE(walk_state->op_info-> + runtime_args), + walk_state->opcode, + acpi_ut_get_type_name(destination_type))); + status = AE_AML_INTERNAL; + } + + /* + * Source-to-Target conversion semantics: + * + * If conversion to the target type cannot be performed, then simply + * overwrite the target with the new object and type. + */ + if (status == AE_TYPE) { + status = AE_OK; + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/excreate.c b/kernel/drivers/acpi/acpica/excreate.c new file mode 100644 index 000000000..aaeea4840 --- /dev/null +++ b/kernel/drivers/acpi/acpica/excreate.c @@ -0,0 +1,528 @@ +/****************************************************************************** + * + * Module Name: excreate - Named object creation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("excreate") +#ifndef ACPI_NO_METHOD_EXECUTION +/******************************************************************************* + * + * FUNCTION: acpi_ex_create_alias + * + * PARAMETERS: walk_state - Current state, contains operands + * + * RETURN: Status + * + * DESCRIPTION: Create a new named alias + * + ******************************************************************************/ +acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state) +{ + struct acpi_namespace_node *target_node; + struct acpi_namespace_node *alias_node; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_create_alias); + + /* Get the source/alias operands (both namespace nodes) */ + + alias_node = (struct acpi_namespace_node *)walk_state->operands[0]; + target_node = (struct acpi_namespace_node *)walk_state->operands[1]; + + if ((target_node->type == ACPI_TYPE_LOCAL_ALIAS) || + (target_node->type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) { + /* + * Dereference an existing alias so that we don't create a chain + * of aliases. With this code, we guarantee that an alias is + * always exactly one level of indirection away from the + * actual aliased name. + */ + target_node = + ACPI_CAST_PTR(struct acpi_namespace_node, + target_node->object); + } + + /* + * For objects that can never change (i.e., the NS node will + * permanently point to the same object), we can simply attach + * the object to the new NS node. For other objects (such as + * Integers, buffers, etc.), we have to point the Alias node + * to the original Node. + */ + switch (target_node->type) { + + /* For these types, the sub-object can change dynamically via a Store */ + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_BUFFER_FIELD: + /* + * These types open a new scope, so we need the NS node in order to access + * any children. + */ + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + case ACPI_TYPE_LOCAL_SCOPE: + /* + * The new alias has the type ALIAS and points to the original + * NS node, not the object itself. + */ + alias_node->type = ACPI_TYPE_LOCAL_ALIAS; + alias_node->object = + ACPI_CAST_PTR(union acpi_operand_object, target_node); + break; + + case ACPI_TYPE_METHOD: + /* + * Control method aliases need to be differentiated + */ + alias_node->type = ACPI_TYPE_LOCAL_METHOD_ALIAS; + alias_node->object = + ACPI_CAST_PTR(union acpi_operand_object, target_node); + break; + + default: + + /* Attach the original source object to the new Alias Node */ + + /* + * The new alias assumes the type of the target, and it points + * to the same object. The reference count of the object has an + * additional reference to prevent deletion out from under either the + * target node or the alias Node + */ + status = acpi_ns_attach_object(alias_node, + acpi_ns_get_attached_object + (target_node), + target_node->type); + break; + } + + /* Since both operands are Nodes, we don't need to delete them */ + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_create_event + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new event object + * + ******************************************************************************/ + +acpi_status acpi_ex_create_event(struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_TRACE(ex_create_event); + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_EVENT); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Create the actual OS semaphore, with zero initial units -- meaning + * that the event is created in an unsignalled state + */ + status = acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0, + &obj_desc->event.os_semaphore); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Attach object to the Node */ + + status = + acpi_ns_attach_object((struct acpi_namespace_node *)walk_state-> + operands[0], obj_desc, ACPI_TYPE_EVENT); + +cleanup: + /* + * Remove local reference to the object (on error, will cause deletion + * of both object and semaphore if present.) + */ + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_create_mutex + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new mutex object + * + * Mutex (Name[0], sync_level[1]) + * + ******************************************************************************/ + +acpi_status acpi_ex_create_mutex(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_TRACE_PTR(ex_create_mutex, ACPI_WALK_OPERANDS); + + /* Create the new mutex object */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_MUTEX); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Create the actual OS Mutex */ + + status = acpi_os_create_mutex(&obj_desc->mutex.os_mutex); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Init object and attach to NS node */ + + obj_desc->mutex.sync_level = (u8)walk_state->operands[1]->integer.value; + obj_desc->mutex.node = + (struct acpi_namespace_node *)walk_state->operands[0]; + + status = + acpi_ns_attach_object(obj_desc->mutex.node, obj_desc, + ACPI_TYPE_MUTEX); + +cleanup: + /* + * Remove local reference to the object (on error, will cause deletion + * of both object and semaphore if present.) + */ + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_create_region + * + * PARAMETERS: aml_start - Pointer to the region declaration AML + * aml_length - Max length of the declaration AML + * space_id - Address space ID for the region + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new operation region object + * + ******************************************************************************/ + +acpi_status +acpi_ex_create_region(u8 * aml_start, + u32 aml_length, + u8 space_id, struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + union acpi_operand_object *region_obj2; + + ACPI_FUNCTION_TRACE(ex_create_region); + + /* Get the Namespace Node */ + + node = walk_state->op->common.node; + + /* + * If the region object is already attached to this node, + * just return + */ + if (acpi_ns_get_attached_object(node)) { + return_ACPI_STATUS(AE_OK); + } + + /* + * Space ID must be one of the predefined IDs, or in the user-defined + * range + */ + if (!acpi_is_valid_space_id(space_id)) { + /* + * Print an error message, but continue. We don't want to abort + * a table load for this exception. Instead, if the region is + * actually used at runtime, abort the executing method. + */ + ACPI_ERROR((AE_INFO, + "Invalid/unknown Address Space ID: 0x%2.2X", + space_id)); + } + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Region Type - %s (0x%X)\n", + acpi_ut_get_region_name(space_id), space_id)); + + /* Create the region descriptor */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_REGION); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Remember location in AML stream of address & length + * operands since they need to be evaluated at run time. + */ + region_obj2 = obj_desc->common.next_object; + region_obj2->extra.aml_start = aml_start; + region_obj2->extra.aml_length = aml_length; + if (walk_state->scope_info) { + region_obj2->extra.scope_node = + walk_state->scope_info->scope.node; + } else { + region_obj2->extra.scope_node = node; + } + + /* Init the region from the operands */ + + obj_desc->region.space_id = space_id; + obj_desc->region.address = 0; + obj_desc->region.length = 0; + obj_desc->region.node = node; + + /* Install the new region object in the parent Node */ + + status = acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_REGION); + +cleanup: + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_create_processor + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new processor object and populate the fields + * + * Processor (Name[0], cpu_ID[1], pblock_addr[2], pblock_length[3]) + * + ******************************************************************************/ + +acpi_status acpi_ex_create_processor(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ex_create_processor, walk_state); + + /* Create the processor object */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_PROCESSOR); + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Initialize the processor object from the operands */ + + obj_desc->processor.proc_id = (u8) operand[1]->integer.value; + obj_desc->processor.length = (u8) operand[3]->integer.value; + obj_desc->processor.address = + (acpi_io_address) operand[2]->integer.value; + + /* Install the processor object in the parent Node */ + + status = acpi_ns_attach_object((struct acpi_namespace_node *)operand[0], + obj_desc, ACPI_TYPE_PROCESSOR); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_create_power_resource + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new power_resource object and populate the fields + * + * power_resource (Name[0], system_level[1], resource_order[2]) + * + ******************************************************************************/ + +acpi_status acpi_ex_create_power_resource(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + acpi_status status; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_TRACE_PTR(ex_create_power_resource, walk_state); + + /* Create the power resource object */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_POWER); + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Initialize the power object from the operands */ + + obj_desc->power_resource.system_level = (u8) operand[1]->integer.value; + obj_desc->power_resource.resource_order = + (u16) operand[2]->integer.value; + + /* Install the power resource object in the parent Node */ + + status = acpi_ns_attach_object((struct acpi_namespace_node *)operand[0], + obj_desc, ACPI_TYPE_POWER); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ex_create_method + * + * PARAMETERS: aml_start - First byte of the method's AML + * aml_length - AML byte count for this method + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new method object + * + ******************************************************************************/ + +acpi_status +acpi_ex_create_method(u8 * aml_start, + u32 aml_length, struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *obj_desc; + acpi_status status; + u8 method_flags; + + ACPI_FUNCTION_TRACE_PTR(ex_create_method, walk_state); + + /* Create a new method object */ + + obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto exit; + } + + /* Save the method's AML pointer and length */ + + obj_desc->method.aml_start = aml_start; + obj_desc->method.aml_length = aml_length; + + /* + * Disassemble the method flags. Split off the arg_count, Serialized + * flag, and sync_level for efficiency. + */ + method_flags = (u8) operand[1]->integer.value; + + obj_desc->method.param_count = + (u8) (method_flags & AML_METHOD_ARG_COUNT); + + /* + * Get the sync_level. If method is serialized, a mutex will be + * created for this method when it is parsed. + */ + if (method_flags & AML_METHOD_SERIALIZED) { + obj_desc->method.info_flags = ACPI_METHOD_SERIALIZED; + + /* + * ACPI 1.0: sync_level = 0 + * ACPI 2.0: sync_level = sync_level in method declaration + */ + obj_desc->method.sync_level = (u8) + ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4); + } + + /* Attach the new object to the method Node */ + + status = acpi_ns_attach_object((struct acpi_namespace_node *)operand[0], + obj_desc, ACPI_TYPE_METHOD); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + +exit: + /* Remove a reference to the operand */ + + acpi_ut_remove_reference(operand[1]); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exdebug.c b/kernel/drivers/acpi/acpica/exdebug.c new file mode 100644 index 000000000..e67d0aca3 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exdebug.c @@ -0,0 +1,272 @@ +/****************************************************************************** + * + * Module Name: exdebug - Support for stores to the AML Debug Object + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exdebug") + +#ifndef ACPI_NO_ERROR_MESSAGES +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_debug_object + * + * PARAMETERS: source_desc - Object to be output to "Debug Object" + * level - Indentation level (used for packages) + * index - Current package element, zero if not pkg + * + * RETURN: None + * + * DESCRIPTION: Handles stores to the AML Debug Object. For example: + * Store(INT1, Debug) + * + * This function is not compiled if ACPI_NO_ERROR_MESSAGES is set. + * + * This function is only enabled if acpi_gbl_enable_aml_debug_object is set, or + * if ACPI_LV_DEBUG_OBJECT is set in the acpi_dbg_level. Thus, in the normal + * operational case, stores to the debug object are ignored but can be easily + * enabled if necessary. + * + ******************************************************************************/ +void +acpi_ex_do_debug_object(union acpi_operand_object *source_desc, + u32 level, u32 index) +{ + u32 i; + u32 timer; + + ACPI_FUNCTION_TRACE_PTR(ex_do_debug_object, source_desc); + + /* Output must be enabled via the debug_object global or the dbg_level */ + + if (!acpi_gbl_enable_aml_debug_object && + !(acpi_dbg_level & ACPI_LV_DEBUG_OBJECT)) { + return_VOID; + } + + /* + * We will emit the current timer value (in microseconds) with each + * debug output. Only need the lower 26 bits. This allows for 67 + * million microseconds or 67 seconds before rollover. + */ + timer = ((u32)acpi_os_get_timer() / 10); /* (100 nanoseconds to microseconds) */ + timer &= 0x03FFFFFF; + + /* + * Print line header as long as we are not in the middle of an + * object display + */ + if (!((level > 0) && index == 0)) { + acpi_os_printf("[ACPI Debug %.8u] %*s", timer, level, " "); + } + + /* Display the index for package output only */ + + if (index > 0) { + acpi_os_printf("(%.2u) ", index - 1); + } + + if (!source_desc) { + acpi_os_printf("[Null Object]\n"); + return_VOID; + } + + if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf("%s ", + acpi_ut_get_object_type_name(source_desc)); + + if (!acpi_ut_valid_internal_object(source_desc)) { + acpi_os_printf("%p, Invalid Internal Object!\n", + source_desc); + return_VOID; + } + } else if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == + ACPI_DESC_TYPE_NAMED) { + acpi_os_printf("%s: %p\n", + acpi_ut_get_type_name(((struct + acpi_namespace_node *) + source_desc)->type), + source_desc); + return_VOID; + } else { + return_VOID; + } + + /* source_desc is of type ACPI_DESC_TYPE_OPERAND */ + + switch (source_desc->common.type) { + case ACPI_TYPE_INTEGER: + + /* Output correct integer width */ + + if (acpi_gbl_integer_byte_width == 4) { + acpi_os_printf("0x%8.8X\n", + (u32)source_desc->integer.value); + } else { + acpi_os_printf("0x%8.8X%8.8X\n", + ACPI_FORMAT_UINT64(source_desc->integer. + value)); + } + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("[0x%.2X]\n", (u32)source_desc->buffer.length); + acpi_ut_dump_buffer(source_desc->buffer.pointer, + (source_desc->buffer.length < 256) ? + source_desc->buffer.length : 256, + DB_BYTE_DISPLAY, 0); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("[0x%.2X] \"%s\"\n", + source_desc->string.length, + source_desc->string.pointer); + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("[Contains 0x%.2X Elements]\n", + source_desc->package.count); + + /* Output the entire contents of the package */ + + for (i = 0; i < source_desc->package.count; i++) { + acpi_ex_do_debug_object(source_desc->package. + elements[i], level + 4, i + 1); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[%s] ", + acpi_ut_get_reference_name(source_desc)); + + /* Decode the reference */ + + switch (source_desc->reference.class) { + case ACPI_REFCLASS_INDEX: + + acpi_os_printf("0x%X\n", source_desc->reference.value); + break; + + case ACPI_REFCLASS_TABLE: + + /* Case for ddb_handle */ + + acpi_os_printf("Table Index 0x%X\n", + source_desc->reference.value); + return_VOID; + + default: + + break; + } + + acpi_os_printf(" "); + + /* Check for valid node first, then valid object */ + + if (source_desc->reference.node) { + if (ACPI_GET_DESCRIPTOR_TYPE + (source_desc->reference.node) != + ACPI_DESC_TYPE_NAMED) { + acpi_os_printf + (" %p - Not a valid namespace node\n", + source_desc->reference.node); + } else { + acpi_os_printf("Node %p [%4.4s] ", + source_desc->reference.node, + (source_desc->reference.node)-> + name.ascii); + + switch ((source_desc->reference.node)->type) { + + /* These types have no attached object */ + + case ACPI_TYPE_DEVICE: + acpi_os_printf("Device\n"); + break; + + case ACPI_TYPE_THERMAL: + acpi_os_printf("Thermal Zone\n"); + break; + + default: + + acpi_ex_do_debug_object((source_desc-> + reference. + node)->object, + level + 4, 0); + break; + } + } + } else if (source_desc->reference.object) { + if (ACPI_GET_DESCRIPTOR_TYPE + (source_desc->reference.object) == + ACPI_DESC_TYPE_NAMED) { + acpi_ex_do_debug_object(((struct + acpi_namespace_node *) + source_desc->reference. + object)->object, + level + 4, 0); + } else { + acpi_ex_do_debug_object(source_desc->reference. + object, level + 4, 0); + } + } + break; + + default: + + acpi_os_printf("%p\n", source_desc); + break; + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "\n")); + return_VOID; +} +#endif diff --git a/kernel/drivers/acpi/acpica/exdump.c b/kernel/drivers/acpi/acpica/exdump.c new file mode 100644 index 000000000..1da52bef6 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exdump.c @@ -0,0 +1,1209 @@ +/****************************************************************************** + * + * Module Name: exdump - Interpreter debug output routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exdump") + +/* + * The following routines are used for debug output only + */ +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +/* Local prototypes */ +static void acpi_ex_out_string(char *title, char *value); + +static void acpi_ex_out_pointer(char *title, void *value); + +static void +acpi_ex_dump_object(union acpi_operand_object *obj_desc, + struct acpi_exdump_info *info); + +static void acpi_ex_dump_reference_obj(union acpi_operand_object *obj_desc); + +static void +acpi_ex_dump_package_obj(union acpi_operand_object *obj_desc, + u32 level, u32 index); + +/******************************************************************************* + * + * Object Descriptor info tables + * + * Note: The first table entry must be an INIT opcode and must contain + * the table length (number of table entries) + * + ******************************************************************************/ + +static struct acpi_exdump_info acpi_ex_dump_integer[2] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_integer), NULL}, + {ACPI_EXD_UINT64, ACPI_EXD_OFFSET(integer.value), "Value"} +}; + +static struct acpi_exdump_info acpi_ex_dump_string[4] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_string), NULL}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(string.length), "Length"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(string.pointer), "Pointer"}, + {ACPI_EXD_STRING, 0, NULL} +}; + +static struct acpi_exdump_info acpi_ex_dump_buffer[5] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_buffer), NULL}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(buffer.length), "Length"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(buffer.pointer), "Pointer"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(buffer.node), "Parent Node"}, + {ACPI_EXD_BUFFER, 0, NULL} +}; + +static struct acpi_exdump_info acpi_ex_dump_package[6] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_package), NULL}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(package.node), "Parent Node"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(package.flags), "Flags"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(package.count), "Elements"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(package.elements), "Element List"}, + {ACPI_EXD_PACKAGE, 0, NULL} +}; + +static struct acpi_exdump_info acpi_ex_dump_device[4] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_device), NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[0]), + "System Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[1]), + "Device Notify"}, + {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(device.handler), "Handler"} +}; + +static struct acpi_exdump_info acpi_ex_dump_event[2] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_event), NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(event.os_semaphore), "OsSemaphore"} +}; + +static struct acpi_exdump_info acpi_ex_dump_method[9] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_method), NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.info_flags), "Info Flags"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.param_count), + "Parameter Count"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.sync_level), "Sync Level"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(method.mutex), "Mutex"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.owner_id), "Owner Id"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.thread_count), "Thread Count"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(method.aml_length), "Aml Length"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(method.aml_start), "Aml Start"} +}; + +static struct acpi_exdump_info acpi_ex_dump_mutex[6] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_mutex), NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.sync_level), "Sync Level"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.original_sync_level), + "Original Sync Level"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.owner_thread), "Owner Thread"}, + {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(mutex.acquisition_depth), + "Acquire Depth"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.os_mutex), "OsMutex"} +}; + +static struct acpi_exdump_info acpi_ex_dump_region[8] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_region), NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(region.space_id), "Space Id"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(region.flags), "Flags"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(region.node), "Parent Node"}, + {ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET(region.address), "Address"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(region.length), "Length"}, + {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(region.handler), "Handler"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(region.next), "Next"} +}; + +static struct acpi_exdump_info acpi_ex_dump_power[6] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_power), NULL}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(power_resource.system_level), + "System Level"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(power_resource.resource_order), + "Resource Order"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[0]), + "System Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[1]), + "Device Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.handler), "Handler"} +}; + +static struct acpi_exdump_info acpi_ex_dump_processor[7] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_processor), NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.proc_id), "Processor ID"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(processor.length), "Length"}, + {ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET(processor.address), "Address"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[0]), + "System Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.notify_list[1]), + "Device Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(processor.handler), "Handler"} +}; + +static struct acpi_exdump_info acpi_ex_dump_thermal[4] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_thermal), NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[0]), + "System Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.notify_list[1]), + "Device Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(thermal_zone.handler), "Handler"} +}; + +static struct acpi_exdump_info acpi_ex_dump_buffer_field[3] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_buffer_field), NULL}, + {ACPI_EXD_FIELD, 0, NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(buffer_field.buffer_obj), + "Buffer Object"} +}; + +static struct acpi_exdump_info acpi_ex_dump_region_field[5] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_region_field), NULL}, + {ACPI_EXD_FIELD, 0, NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(field.access_length), "AccessLength"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(field.region_obj), "Region Object"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(field.resource_buffer), + "ResourceBuffer"} +}; + +static struct acpi_exdump_info acpi_ex_dump_bank_field[5] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_bank_field), NULL}, + {ACPI_EXD_FIELD, 0, NULL}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(bank_field.value), "Value"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(bank_field.region_obj), + "Region Object"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(bank_field.bank_obj), "Bank Object"} +}; + +static struct acpi_exdump_info acpi_ex_dump_index_field[5] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_bank_field), NULL}, + {ACPI_EXD_FIELD, 0, NULL}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(index_field.value), "Value"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(index_field.index_obj), + "Index Object"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(index_field.data_obj), "Data Object"} +}; + +static struct acpi_exdump_info acpi_ex_dump_reference[8] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_reference), NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(reference.class), "Class"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(reference.target_type), "Target Type"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(reference.value), "Value"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.object), "Object Desc"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(reference.node), "Node"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.where), "Where"}, + {ACPI_EXD_REFERENCE, 0, NULL} +}; + +static struct acpi_exdump_info acpi_ex_dump_address_handler[6] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_address_handler), + NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(address_space.space_id), "Space Id"}, + {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(address_space.next), "Next"}, + {ACPI_EXD_RGN_LIST, ACPI_EXD_OFFSET(address_space.region_list), + "Region List"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(address_space.node), "Node"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.context), "Context"} +}; + +static struct acpi_exdump_info acpi_ex_dump_notify[7] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_notify), NULL}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(notify.node), "Node"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(notify.handler_type), "Handler Type"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.handler), "Handler"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.context), "Context"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[0]), + "Next System Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[1]), "Next Device Notify"} +}; + +static struct acpi_exdump_info acpi_ex_dump_extra[6] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_extra), NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.method_REG), "_REG Method"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(extra.scope_node), "Scope Node"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.region_context), + "Region Context"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.aml_start), "Aml Start"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(extra.aml_length), "Aml Length"} +}; + +static struct acpi_exdump_info acpi_ex_dump_data[3] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_data), NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(data.handler), "Handler"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(data.pointer), "Raw Data"} +}; + +/* Miscellaneous tables */ + +static struct acpi_exdump_info acpi_ex_dump_common[5] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_common), NULL}, + {ACPI_EXD_TYPE, 0, NULL}, + {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(common.reference_count), + "Reference Count"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common.flags), "Flags"}, + {ACPI_EXD_LIST, ACPI_EXD_OFFSET(common.next_object), "Object List"} +}; + +static struct acpi_exdump_info acpi_ex_dump_field_common[7] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_field_common), NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common_field.field_flags), + "Field Flags"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common_field.access_byte_width), + "Access Byte Width"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(common_field.bit_length), + "Bit Length"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common_field.start_field_bit_offset), + "Field Bit Offset"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(common_field.base_byte_offset), + "Base Byte Offset"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(common_field.node), "Parent Node"} +}; + +static struct acpi_exdump_info acpi_ex_dump_node[7] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_node), NULL}, + {ACPI_EXD_UINT8, ACPI_EXD_NSOFFSET(flags), "Flags"}, + {ACPI_EXD_UINT8, ACPI_EXD_NSOFFSET(owner_id), "Owner Id"}, + {ACPI_EXD_LIST, ACPI_EXD_NSOFFSET(object), "Object List"}, + {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(parent), "Parent"}, + {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(child), "Child"}, + {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(peer), "Peer"} +}; + +/* Dispatch table, indexed by object type */ + +static struct acpi_exdump_info *acpi_ex_dump_info[] = { + NULL, + acpi_ex_dump_integer, + acpi_ex_dump_string, + acpi_ex_dump_buffer, + acpi_ex_dump_package, + NULL, + acpi_ex_dump_device, + acpi_ex_dump_event, + acpi_ex_dump_method, + acpi_ex_dump_mutex, + acpi_ex_dump_region, + acpi_ex_dump_power, + acpi_ex_dump_processor, + acpi_ex_dump_thermal, + acpi_ex_dump_buffer_field, + NULL, + NULL, + acpi_ex_dump_region_field, + acpi_ex_dump_bank_field, + acpi_ex_dump_index_field, + acpi_ex_dump_reference, + NULL, + NULL, + acpi_ex_dump_notify, + acpi_ex_dump_address_handler, + NULL, + NULL, + NULL, + acpi_ex_dump_extra, + acpi_ex_dump_data +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ex_dump_object + * + * PARAMETERS: obj_desc - Descriptor to dump + * info - Info table corresponding to this object + * type + * + * RETURN: None + * + * DESCRIPTION: Walk the info table for this object + * + ******************************************************************************/ + +static void +acpi_ex_dump_object(union acpi_operand_object *obj_desc, + struct acpi_exdump_info *info) +{ + u8 *target; + char *name; + const char *reference_name; + u8 count; + union acpi_operand_object *start; + union acpi_operand_object *data = NULL; + union acpi_operand_object *next; + struct acpi_namespace_node *node; + + if (!info) { + acpi_os_printf + ("ExDumpObject: Display not implemented for object type %s\n", + acpi_ut_get_object_type_name(obj_desc)); + return; + } + + /* First table entry must contain the table length (# of table entries) */ + + count = info->offset; + + while (count) { + target = ACPI_ADD_PTR(u8, obj_desc, info->offset); + name = info->name; + + switch (info->opcode) { + case ACPI_EXD_INIT: + + break; + + case ACPI_EXD_TYPE: + + acpi_os_printf("%20s : %2.2X [%s]\n", "Type", + obj_desc->common.type, + acpi_ut_get_object_type_name(obj_desc)); + break; + + case ACPI_EXD_UINT8: + + acpi_os_printf("%20s : %2.2X\n", name, *target); + break; + + case ACPI_EXD_UINT16: + + acpi_os_printf("%20s : %4.4X\n", name, + ACPI_GET16(target)); + break; + + case ACPI_EXD_UINT32: + + acpi_os_printf("%20s : %8.8X\n", name, + ACPI_GET32(target)); + break; + + case ACPI_EXD_UINT64: + + acpi_os_printf("%20s : %8.8X%8.8X\n", "Value", + ACPI_FORMAT_UINT64(ACPI_GET64(target))); + break; + + case ACPI_EXD_POINTER: + case ACPI_EXD_ADDRESS: + + acpi_ex_out_pointer(name, + *ACPI_CAST_PTR(void *, target)); + break; + + case ACPI_EXD_STRING: + + acpi_ut_print_string(obj_desc->string.pointer, + ACPI_UINT8_MAX); + acpi_os_printf("\n"); + break; + + case ACPI_EXD_BUFFER: + + ACPI_DUMP_BUFFER(obj_desc->buffer.pointer, + obj_desc->buffer.length); + break; + + case ACPI_EXD_PACKAGE: + + /* Dump the package contents */ + + acpi_os_printf("\nPackage Contents:\n"); + acpi_ex_dump_package_obj(obj_desc, 0, 0); + break; + + case ACPI_EXD_FIELD: + + acpi_ex_dump_object(obj_desc, + acpi_ex_dump_field_common); + break; + + case ACPI_EXD_REFERENCE: + + reference_name = acpi_ut_get_reference_name(obj_desc); + acpi_ex_out_string("Class Name", + ACPI_CAST_PTR(char, reference_name)); + acpi_ex_dump_reference_obj(obj_desc); + break; + + case ACPI_EXD_LIST: + + start = *ACPI_CAST_PTR(void *, target); + next = start; + + acpi_os_printf("%20s : %p", name, next); + if (next) { + acpi_os_printf("(%s %2.2X)", + acpi_ut_get_object_type_name + (next), next->common.type); + + while (next->common.next_object) { + if ((next->common.type == + ACPI_TYPE_LOCAL_DATA) && !data) { + data = next; + } + + next = next->common.next_object; + acpi_os_printf("->%p(%s %2.2X)", next, + acpi_ut_get_object_type_name + (next), + next->common.type); + + if ((next == start) || (next == data)) { + acpi_os_printf + ("\n**** Error: Object list appears to be circular linked"); + break; + } + } + } + + acpi_os_printf("\n"); + break; + + case ACPI_EXD_HDLR_LIST: + + start = *ACPI_CAST_PTR(void *, target); + next = start; + + acpi_os_printf("%20s : %p", name, next); + if (next) { + acpi_os_printf("(%s %2.2X)", + acpi_ut_get_object_type_name + (next), next->common.type); + + while (next->address_space.next) { + if ((next->common.type == + ACPI_TYPE_LOCAL_DATA) && !data) { + data = next; + } + + next = next->address_space.next; + acpi_os_printf("->%p(%s %2.2X)", next, + acpi_ut_get_object_type_name + (next), + next->common.type); + + if ((next == start) || (next == data)) { + acpi_os_printf + ("\n**** Error: Handler list appears to be circular linked"); + break; + } + } + } + + acpi_os_printf("\n"); + break; + + case ACPI_EXD_RGN_LIST: + + start = *ACPI_CAST_PTR(void *, target); + next = start; + + acpi_os_printf("%20s : %p", name, next); + if (next) { + acpi_os_printf("(%s %2.2X)", + acpi_ut_get_object_type_name + (next), next->common.type); + + while (next->region.next) { + if ((next->common.type == + ACPI_TYPE_LOCAL_DATA) && !data) { + data = next; + } + + next = next->region.next; + acpi_os_printf("->%p(%s %2.2X)", next, + acpi_ut_get_object_type_name + (next), + next->common.type); + + if ((next == start) || (next == data)) { + acpi_os_printf + ("\n**** Error: Region list appears to be circular linked"); + break; + } + } + } + + acpi_os_printf("\n"); + break; + + case ACPI_EXD_NODE: + + node = + *ACPI_CAST_PTR(struct acpi_namespace_node *, + target); + + acpi_os_printf("%20s : %p", name, node); + if (node) { + acpi_os_printf(" [%4.4s]", node->name.ascii); + } + acpi_os_printf("\n"); + break; + + default: + + acpi_os_printf("**** Invalid table opcode [%X] ****\n", + info->opcode); + return; + } + + info++; + count--; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_dump_operand + * + * PARAMETERS: *obj_desc - Pointer to entry to be dumped + * depth - Current nesting depth + * + * RETURN: None + * + * DESCRIPTION: Dump an operand object + * + ******************************************************************************/ + +void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth) +{ + u32 length; + u32 index; + + ACPI_FUNCTION_NAME(ex_dump_operand) + + /* Check if debug output enabled */ + if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_EXEC, _COMPONENT)) { + return; + } + + if (!obj_desc) { + + /* This could be a null element of a package */ + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Null Object Descriptor\n")); + return; + } + + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_NAMED) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%p Namespace Node: ", + obj_desc)); + ACPI_DUMP_ENTRY(obj_desc, ACPI_LV_EXEC); + return; + } + + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "%p is not a node or operand object: [%s]\n", + obj_desc, + acpi_ut_get_descriptor_name(obj_desc))); + ACPI_DUMP_BUFFER(obj_desc, sizeof(union acpi_operand_object)); + return; + } + + /* obj_desc is a valid object */ + + if (depth > 0) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%*s[%u] %p ", + depth, " ", depth, obj_desc)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%p ", obj_desc)); + } + + /* Decode object type */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("Reference: [%s] ", + acpi_ut_get_reference_name(obj_desc)); + + switch (obj_desc->reference.class) { + case ACPI_REFCLASS_DEBUG: + + acpi_os_printf("\n"); + break; + + case ACPI_REFCLASS_INDEX: + + acpi_os_printf("%p\n", obj_desc->reference.object); + break; + + case ACPI_REFCLASS_TABLE: + + acpi_os_printf("Table Index %X\n", + obj_desc->reference.value); + break; + + case ACPI_REFCLASS_REFOF: + + acpi_os_printf("%p [%s]\n", obj_desc->reference.object, + acpi_ut_get_type_name(((union + acpi_operand_object + *) + obj_desc-> + reference. + object)->common. + type)); + break; + + case ACPI_REFCLASS_NAME: + + acpi_os_printf("- [%4.4s]\n", + obj_desc->reference.node->name.ascii); + break; + + case ACPI_REFCLASS_ARG: + case ACPI_REFCLASS_LOCAL: + + acpi_os_printf("%X\n", obj_desc->reference.value); + break; + + default: /* Unknown reference class */ + + acpi_os_printf("%2.2X\n", obj_desc->reference.class); + break; + } + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("Buffer length %.2X @ %p\n", + obj_desc->buffer.length, + obj_desc->buffer.pointer); + + /* Debug only -- dump the buffer contents */ + + if (obj_desc->buffer.pointer) { + length = obj_desc->buffer.length; + if (length > 128) { + length = 128; + } + + acpi_os_printf + ("Buffer Contents: (displaying length 0x%.2X)\n", + length); + ACPI_DUMP_BUFFER(obj_desc->buffer.pointer, length); + } + break; + + case ACPI_TYPE_INTEGER: + + acpi_os_printf("Integer %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(obj_desc->integer.value)); + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("Package [Len %X] ElementArray %p\n", + obj_desc->package.count, + obj_desc->package.elements); + + /* + * If elements exist, package element pointer is valid, + * and debug_level exceeds 1, dump package's elements. + */ + if (obj_desc->package.count && + obj_desc->package.elements && acpi_dbg_level > 1) { + for (index = 0; index < obj_desc->package.count; + index++) { + acpi_ex_dump_operand(obj_desc->package. + elements[index], + depth + 1); + } + } + break; + + case ACPI_TYPE_REGION: + + acpi_os_printf("Region %s (%X)", + acpi_ut_get_region_name(obj_desc->region. + space_id), + obj_desc->region.space_id); + + /* + * If the address and length have not been evaluated, + * don't print them. + */ + if (!(obj_desc->region.flags & AOPOBJ_DATA_VALID)) { + acpi_os_printf("\n"); + } else { + acpi_os_printf(" base %8.8X%8.8X Length %X\n", + ACPI_FORMAT_UINT64(obj_desc->region. + address), + obj_desc->region.length); + } + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("String length %X @ %p ", + obj_desc->string.length, + obj_desc->string.pointer); + + acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX); + acpi_os_printf("\n"); + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + acpi_os_printf("BankField\n"); + break; + + case ACPI_TYPE_LOCAL_REGION_FIELD: + + acpi_os_printf + ("RegionField: Bits=%X AccWidth=%X Lock=%X Update=%X at " + "byte=%X bit=%X of below:\n", obj_desc->field.bit_length, + obj_desc->field.access_byte_width, + obj_desc->field.field_flags & AML_FIELD_LOCK_RULE_MASK, + obj_desc->field.field_flags & AML_FIELD_UPDATE_RULE_MASK, + obj_desc->field.base_byte_offset, + obj_desc->field.start_field_bit_offset); + + acpi_ex_dump_operand(obj_desc->field.region_obj, depth + 1); + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + acpi_os_printf("IndexField\n"); + break; + + case ACPI_TYPE_BUFFER_FIELD: + + acpi_os_printf("BufferField: %X bits at byte %X bit %X of\n", + obj_desc->buffer_field.bit_length, + obj_desc->buffer_field.base_byte_offset, + obj_desc->buffer_field.start_field_bit_offset); + + if (!obj_desc->buffer_field.buffer_obj) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "*NULL*\n")); + } else if ((obj_desc->buffer_field.buffer_obj)->common.type != + ACPI_TYPE_BUFFER) { + acpi_os_printf("*not a Buffer*\n"); + } else { + acpi_ex_dump_operand(obj_desc->buffer_field.buffer_obj, + depth + 1); + } + break; + + case ACPI_TYPE_EVENT: + + acpi_os_printf("Event\n"); + break; + + case ACPI_TYPE_METHOD: + + acpi_os_printf("Method(%X) @ %p:%X\n", + obj_desc->method.param_count, + obj_desc->method.aml_start, + obj_desc->method.aml_length); + break; + + case ACPI_TYPE_MUTEX: + + acpi_os_printf("Mutex\n"); + break; + + case ACPI_TYPE_DEVICE: + + acpi_os_printf("Device\n"); + break; + + case ACPI_TYPE_POWER: + + acpi_os_printf("Power\n"); + break; + + case ACPI_TYPE_PROCESSOR: + + acpi_os_printf("Processor\n"); + break; + + case ACPI_TYPE_THERMAL: + + acpi_os_printf("Thermal\n"); + break; + + default: + + /* Unknown Type */ + + acpi_os_printf("Unknown Type %X\n", obj_desc->common.type); + break; + } + + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_dump_operands + * + * PARAMETERS: operands - A list of Operand objects + * opcode_name - AML opcode name + * num_operands - Operand count for this opcode + * + * DESCRIPTION: Dump the operands associated with the opcode + * + ******************************************************************************/ + +void +acpi_ex_dump_operands(union acpi_operand_object **operands, + const char *opcode_name, u32 num_operands) +{ + ACPI_FUNCTION_NAME(ex_dump_operands); + + if (!opcode_name) { + opcode_name = "UNKNOWN"; + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "**** Start operand dump for opcode [%s], %u operands\n", + opcode_name, num_operands)); + + if (num_operands == 0) { + num_operands = 1; + } + + /* Dump the individual operands */ + + while (num_operands) { + acpi_ex_dump_operand(*operands, 0); + operands++; + num_operands--; + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "**** End operand dump for [%s]\n", opcode_name)); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_out* functions + * + * PARAMETERS: title - Descriptive text + * value - Value to be displayed + * + * DESCRIPTION: Object dump output formatting functions. These functions + * reduce the number of format strings required and keeps them + * all in one place for easy modification. + * + ******************************************************************************/ + +static void acpi_ex_out_string(char *title, char *value) +{ + acpi_os_printf("%20s : %s\n", title, value); +} + +static void acpi_ex_out_pointer(char *title, void *value) +{ + acpi_os_printf("%20s : %p\n", title, value); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_dump_namespace_node + * + * PARAMETERS: node - Descriptor to dump + * flags - Force display if TRUE + * + * DESCRIPTION: Dumps the members of the given.Node + * + ******************************************************************************/ + +void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags) +{ + + ACPI_FUNCTION_ENTRY(); + + if (!flags) { + + /* Check if debug output enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_OBJECTS, _COMPONENT)) { + return; + } + } + + acpi_os_printf("%20s : %4.4s\n", "Name", acpi_ut_get_node_name(node)); + acpi_os_printf("%20s : %2.2X [%s]\n", "Type", + node->type, acpi_ut_get_type_name(node->type)); + + acpi_ex_dump_object(ACPI_CAST_PTR(union acpi_operand_object, node), + acpi_ex_dump_node); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_dump_reference_obj + * + * PARAMETERS: object - Descriptor to dump + * + * DESCRIPTION: Dumps a reference object + * + ******************************************************************************/ + +static void acpi_ex_dump_reference_obj(union acpi_operand_object *obj_desc) +{ + struct acpi_buffer ret_buf; + acpi_status status; + + ret_buf.length = ACPI_ALLOCATE_LOCAL_BUFFER; + + if (obj_desc->reference.class == ACPI_REFCLASS_NAME) { + acpi_os_printf(" %p ", obj_desc->reference.node); + + status = + acpi_ns_handle_to_pathname(obj_desc->reference.node, + &ret_buf); + if (ACPI_FAILURE(status)) { + acpi_os_printf(" Could not convert name to pathname\n"); + } else { + acpi_os_printf("%s\n", (char *)ret_buf.pointer); + ACPI_FREE(ret_buf.pointer); + } + } else if (obj_desc->reference.object) { + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == + ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf(" Target: %p", + obj_desc->reference.object); + if (obj_desc->reference.class == ACPI_REFCLASS_TABLE) { + acpi_os_printf(" Table Index: %X\n", + obj_desc->reference.value); + } else { + acpi_os_printf(" Target: %p [%s]\n", + obj_desc->reference.object, + acpi_ut_get_type_name(((union + acpi_operand_object + *) + obj_desc-> + reference. + object)-> + common. + type)); + } + } else { + acpi_os_printf(" Target: %p\n", + obj_desc->reference.object); + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_dump_package_obj + * + * PARAMETERS: obj_desc - Descriptor to dump + * level - Indentation Level + * index - Package index for this object + * + * DESCRIPTION: Dumps the elements of the package + * + ******************************************************************************/ + +static void +acpi_ex_dump_package_obj(union acpi_operand_object *obj_desc, + u32 level, u32 index) +{ + u32 i; + + /* Indentation and index output */ + + if (level > 0) { + for (i = 0; i < level; i++) { + acpi_os_printf(" "); + } + + acpi_os_printf("[%.2d] ", index); + } + + acpi_os_printf("%p ", obj_desc); + + /* Null package elements are allowed */ + + if (!obj_desc) { + acpi_os_printf("[Null Object]\n"); + return; + } + + /* Packages may only contain a few object types */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_INTEGER: + + acpi_os_printf("[Integer] = %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(obj_desc->integer.value)); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("[String] Value: "); + acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX); + acpi_os_printf("\n"); + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("[Buffer] Length %.2X = ", + obj_desc->buffer.length); + if (obj_desc->buffer.length) { + acpi_ut_debug_dump_buffer(ACPI_CAST_PTR + (u8, + obj_desc->buffer.pointer), + obj_desc->buffer.length, + DB_DWORD_DISPLAY, _COMPONENT); + } else { + acpi_os_printf("\n"); + } + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("[Package] Contains %u Elements:\n", + obj_desc->package.count); + + for (i = 0; i < obj_desc->package.count; i++) { + acpi_ex_dump_package_obj(obj_desc->package.elements[i], + level + 1, i); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[Object Reference] Type [%s] %2.2X", + acpi_ut_get_reference_name(obj_desc), + obj_desc->reference.class); + acpi_ex_dump_reference_obj(obj_desc); + break; + + default: + + acpi_os_printf("[Unknown Type] %X\n", obj_desc->common.type); + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_dump_object_descriptor + * + * PARAMETERS: obj_desc - Descriptor to dump + * flags - Force display if TRUE + * + * DESCRIPTION: Dumps the members of the object descriptor given. + * + ******************************************************************************/ + +void +acpi_ex_dump_object_descriptor(union acpi_operand_object *obj_desc, u32 flags) +{ + ACPI_FUNCTION_TRACE(ex_dump_object_descriptor); + + if (!obj_desc) { + return_VOID; + } + + if (!flags) { + + /* Check if debug output enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_OBJECTS, _COMPONENT)) { + return_VOID; + } + } + + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_NAMED) { + acpi_ex_dump_namespace_node((struct acpi_namespace_node *) + obj_desc, flags); + + acpi_os_printf("\nAttached Object (%p):\n", + ((struct acpi_namespace_node *)obj_desc)-> + object); + + obj_desc = ((struct acpi_namespace_node *)obj_desc)->object; + goto dump_object; + } + + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf("%p is not an ACPI operand object: [%s]\n", + obj_desc, acpi_ut_get_descriptor_name(obj_desc)); + return_VOID; + } + + /* Validate the object type */ + + if (obj_desc->common.type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf("Not a known object type: %2.2X\n", + obj_desc->common.type); + return_VOID; + } + +dump_object: + + /* Common Fields */ + + acpi_ex_dump_object(obj_desc, acpi_ex_dump_common); + + /* Object-specific fields */ + + acpi_ex_dump_object(obj_desc, acpi_ex_dump_info[obj_desc->common.type]); + + if (obj_desc->common.type == ACPI_TYPE_REGION) { + obj_desc = obj_desc->common.next_object; + if (obj_desc->common.type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf + ("Secondary object is not a known object type: %2.2X\n", + obj_desc->common.type); + + return_VOID; + } + + acpi_os_printf("\nExtra attached Object (%p):\n", obj_desc); + acpi_ex_dump_object(obj_desc, + acpi_ex_dump_info[obj_desc->common.type]); + } + + return_VOID; +} + +#endif diff --git a/kernel/drivers/acpi/acpica/exfield.c b/kernel/drivers/acpi/acpica/exfield.c new file mode 100644 index 000000000..c161dd974 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exfield.c @@ -0,0 +1,536 @@ +/****************************************************************************** + * + * Module Name: exfield - ACPI AML (p-code) execution - field manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acinterp.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exfield") + +/* Local prototypes */ +static u32 +acpi_ex_get_serial_access_length(u32 accessor_type, u32 access_length); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_get_serial_access_length + * + * PARAMETERS: accessor_type - The type of the protocol indicated by region + * field access attributes + * access_length - The access length of the region field + * + * RETURN: Decoded access length + * + * DESCRIPTION: This routine returns the length of the generic_serial_bus + * protocol bytes + * + ******************************************************************************/ + +static u32 +acpi_ex_get_serial_access_length(u32 accessor_type, u32 access_length) +{ + u32 length; + + switch (accessor_type) { + case AML_FIELD_ATTRIB_QUICK: + + length = 0; + break; + + case AML_FIELD_ATTRIB_SEND_RCV: + case AML_FIELD_ATTRIB_BYTE: + + length = 1; + break; + + case AML_FIELD_ATTRIB_WORD: + case AML_FIELD_ATTRIB_WORD_CALL: + + length = 2; + break; + + case AML_FIELD_ATTRIB_MULTIBYTE: + case AML_FIELD_ATTRIB_RAW_BYTES: + case AML_FIELD_ATTRIB_RAW_PROCESS: + + length = access_length; + break; + + case AML_FIELD_ATTRIB_BLOCK: + case AML_FIELD_ATTRIB_BLOCK_CALL: + default: + + length = ACPI_GSBUS_BUFFER_SIZE - 2; + break; + } + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_read_data_from_field + * + * PARAMETERS: walk_state - Current execution state + * obj_desc - The named field + * ret_buffer_desc - Where the return data object is stored + * + * RETURN: Status + * + * DESCRIPTION: Read from a named field. Returns either an Integer or a + * Buffer, depending on the size of the field. + * + ******************************************************************************/ + +acpi_status +acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state, + union acpi_operand_object *obj_desc, + union acpi_operand_object **ret_buffer_desc) +{ + acpi_status status; + union acpi_operand_object *buffer_desc; + acpi_size length; + void *buffer; + u32 function; + u16 accessor_type; + + ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc); + + /* Parameter validation */ + + if (!obj_desc) { + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + if (!ret_buffer_desc) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) { + /* + * If the buffer_field arguments have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_buffer_field_arguments(obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && + (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_SMBUS + || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS + || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_IPMI)) { + /* + * This is an SMBus, GSBus or IPMI read. We must create a buffer to hold + * the data and then directly access the region handler. + * + * Note: SMBus and GSBus protocol value is passed in upper 16-bits of Function + */ + if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_SMBUS) { + length = ACPI_SMBUS_BUFFER_SIZE; + function = + ACPI_READ | (obj_desc->field.attribute << 16); + } else if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS) { + accessor_type = obj_desc->field.attribute; + length = acpi_ex_get_serial_access_length(accessor_type, + obj_desc-> + field. + access_length); + + /* + * Add additional 2 bytes for the generic_serial_bus data buffer: + * + * Status; (Byte 0 of the data buffer) + * Length; (Byte 1 of the data buffer) + * Data[x-1]; (Bytes 2-x of the arbitrary length data buffer) + */ + length += 2; + function = ACPI_READ | (accessor_type << 16); + } else { /* IPMI */ + + length = ACPI_IPMI_BUFFER_SIZE; + function = ACPI_READ; + } + + buffer_desc = acpi_ut_create_buffer_object(length); + if (!buffer_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Lock entire transaction if requested */ + + acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); + + /* Call the region handler for the read */ + + status = acpi_ex_access_region(obj_desc, 0, + ACPI_CAST_PTR(u64, + buffer_desc-> + buffer.pointer), + function); + acpi_ex_release_global_lock(obj_desc->common_field.field_flags); + goto exit; + } + + /* + * Allocate a buffer for the contents of the field. + * + * If the field is larger than the current integer width, create + * a BUFFER to hold it. Otherwise, use an INTEGER. This allows + * the use of arithmetic operators on the returned value if the + * field size is equal or smaller than an Integer. + * + * Note: Field.length is in bits. + */ + length = + (acpi_size) ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length); + if (length > acpi_gbl_integer_byte_width) { + + /* Field is too large for an Integer, create a Buffer instead */ + + buffer_desc = acpi_ut_create_buffer_object(length); + if (!buffer_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + buffer = buffer_desc->buffer.pointer; + } else { + /* Field will fit within an Integer (normal case) */ + + buffer_desc = acpi_ut_create_integer_object((u64) 0); + if (!buffer_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + length = acpi_gbl_integer_byte_width; + buffer = &buffer_desc->integer.value; + } + + if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && + (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GPIO)) { + /* + * For GPIO (general_purpose_io), the Address will be the bit offset + * from the previous Connection() operator, making it effectively a + * pin number index. The bit_length is the length of the field, which + * is thus the number of pins. + */ + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "GPIO FieldRead [FROM]: Pin %u Bits %u\n", + obj_desc->field.pin_number_index, + obj_desc->field.bit_length)); + + /* Lock entire transaction if requested */ + + acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); + + /* Perform the write */ + + status = acpi_ex_access_region(obj_desc, 0, + (u64 *)buffer, ACPI_READ); + acpi_ex_release_global_lock(obj_desc->common_field.field_flags); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(buffer_desc); + } else { + *ret_buffer_desc = buffer_desc; + } + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "FieldRead [TO]: Obj %p, Type %X, Buf %p, ByteLen %X\n", + obj_desc, obj_desc->common.type, buffer, + (u32) length)); + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n", + obj_desc->common_field.bit_length, + obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.base_byte_offset)); + + /* Lock entire transaction if requested */ + + acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); + + /* Read from the field */ + + status = acpi_ex_extract_from_field(obj_desc, buffer, (u32) length); + acpi_ex_release_global_lock(obj_desc->common_field.field_flags); + +exit: + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(buffer_desc); + } else { + *ret_buffer_desc = buffer_desc; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_write_data_to_field + * + * PARAMETERS: source_desc - Contains data to write + * obj_desc - The named field + * result_desc - Where the return value is returned, if any + * + * RETURN: Status + * + * DESCRIPTION: Write to a named field + * + ******************************************************************************/ + +acpi_status +acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, + union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc) +{ + acpi_status status; + u32 length; + void *buffer; + union acpi_operand_object *buffer_desc; + u32 function; + u16 accessor_type; + + ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc); + + /* Parameter validation */ + + if (!source_desc || !obj_desc) { + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) { + /* + * If the buffer_field arguments have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_buffer_field_arguments(obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && + (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_SMBUS + || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS + || obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_IPMI)) { + /* + * This is an SMBus, GSBus or IPMI write. We will bypass the entire field + * mechanism and handoff the buffer directly to the handler. For + * these address spaces, the buffer is bi-directional; on a write, + * return data is returned in the same buffer. + * + * Source must be a buffer of sufficient size: + * ACPI_SMBUS_BUFFER_SIZE, ACPI_GSBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. + * + * Note: SMBus and GSBus protocol type is passed in upper 16-bits of Function + */ + if (source_desc->common.type != ACPI_TYPE_BUFFER) { + ACPI_ERROR((AE_INFO, + "SMBus/IPMI/GenericSerialBus write requires Buffer, found type %s", + acpi_ut_get_object_type_name(source_desc))); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_SMBUS) { + length = ACPI_SMBUS_BUFFER_SIZE; + function = + ACPI_WRITE | (obj_desc->field.attribute << 16); + } else if (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GSBUS) { + accessor_type = obj_desc->field.attribute; + length = acpi_ex_get_serial_access_length(accessor_type, + obj_desc-> + field. + access_length); + + /* + * Add additional 2 bytes for the generic_serial_bus data buffer: + * + * Status; (Byte 0 of the data buffer) + * Length; (Byte 1 of the data buffer) + * Data[x-1]; (Bytes 2-x of the arbitrary length data buffer) + */ + length += 2; + function = ACPI_WRITE | (accessor_type << 16); + } else { /* IPMI */ + + length = ACPI_IPMI_BUFFER_SIZE; + function = ACPI_WRITE; + } + + if (source_desc->buffer.length < length) { + ACPI_ERROR((AE_INFO, + "SMBus/IPMI/GenericSerialBus write requires Buffer of length %u, found length %u", + length, source_desc->buffer.length)); + + return_ACPI_STATUS(AE_AML_BUFFER_LIMIT); + } + + /* Create the bi-directional buffer */ + + buffer_desc = acpi_ut_create_buffer_object(length); + if (!buffer_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + buffer = buffer_desc->buffer.pointer; + ACPI_MEMCPY(buffer, source_desc->buffer.pointer, length); + + /* Lock entire transaction if requested */ + + acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); + + /* + * Perform the write (returns status and perhaps data in the + * same buffer) + */ + status = acpi_ex_access_region(obj_desc, 0, + (u64 *) buffer, function); + acpi_ex_release_global_lock(obj_desc->common_field.field_flags); + + *result_desc = buffer_desc; + return_ACPI_STATUS(status); + } else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) && + (obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_GPIO)) { + /* + * For GPIO (general_purpose_io), we will bypass the entire field + * mechanism and handoff the bit address and bit width directly to + * the handler. The Address will be the bit offset + * from the previous Connection() operator, making it effectively a + * pin number index. The bit_length is the length of the field, which + * is thus the number of pins. + */ + if (source_desc->common.type != ACPI_TYPE_INTEGER) { + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "GPIO FieldWrite [FROM]: (%s:%X), Val %.8X [TO]: Pin %u Bits %u\n", + acpi_ut_get_type_name(source_desc->common. + type), + source_desc->common.type, + (u32)source_desc->integer.value, + obj_desc->field.pin_number_index, + obj_desc->field.bit_length)); + + buffer = &source_desc->integer.value; + + /* Lock entire transaction if requested */ + + acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); + + /* Perform the write */ + + status = acpi_ex_access_region(obj_desc, 0, + (u64 *)buffer, ACPI_WRITE); + acpi_ex_release_global_lock(obj_desc->common_field.field_flags); + return_ACPI_STATUS(status); + } + + /* Get a pointer to the data to be written */ + + switch (source_desc->common.type) { + case ACPI_TYPE_INTEGER: + + buffer = &source_desc->integer.value; + length = sizeof(source_desc->integer.value); + break; + + case ACPI_TYPE_BUFFER: + + buffer = source_desc->buffer.pointer; + length = source_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + + buffer = source_desc->string.pointer; + length = source_desc->string.length; + break; + + default: + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n", + source_desc, + acpi_ut_get_type_name(source_desc->common.type), + source_desc->common.type, buffer, length)); + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "FieldWrite [TO]: Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n", + obj_desc, + acpi_ut_get_type_name(obj_desc->common.type), + obj_desc->common.type, + obj_desc->common_field.bit_length, + obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.base_byte_offset)); + + /* Lock entire transaction if requested */ + + acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); + + /* Write to the field */ + + status = acpi_ex_insert_into_field(obj_desc, buffer, length); + acpi_ex_release_global_lock(obj_desc->common_field.field_flags); + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exfldio.c b/kernel/drivers/acpi/acpica/exfldio.c new file mode 100644 index 000000000..725a3746a --- /dev/null +++ b/kernel/drivers/acpi/acpica/exfldio.c @@ -0,0 +1,1004 @@ +/****************************************************************************** + * + * Module Name: exfldio - Aml Field I/O + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acevents.h" +#include "acdispat.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exfldio") + +/* Local prototypes */ +static acpi_status +acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset, u64 *value, u32 read_write); + +static u8 +acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value); + +static acpi_status +acpi_ex_setup_region(union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_setup_region + * + * PARAMETERS: obj_desc - Field to be read or written + * field_datum_byte_offset - Byte offset of this datum within the + * parent field + * + * RETURN: Status + * + * DESCRIPTION: Common processing for acpi_ex_extract_from_field and + * acpi_ex_insert_into_field. Initialize the Region if necessary and + * validate the request. + * + ******************************************************************************/ + +static acpi_status +acpi_ex_setup_region(union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset) +{ + acpi_status status = AE_OK; + union acpi_operand_object *rgn_desc; + u8 space_id; + + ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset); + + rgn_desc = obj_desc->common_field.region_obj; + + /* We must have a valid region */ + + if (rgn_desc->common.type != ACPI_TYPE_REGION) { + ACPI_ERROR((AE_INFO, "Needed Region, found type 0x%X (%s)", + rgn_desc->common.type, + acpi_ut_get_object_type_name(rgn_desc))); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + space_id = rgn_desc->region.space_id; + + /* Validate the Space ID */ + + if (!acpi_is_valid_space_id(space_id)) { + ACPI_ERROR((AE_INFO, + "Invalid/unknown Address Space ID: 0x%2.2X", + space_id)); + return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); + } + + /* + * If the Region Address and Length have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_region_arguments(rgn_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* + * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear + * address space and the request cannot be directly validated + */ + if (space_id == ACPI_ADR_SPACE_SMBUS || + space_id == ACPI_ADR_SPACE_GSBUS || + space_id == ACPI_ADR_SPACE_IPMI) { + + /* SMBus or IPMI has a non-linear address space */ + + return_ACPI_STATUS(AE_OK); + } +#ifdef ACPI_UNDER_DEVELOPMENT + /* + * If the Field access is any_acc, we can now compute the optimal + * access (because we know know the length of the parent region) + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } +#endif + + /* + * Validate the request. The entire request from the byte offset for a + * length of one field datum (access width) must fit within the region. + * (Region length is specified in bytes) + */ + if (rgn_desc->region.length < + (obj_desc->common_field.base_byte_offset + field_datum_byte_offset + + obj_desc->common_field.access_byte_width)) { + if (acpi_gbl_enable_interpreter_slack) { + /* + * Slack mode only: We will go ahead and allow access to this + * field if it is within the region length rounded up to the next + * access width boundary. acpi_size cast for 64-bit compile. + */ + if (ACPI_ROUND_UP(rgn_desc->region.length, + obj_desc->common_field. + access_byte_width) >= + ((acpi_size) obj_desc->common_field. + base_byte_offset + + obj_desc->common_field.access_byte_width + + field_datum_byte_offset)) { + return_ACPI_STATUS(AE_OK); + } + } + + if (rgn_desc->region.length < + obj_desc->common_field.access_byte_width) { + /* + * This is the case where the access_type (acc_word, etc.) is wider + * than the region itself. For example, a region of length one + * byte, and a field with Dword access specified. + */ + ACPI_ERROR((AE_INFO, + "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)", + acpi_ut_get_node_name(obj_desc-> + common_field.node), + obj_desc->common_field.access_byte_width, + acpi_ut_get_node_name(rgn_desc->region. + node), + rgn_desc->region.length)); + } + + /* + * Offset rounded up to next multiple of field width + * exceeds region length, indicate an error + */ + ACPI_ERROR((AE_INFO, + "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)", + acpi_ut_get_node_name(obj_desc->common_field.node), + obj_desc->common_field.base_byte_offset, + field_datum_byte_offset, + obj_desc->common_field.access_byte_width, + acpi_ut_get_node_name(rgn_desc->region.node), + rgn_desc->region.length)); + + return_ACPI_STATUS(AE_AML_REGION_LIMIT); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_access_region + * + * PARAMETERS: obj_desc - Field to be read + * field_datum_byte_offset - Byte offset of this datum within the + * parent field + * value - Where to store value (must at least + * 64 bits) + * function - Read or Write flag plus other region- + * dependent flags + * + * RETURN: Status + * + * DESCRIPTION: Read or Write a single field datum to an Operation Region. + * + ******************************************************************************/ + +acpi_status +acpi_ex_access_region(union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset, u64 *value, u32 function) +{ + acpi_status status; + union acpi_operand_object *rgn_desc; + u32 region_offset; + + ACPI_FUNCTION_TRACE(ex_access_region); + + /* + * Ensure that the region operands are fully evaluated and verify + * the validity of the request + */ + status = acpi_ex_setup_region(obj_desc, field_datum_byte_offset); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * The physical address of this field datum is: + * + * 1) The base of the region, plus + * 2) The base offset of the field, plus + * 3) The current offset into the field + */ + rgn_desc = obj_desc->common_field.region_obj; + region_offset = + obj_desc->common_field.base_byte_offset + field_datum_byte_offset; + + if ((function & ACPI_IO_MASK) == ACPI_READ) { + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[READ]")); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[WRITE]")); + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD, + " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n", + acpi_ut_get_region_name(rgn_desc->region. + space_id), + rgn_desc->region.space_id, + obj_desc->common_field.access_byte_width, + obj_desc->common_field.base_byte_offset, + field_datum_byte_offset, + ACPI_FORMAT_UINT64(rgn_desc->region.address + + region_offset))); + + /* Invoke the appropriate address_space/op_region handler */ + + status = acpi_ev_address_space_dispatch(rgn_desc, obj_desc, + function, region_offset, + ACPI_MUL_8(obj_desc-> + common_field. + access_byte_width), + value); + + if (ACPI_FAILURE(status)) { + if (status == AE_NOT_IMPLEMENTED) { + ACPI_ERROR((AE_INFO, + "Region %s (ID=%u) not implemented", + acpi_ut_get_region_name(rgn_desc->region. + space_id), + rgn_desc->region.space_id)); + } else if (status == AE_NOT_EXIST) { + ACPI_ERROR((AE_INFO, + "Region %s (ID=%u) has no handler", + acpi_ut_get_region_name(rgn_desc->region. + space_id), + rgn_desc->region.space_id)); + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_register_overflow + * + * PARAMETERS: obj_desc - Register(Field) to be written + * value - Value to be stored + * + * RETURN: TRUE if value overflows the field, FALSE otherwise + * + * DESCRIPTION: Check if a value is out of range of the field being written. + * Used to check if the values written to Index and Bank registers + * are out of range. Normally, the value is simply truncated + * to fit the field, but this case is most likely a serious + * coding error in the ASL. + * + ******************************************************************************/ + +static u8 +acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value) +{ + + if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) { + /* + * The field is large enough to hold the maximum integer, so we can + * never overflow it. + */ + return (FALSE); + } + + if (value >= ((u64) 1 << obj_desc->common_field.bit_length)) { + /* + * The Value is larger than the maximum value that can fit into + * the register. + */ + ACPI_ERROR((AE_INFO, + "Index value 0x%8.8X%8.8X overflows field width 0x%X", + ACPI_FORMAT_UINT64(value), + obj_desc->common_field.bit_length)); + + return (TRUE); + } + + /* The Value will fit into the field with no truncation */ + + return (FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_field_datum_io + * + * PARAMETERS: obj_desc - Field to be read + * field_datum_byte_offset - Byte offset of this datum within the + * parent field + * value - Where to store value (must be 64 bits) + * read_write - Read or Write flag + * + * RETURN: Status + * + * DESCRIPTION: Read or Write a single datum of a field. The field_type is + * demultiplexed here to handle the different types of fields + * (buffer_field, region_field, index_field, bank_field) + * + ******************************************************************************/ + +static acpi_status +acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset, u64 *value, u32 read_write) +{ + acpi_status status; + u64 local_value; + + ACPI_FUNCTION_TRACE_U32(ex_field_datum_io, field_datum_byte_offset); + + if (read_write == ACPI_READ) { + if (!value) { + local_value = 0; + + /* To support reads without saving return value */ + value = &local_value; + } + + /* Clear the entire return buffer first, [Very Important!] */ + + *value = 0; + } + + /* + * The four types of fields are: + * + * buffer_field - Read/write from/to a Buffer + * region_field - Read/write from/to a Operation Region. + * bank_field - Write to a Bank Register, then read/write from/to an + * operation_region + * index_field - Write to an Index Register, then read/write from/to a + * Data Register + */ + switch (obj_desc->common.type) { + case ACPI_TYPE_BUFFER_FIELD: + /* + * If the buffer_field arguments have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_buffer_field_arguments(obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + if (read_write == ACPI_READ) { + /* + * Copy the data from the source buffer. + * Length is the field width in bytes. + */ + ACPI_MEMCPY(value, + (obj_desc->buffer_field.buffer_obj)->buffer. + pointer + + obj_desc->buffer_field.base_byte_offset + + field_datum_byte_offset, + obj_desc->common_field.access_byte_width); + } else { + /* + * Copy the data to the target buffer. + * Length is the field width in bytes. + */ + ACPI_MEMCPY((obj_desc->buffer_field.buffer_obj)->buffer. + pointer + + obj_desc->buffer_field.base_byte_offset + + field_datum_byte_offset, value, + obj_desc->common_field.access_byte_width); + } + + status = AE_OK; + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + /* + * Ensure that the bank_value is not beyond the capacity of + * the register + */ + if (acpi_ex_register_overflow(obj_desc->bank_field.bank_obj, + (u64) obj_desc->bank_field. + value)) { + return_ACPI_STATUS(AE_AML_REGISTER_LIMIT); + } + + /* + * For bank_fields, we must write the bank_value to the bank_register + * (itself a region_field) before we can access the data. + */ + status = + acpi_ex_insert_into_field(obj_desc->bank_field.bank_obj, + &obj_desc->bank_field.value, + sizeof(obj_desc->bank_field. + value)); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Now that the Bank has been selected, fall through to the + * region_field case and write the datum to the Operation Region + */ + + /*lint -fallthrough */ + + case ACPI_TYPE_LOCAL_REGION_FIELD: + /* + * For simple region_fields, we just directly access the owning + * Operation Region. + */ + status = + acpi_ex_access_region(obj_desc, field_datum_byte_offset, + value, read_write); + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + /* + * Ensure that the index_value is not beyond the capacity of + * the register + */ + if (acpi_ex_register_overflow(obj_desc->index_field.index_obj, + (u64) obj_desc->index_field. + value)) { + return_ACPI_STATUS(AE_AML_REGISTER_LIMIT); + } + + /* Write the index value to the index_register (itself a region_field) */ + + field_datum_byte_offset += obj_desc->index_field.value; + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Write to Index Register: Value %8.8X\n", + field_datum_byte_offset)); + + status = + acpi_ex_insert_into_field(obj_desc->index_field.index_obj, + &field_datum_byte_offset, + sizeof(field_datum_byte_offset)); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (read_write == ACPI_READ) { + + /* Read the datum from the data_register */ + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Read from Data Register\n")); + + status = + acpi_ex_extract_from_field(obj_desc->index_field. + data_obj, value, + sizeof(u64)); + } else { + /* Write the datum to the data_register */ + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Write to Data Register: Value %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(*value))); + + status = + acpi_ex_insert_into_field(obj_desc->index_field. + data_obj, value, + sizeof(u64)); + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %u", + obj_desc->common.type)); + status = AE_AML_INTERNAL; + break; + } + + if (ACPI_SUCCESS(status)) { + if (read_write == ACPI_READ) { + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Value Read %8.8X%8.8X, Width %u\n", + ACPI_FORMAT_UINT64(*value), + obj_desc->common_field. + access_byte_width)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Value Written %8.8X%8.8X, Width %u\n", + ACPI_FORMAT_UINT64(*value), + obj_desc->common_field. + access_byte_width)); + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_write_with_update_rule + * + * PARAMETERS: obj_desc - Field to be written + * mask - bitmask within field datum + * field_value - Value to write + * field_datum_byte_offset - Offset of datum within field + * + * RETURN: Status + * + * DESCRIPTION: Apply the field update rule to a field write + * + ******************************************************************************/ + +acpi_status +acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc, + u64 mask, + u64 field_value, u32 field_datum_byte_offset) +{ + acpi_status status = AE_OK; + u64 merged_value; + u64 current_value; + + ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask); + + /* Start with the new bits */ + + merged_value = field_value; + + /* If the mask is all ones, we don't need to worry about the update rule */ + + if (mask != ACPI_UINT64_MAX) { + + /* Decode the update rule */ + + switch (obj_desc->common_field. + field_flags & AML_FIELD_UPDATE_RULE_MASK) { + case AML_FIELD_UPDATE_PRESERVE: + /* + * Check if update rule needs to be applied (not if mask is all + * ones) The left shift drops the bits we want to ignore. + */ + if ((~mask << (ACPI_MUL_8(sizeof(mask)) - + ACPI_MUL_8(obj_desc->common_field. + access_byte_width))) != 0) { + /* + * Read the current contents of the byte/word/dword containing + * the field, and merge with the new field value. + */ + status = + acpi_ex_field_datum_io(obj_desc, + field_datum_byte_offset, + ¤t_value, + ACPI_READ); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + merged_value |= (current_value & ~mask); + } + break; + + case AML_FIELD_UPDATE_WRITE_AS_ONES: + + /* Set positions outside the field to all ones */ + + merged_value |= ~mask; + break; + + case AML_FIELD_UPDATE_WRITE_AS_ZEROS: + + /* Set positions outside the field to all zeros */ + + merged_value &= mask; + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown UpdateRule value: 0x%X", + (obj_desc->common_field. + field_flags & + AML_FIELD_UPDATE_RULE_MASK))); + return_ACPI_STATUS(AE_AML_OPERAND_VALUE); + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(mask), + field_datum_byte_offset, + obj_desc->common_field.access_byte_width, + ACPI_FORMAT_UINT64(field_value), + ACPI_FORMAT_UINT64(merged_value))); + + /* Write the merged value */ + + status = acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset, + &merged_value, ACPI_WRITE); + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_extract_from_field + * + * PARAMETERS: obj_desc - Field to be read + * buffer - Where to store the field data + * buffer_length - Length of Buffer + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the current value of the given field + * + ******************************************************************************/ + +acpi_status +acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, + void *buffer, u32 buffer_length) +{ + acpi_status status; + u64 raw_datum; + u64 merged_datum; + u32 field_offset = 0; + u32 buffer_offset = 0; + u32 buffer_tail_bits; + u32 datum_count; + u32 field_datum_count; + u32 access_bit_width; + u32 i; + + ACPI_FUNCTION_TRACE(ex_extract_from_field); + + /* Validate target buffer and clear it */ + + if (buffer_length < + ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) { + ACPI_ERROR((AE_INFO, + "Field size %u (bits) is too large for buffer (%u)", + obj_desc->common_field.bit_length, buffer_length)); + + return_ACPI_STATUS(AE_BUFFER_OVERFLOW); + } + + ACPI_MEMSET(buffer, 0, buffer_length); + access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width); + + /* Handle the simple case here */ + + if ((obj_desc->common_field.start_field_bit_offset == 0) && + (obj_desc->common_field.bit_length == access_bit_width)) { + if (buffer_length >= sizeof(u64)) { + status = + acpi_ex_field_datum_io(obj_desc, 0, buffer, + ACPI_READ); + } else { + /* Use raw_datum (u64) to handle buffers < 64 bits */ + + status = + acpi_ex_field_datum_io(obj_desc, 0, &raw_datum, + ACPI_READ); + ACPI_MEMCPY(buffer, &raw_datum, buffer_length); + } + + return_ACPI_STATUS(status); + } + +/* TBD: Move to common setup code */ + + /* Field algorithm is limited to sizeof(u64), truncate if needed */ + + if (obj_desc->common_field.access_byte_width > sizeof(u64)) { + obj_desc->common_field.access_byte_width = sizeof(u64); + access_bit_width = sizeof(u64) * 8; + } + + /* Compute the number of datums (access width data items) */ + + datum_count = + ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, + access_bit_width); + + field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length + + obj_desc->common_field. + start_field_bit_offset, + access_bit_width); + + /* Priming read from the field */ + + status = + acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum, + ACPI_READ); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + merged_datum = + raw_datum >> obj_desc->common_field.start_field_bit_offset; + + /* Read the rest of the field */ + + for (i = 1; i < field_datum_count; i++) { + + /* Get next input datum from the field */ + + field_offset += obj_desc->common_field.access_byte_width; + status = acpi_ex_field_datum_io(obj_desc, field_offset, + &raw_datum, ACPI_READ); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Merge with previous datum if necessary. + * + * Note: Before the shift, check if the shift value will be larger than + * the integer size. If so, there is no need to perform the operation. + * This avoids the differences in behavior between different compilers + * concerning shift values larger than the target data width. + */ + if (access_bit_width - + obj_desc->common_field.start_field_bit_offset < + ACPI_INTEGER_BIT_SIZE) { + merged_datum |= + raw_datum << (access_bit_width - + obj_desc->common_field. + start_field_bit_offset); + } + + if (i == datum_count) { + break; + } + + /* Write merged datum to target buffer */ + + ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + + buffer_offset += obj_desc->common_field.access_byte_width; + merged_datum = + raw_datum >> obj_desc->common_field.start_field_bit_offset; + } + + /* Mask off any extra bits in the last datum */ + + buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width; + if (buffer_tail_bits) { + merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits); + } + + /* Write the last datum to the buffer */ + + ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_insert_into_field + * + * PARAMETERS: obj_desc - Field to be written + * buffer - Data to be written + * buffer_length - Length of Buffer + * + * RETURN: Status + * + * DESCRIPTION: Store the Buffer contents into the given field + * + ******************************************************************************/ + +acpi_status +acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, + void *buffer, u32 buffer_length) +{ + void *new_buffer; + acpi_status status; + u64 mask; + u64 width_mask; + u64 merged_datum; + u64 raw_datum = 0; + u32 field_offset = 0; + u32 buffer_offset = 0; + u32 buffer_tail_bits; + u32 datum_count; + u32 field_datum_count; + u32 access_bit_width; + u32 required_length; + u32 i; + + ACPI_FUNCTION_TRACE(ex_insert_into_field); + + /* Validate input buffer */ + + new_buffer = NULL; + required_length = + ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length); + /* + * We must have a buffer that is at least as long as the field + * we are writing to. This is because individual fields are + * indivisible and partial writes are not supported -- as per + * the ACPI specification. + */ + if (buffer_length < required_length) { + + /* We need to create a new buffer */ + + new_buffer = ACPI_ALLOCATE_ZEROED(required_length); + if (!new_buffer) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* + * Copy the original data to the new buffer, starting + * at Byte zero. All unused (upper) bytes of the + * buffer will be 0. + */ + ACPI_MEMCPY((char *)new_buffer, (char *)buffer, buffer_length); + buffer = new_buffer; + buffer_length = required_length; + } + +/* TBD: Move to common setup code */ + + /* Algo is limited to sizeof(u64), so cut the access_byte_width */ + if (obj_desc->common_field.access_byte_width > sizeof(u64)) { + obj_desc->common_field.access_byte_width = sizeof(u64); + } + + access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width); + + /* + * Create the bitmasks used for bit insertion. + * Note: This if/else is used to bypass compiler differences with the + * shift operator + */ + if (access_bit_width == ACPI_INTEGER_BIT_SIZE) { + width_mask = ACPI_UINT64_MAX; + } else { + width_mask = ACPI_MASK_BITS_ABOVE(access_bit_width); + } + + mask = width_mask & + ACPI_MASK_BITS_BELOW(obj_desc->common_field.start_field_bit_offset); + + /* Compute the number of datums (access width data items) */ + + datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length, + access_bit_width); + + field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length + + obj_desc->common_field. + start_field_bit_offset, + access_bit_width); + + /* Get initial Datum from the input buffer */ + + ACPI_MEMCPY(&raw_datum, buffer, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + + merged_datum = + raw_datum << obj_desc->common_field.start_field_bit_offset; + + /* Write the entire field */ + + for (i = 1; i < field_datum_count; i++) { + + /* Write merged datum to the target field */ + + merged_datum &= mask; + status = acpi_ex_write_with_update_rule(obj_desc, mask, + merged_datum, + field_offset); + if (ACPI_FAILURE(status)) { + goto exit; + } + + field_offset += obj_desc->common_field.access_byte_width; + + /* + * Start new output datum by merging with previous input datum + * if necessary. + * + * Note: Before the shift, check if the shift value will be larger than + * the integer size. If so, there is no need to perform the operation. + * This avoids the differences in behavior between different compilers + * concerning shift values larger than the target data width. + */ + if ((access_bit_width - + obj_desc->common_field.start_field_bit_offset) < + ACPI_INTEGER_BIT_SIZE) { + merged_datum = + raw_datum >> (access_bit_width - + obj_desc->common_field. + start_field_bit_offset); + } else { + merged_datum = 0; + } + + mask = width_mask; + + if (i == datum_count) { + break; + } + + /* Get the next input datum from the buffer */ + + buffer_offset += obj_desc->common_field.access_byte_width; + ACPI_MEMCPY(&raw_datum, ((char *)buffer) + buffer_offset, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + + merged_datum |= + raw_datum << obj_desc->common_field.start_field_bit_offset; + } + + /* Mask off any extra bits in the last datum */ + + buffer_tail_bits = (obj_desc->common_field.bit_length + + obj_desc->common_field.start_field_bit_offset) % + access_bit_width; + if (buffer_tail_bits) { + mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits); + } + + /* Write the last datum to the field */ + + merged_datum &= mask; + status = acpi_ex_write_with_update_rule(obj_desc, + mask, merged_datum, + field_offset); + +exit: + /* Free temporary buffer if we used one */ + + if (new_buffer) { + ACPI_FREE(new_buffer); + } + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exmisc.c b/kernel/drivers/acpi/acpica/exmisc.c new file mode 100644 index 000000000..b56fc9d6f --- /dev/null +++ b/kernel/drivers/acpi/acpica/exmisc.c @@ -0,0 +1,733 @@ +/****************************************************************************** + * + * Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "amlresrc.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exmisc") + +/******************************************************************************* + * + * FUNCTION: acpi_ex_get_object_reference + * + * PARAMETERS: obj_desc - Create a reference to this object + * return_desc - Where to store the reference + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Obtain and return a "reference" to the target object + * Common code for the ref_of_op and the cond_ref_of_op. + * + ******************************************************************************/ +acpi_status +acpi_ex_get_object_reference(union acpi_operand_object *obj_desc, + union acpi_operand_object **return_desc, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *reference_obj; + union acpi_operand_object *referenced_obj; + + ACPI_FUNCTION_TRACE_PTR(ex_get_object_reference, obj_desc); + + *return_desc = NULL; + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { + case ACPI_DESC_TYPE_OPERAND: + + if (obj_desc->common.type != ACPI_TYPE_LOCAL_REFERENCE) { + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* + * Must be a reference to a Local or Arg + */ + switch (obj_desc->reference.class) { + case ACPI_REFCLASS_LOCAL: + case ACPI_REFCLASS_ARG: + case ACPI_REFCLASS_DEBUG: + + /* The referenced object is the pseudo-node for the local/arg */ + + referenced_obj = obj_desc->reference.object; + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown Reference Class 0x%2.2X", + obj_desc->reference.class)); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + break; + + case ACPI_DESC_TYPE_NAMED: + /* + * A named reference that has already been resolved to a Node + */ + referenced_obj = obj_desc; + break; + + default: + + ACPI_ERROR((AE_INFO, "Invalid descriptor type 0x%X", + ACPI_GET_DESCRIPTOR_TYPE(obj_desc))); + return_ACPI_STATUS(AE_TYPE); + } + + /* Create a new reference object */ + + reference_obj = + acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE); + if (!reference_obj) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + reference_obj->reference.class = ACPI_REFCLASS_REFOF; + reference_obj->reference.object = referenced_obj; + *return_desc = reference_obj; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Object %p Type [%s], returning Reference %p\n", + obj_desc, acpi_ut_get_object_type_name(obj_desc), + *return_desc)); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_concat_template + * + * PARAMETERS: operand0 - First source object + * operand1 - Second source object + * actual_return_desc - Where to place the return object + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Concatenate two resource templates + * + ******************************************************************************/ + +acpi_status +acpi_ex_concat_template(union acpi_operand_object *operand0, + union acpi_operand_object *operand1, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object *return_desc; + u8 *new_buf; + u8 *end_tag; + acpi_size length0; + acpi_size length1; + acpi_size new_length; + + ACPI_FUNCTION_TRACE(ex_concat_template); + + /* + * Find the end_tag descriptor in each resource template. + * Note1: returned pointers point TO the end_tag, not past it. + * Note2: zero-length buffers are allowed; treated like one end_tag + */ + + /* Get the length of the first resource template */ + + status = acpi_ut_get_resource_end_tag(operand0, &end_tag); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + length0 = ACPI_PTR_DIFF(end_tag, operand0->buffer.pointer); + + /* Get the length of the second resource template */ + + status = acpi_ut_get_resource_end_tag(operand1, &end_tag); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + length1 = ACPI_PTR_DIFF(end_tag, operand1->buffer.pointer); + + /* Combine both lengths, minimum size will be 2 for end_tag */ + + new_length = length0 + length1 + sizeof(struct aml_resource_end_tag); + + /* Create a new buffer object for the result (with one end_tag) */ + + return_desc = acpi_ut_create_buffer_object(new_length); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* + * Copy the templates to the new buffer, 0 first, then 1 follows. One + * end_tag descriptor is copied from Operand1. + */ + new_buf = return_desc->buffer.pointer; + ACPI_MEMCPY(new_buf, operand0->buffer.pointer, length0); + ACPI_MEMCPY(new_buf + length0, operand1->buffer.pointer, length1); + + /* Insert end_tag and set the checksum to zero, means "ignore checksum" */ + + new_buf[new_length - 1] = 0; + new_buf[new_length - 2] = ACPI_RESOURCE_NAME_END_TAG | 1; + + /* Return the completed resource template */ + + *actual_return_desc = return_desc; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_concatenate + * + * PARAMETERS: operand0 - First source object + * operand1 - Second source object + * actual_return_desc - Where to place the return object + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Concatenate two objects OF THE SAME TYPE. + * + ******************************************************************************/ + +acpi_status +acpi_ex_do_concatenate(union acpi_operand_object *operand0, + union acpi_operand_object *operand1, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *local_operand1 = operand1; + union acpi_operand_object *return_desc; + char *new_buf; + acpi_status status; + + ACPI_FUNCTION_TRACE(ex_do_concatenate); + + /* + * Convert the second operand if necessary. The first operand + * determines the type of the second operand, (See the Data Types + * section of the ACPI specification.) Both object types are + * guaranteed to be either Integer/String/Buffer by the operand + * resolution mechanism. + */ + switch (operand0->common.type) { + case ACPI_TYPE_INTEGER: + + status = + acpi_ex_convert_to_integer(operand1, &local_operand1, 16); + break; + + case ACPI_TYPE_STRING: + + status = acpi_ex_convert_to_string(operand1, &local_operand1, + ACPI_IMPLICIT_CONVERT_HEX); + break; + + case ACPI_TYPE_BUFFER: + + status = acpi_ex_convert_to_buffer(operand1, &local_operand1); + break; + + default: + + ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", + operand0->common.type)); + status = AE_AML_INTERNAL; + } + + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * Both operands are now known to be the same object type + * (Both are Integer, String, or Buffer), and we can now perform the + * concatenation. + */ + + /* + * There are three cases to handle: + * + * 1) Two Integers concatenated to produce a new Buffer + * 2) Two Strings concatenated to produce a new String + * 3) Two Buffers concatenated to produce a new Buffer + */ + switch (operand0->common.type) { + case ACPI_TYPE_INTEGER: + + /* Result of two Integers is a Buffer */ + /* Need enough buffer space for two integers */ + + return_desc = acpi_ut_create_buffer_object((acpi_size) + ACPI_MUL_2 + (acpi_gbl_integer_byte_width)); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + new_buf = (char *)return_desc->buffer.pointer; + + /* Copy the first integer, LSB first */ + + ACPI_MEMCPY(new_buf, &operand0->integer.value, + acpi_gbl_integer_byte_width); + + /* Copy the second integer (LSB first) after the first */ + + ACPI_MEMCPY(new_buf + acpi_gbl_integer_byte_width, + &local_operand1->integer.value, + acpi_gbl_integer_byte_width); + break; + + case ACPI_TYPE_STRING: + + /* Result of two Strings is a String */ + + return_desc = acpi_ut_create_string_object(((acpi_size) + operand0->string. + length + + local_operand1-> + string.length)); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + new_buf = return_desc->string.pointer; + + /* Concatenate the strings */ + + ACPI_STRCPY(new_buf, operand0->string.pointer); + ACPI_STRCPY(new_buf + operand0->string.length, + local_operand1->string.pointer); + break; + + case ACPI_TYPE_BUFFER: + + /* Result of two Buffers is a Buffer */ + + return_desc = acpi_ut_create_buffer_object(((acpi_size) + operand0->buffer. + length + + local_operand1-> + buffer.length)); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + new_buf = (char *)return_desc->buffer.pointer; + + /* Concatenate the buffers */ + + ACPI_MEMCPY(new_buf, operand0->buffer.pointer, + operand0->buffer.length); + ACPI_MEMCPY(new_buf + operand0->buffer.length, + local_operand1->buffer.pointer, + local_operand1->buffer.length); + break; + + default: + + /* Invalid object type, should not happen here */ + + ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", + operand0->common.type)); + status = AE_AML_INTERNAL; + goto cleanup; + } + + *actual_return_desc = return_desc; + +cleanup: + if (local_operand1 != operand1) { + acpi_ut_remove_reference(local_operand1); + } + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_math_op + * + * PARAMETERS: opcode - AML opcode + * integer0 - Integer operand #0 + * integer1 - Integer operand #1 + * + * RETURN: Integer result of the operation + * + * DESCRIPTION: Execute a math AML opcode. The purpose of having all of the + * math functions here is to prevent a lot of pointer dereferencing + * to obtain the operands. + * + ******************************************************************************/ + +u64 acpi_ex_do_math_op(u16 opcode, u64 integer0, u64 integer1) +{ + + ACPI_FUNCTION_ENTRY(); + + switch (opcode) { + case AML_ADD_OP: /* Add (Integer0, Integer1, Result) */ + + return (integer0 + integer1); + + case AML_BIT_AND_OP: /* And (Integer0, Integer1, Result) */ + + return (integer0 & integer1); + + case AML_BIT_NAND_OP: /* NAnd (Integer0, Integer1, Result) */ + + return (~(integer0 & integer1)); + + case AML_BIT_OR_OP: /* Or (Integer0, Integer1, Result) */ + + return (integer0 | integer1); + + case AML_BIT_NOR_OP: /* NOr (Integer0, Integer1, Result) */ + + return (~(integer0 | integer1)); + + case AML_BIT_XOR_OP: /* XOr (Integer0, Integer1, Result) */ + + return (integer0 ^ integer1); + + case AML_MULTIPLY_OP: /* Multiply (Integer0, Integer1, Result) */ + + return (integer0 * integer1); + + case AML_SHIFT_LEFT_OP: /* shift_left (Operand, shift_count, Result) */ + + /* + * We need to check if the shiftcount is larger than the integer bit + * width since the behavior of this is not well-defined in the C language. + */ + if (integer1 >= acpi_gbl_integer_bit_width) { + return (0); + } + return (integer0 << integer1); + + case AML_SHIFT_RIGHT_OP: /* shift_right (Operand, shift_count, Result) */ + + /* + * We need to check if the shiftcount is larger than the integer bit + * width since the behavior of this is not well-defined in the C language. + */ + if (integer1 >= acpi_gbl_integer_bit_width) { + return (0); + } + return (integer0 >> integer1); + + case AML_SUBTRACT_OP: /* Subtract (Integer0, Integer1, Result) */ + + return (integer0 - integer1); + + default: + + return (0); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_logical_numeric_op + * + * PARAMETERS: opcode - AML opcode + * integer0 - Integer operand #0 + * integer1 - Integer operand #1 + * logical_result - TRUE/FALSE result of the operation + * + * RETURN: Status + * + * DESCRIPTION: Execute a logical "Numeric" AML opcode. For these Numeric + * operators (LAnd and LOr), both operands must be integers. + * + * Note: cleanest machine code seems to be produced by the code + * below, rather than using statements of the form: + * Result = (Integer0 && Integer1); + * + ******************************************************************************/ + +acpi_status +acpi_ex_do_logical_numeric_op(u16 opcode, + u64 integer0, u64 integer1, u8 *logical_result) +{ + acpi_status status = AE_OK; + u8 local_result = FALSE; + + ACPI_FUNCTION_TRACE(ex_do_logical_numeric_op); + + switch (opcode) { + case AML_LAND_OP: /* LAnd (Integer0, Integer1) */ + + if (integer0 && integer1) { + local_result = TRUE; + } + break; + + case AML_LOR_OP: /* LOr (Integer0, Integer1) */ + + if (integer0 || integer1) { + local_result = TRUE; + } + break; + + default: + + status = AE_AML_INTERNAL; + break; + } + + /* Return the logical result and status */ + + *logical_result = local_result; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_logical_op + * + * PARAMETERS: opcode - AML opcode + * operand0 - operand #0 + * operand1 - operand #1 + * logical_result - TRUE/FALSE result of the operation + * + * RETURN: Status + * + * DESCRIPTION: Execute a logical AML opcode. The purpose of having all of the + * functions here is to prevent a lot of pointer dereferencing + * to obtain the operands and to simplify the generation of the + * logical value. For the Numeric operators (LAnd and LOr), both + * operands must be integers. For the other logical operators, + * operands can be any combination of Integer/String/Buffer. The + * first operand determines the type to which the second operand + * will be converted. + * + * Note: cleanest machine code seems to be produced by the code + * below, rather than using statements of the form: + * Result = (Operand0 == Operand1); + * + ******************************************************************************/ + +acpi_status +acpi_ex_do_logical_op(u16 opcode, + union acpi_operand_object *operand0, + union acpi_operand_object *operand1, u8 * logical_result) +{ + union acpi_operand_object *local_operand1 = operand1; + u64 integer0; + u64 integer1; + u32 length0; + u32 length1; + acpi_status status = AE_OK; + u8 local_result = FALSE; + int compare; + + ACPI_FUNCTION_TRACE(ex_do_logical_op); + + /* + * Convert the second operand if necessary. The first operand + * determines the type of the second operand, (See the Data Types + * section of the ACPI 3.0+ specification.) Both object types are + * guaranteed to be either Integer/String/Buffer by the operand + * resolution mechanism. + */ + switch (operand0->common.type) { + case ACPI_TYPE_INTEGER: + + status = + acpi_ex_convert_to_integer(operand1, &local_operand1, 16); + break; + + case ACPI_TYPE_STRING: + + status = acpi_ex_convert_to_string(operand1, &local_operand1, + ACPI_IMPLICIT_CONVERT_HEX); + break; + + case ACPI_TYPE_BUFFER: + + status = acpi_ex_convert_to_buffer(operand1, &local_operand1); + break; + + default: + + status = AE_AML_INTERNAL; + break; + } + + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * Two cases: 1) Both Integers, 2) Both Strings or Buffers + */ + if (operand0->common.type == ACPI_TYPE_INTEGER) { + /* + * 1) Both operands are of type integer + * Note: local_operand1 may have changed above + */ + integer0 = operand0->integer.value; + integer1 = local_operand1->integer.value; + + switch (opcode) { + case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ + + if (integer0 == integer1) { + local_result = TRUE; + } + break; + + case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ + + if (integer0 > integer1) { + local_result = TRUE; + } + break; + + case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ + + if (integer0 < integer1) { + local_result = TRUE; + } + break; + + default: + + status = AE_AML_INTERNAL; + break; + } + } else { + /* + * 2) Both operands are Strings or both are Buffers + * Note: Code below takes advantage of common Buffer/String + * object fields. local_operand1 may have changed above. Use + * memcmp to handle nulls in buffers. + */ + length0 = operand0->buffer.length; + length1 = local_operand1->buffer.length; + + /* Lexicographic compare: compare the data bytes */ + + compare = ACPI_MEMCMP(operand0->buffer.pointer, + local_operand1->buffer.pointer, + (length0 > length1) ? length1 : length0); + + switch (opcode) { + case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ + + /* Length and all bytes must be equal */ + + if ((length0 == length1) && (compare == 0)) { + + /* Length and all bytes match ==> TRUE */ + + local_result = TRUE; + } + break; + + case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ + + if (compare > 0) { + local_result = TRUE; + goto cleanup; /* TRUE */ + } + if (compare < 0) { + goto cleanup; /* FALSE */ + } + + /* Bytes match (to shortest length), compare lengths */ + + if (length0 > length1) { + local_result = TRUE; + } + break; + + case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ + + if (compare > 0) { + goto cleanup; /* FALSE */ + } + if (compare < 0) { + local_result = TRUE; + goto cleanup; /* TRUE */ + } + + /* Bytes match (to shortest length), compare lengths */ + + if (length0 < length1) { + local_result = TRUE; + } + break; + + default: + + status = AE_AML_INTERNAL; + break; + } + } + +cleanup: + + /* New object was created if implicit conversion performed - delete */ + + if (local_operand1 != operand1) { + acpi_ut_remove_reference(local_operand1); + } + + /* Return the logical result and status */ + + *logical_result = local_result; + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exmutex.c b/kernel/drivers/acpi/acpica/exmutex.c new file mode 100644 index 000000000..472030f2b --- /dev/null +++ b/kernel/drivers/acpi/acpica/exmutex.c @@ -0,0 +1,502 @@ +/****************************************************************************** + * + * Module Name: exmutex - ASL Mutex Acquire/Release functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "acevents.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exmutex") + +/* Local prototypes */ +static void +acpi_ex_link_mutex(union acpi_operand_object *obj_desc, + struct acpi_thread_state *thread); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_unlink_mutex + * + * PARAMETERS: obj_desc - The mutex to be unlinked + * + * RETURN: None + * + * DESCRIPTION: Remove a mutex from the "AcquiredMutex" list + * + ******************************************************************************/ + +void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc) +{ + struct acpi_thread_state *thread = obj_desc->mutex.owner_thread; + + if (!thread) { + return; + } + + /* Doubly linked list */ + + if (obj_desc->mutex.next) { + (obj_desc->mutex.next)->mutex.prev = obj_desc->mutex.prev; + } + + if (obj_desc->mutex.prev) { + (obj_desc->mutex.prev)->mutex.next = obj_desc->mutex.next; + + /* + * Migrate the previous sync level associated with this mutex to + * the previous mutex on the list so that it may be preserved. + * This handles the case where several mutexes have been acquired + * at the same level, but are not released in opposite order. + */ + (obj_desc->mutex.prev)->mutex.original_sync_level = + obj_desc->mutex.original_sync_level; + } else { + thread->acquired_mutex_list = obj_desc->mutex.next; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_link_mutex + * + * PARAMETERS: obj_desc - The mutex to be linked + * thread - Current executing thread object + * + * RETURN: None + * + * DESCRIPTION: Add a mutex to the "AcquiredMutex" list for this walk + * + ******************************************************************************/ + +static void +acpi_ex_link_mutex(union acpi_operand_object *obj_desc, + struct acpi_thread_state *thread) +{ + union acpi_operand_object *list_head; + + list_head = thread->acquired_mutex_list; + + /* This object will be the first object in the list */ + + obj_desc->mutex.prev = NULL; + obj_desc->mutex.next = list_head; + + /* Update old first object to point back to this object */ + + if (list_head) { + list_head->mutex.prev = obj_desc; + } + + /* Update list head */ + + thread->acquired_mutex_list = obj_desc; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_acquire_mutex_object + * + * PARAMETERS: timeout - Timeout in milliseconds + * obj_desc - Mutex object + * thread_id - Current thread state + * + * RETURN: Status + * + * DESCRIPTION: Acquire an AML mutex, low-level interface. Provides a common + * path that supports multiple acquires by the same thread. + * + * MUTEX: Interpreter must be locked + * + * NOTE: This interface is called from three places: + * 1) From acpi_ex_acquire_mutex, via an AML Acquire() operator + * 2) From acpi_ex_acquire_global_lock when an AML Field access requires the + * global lock + * 3) From the external interface, acpi_acquire_global_lock + * + ******************************************************************************/ + +acpi_status +acpi_ex_acquire_mutex_object(u16 timeout, + union acpi_operand_object *obj_desc, + acpi_thread_id thread_id) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ex_acquire_mutex_object, obj_desc); + + if (!obj_desc) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Support for multiple acquires by the owning thread */ + + if (obj_desc->mutex.thread_id == thread_id) { + /* + * The mutex is already owned by this thread, just increment the + * acquisition depth + */ + obj_desc->mutex.acquisition_depth++; + return_ACPI_STATUS(AE_OK); + } + + /* Acquire the mutex, wait if necessary. Special case for Global Lock */ + + if (obj_desc == acpi_gbl_global_lock_mutex) { + status = acpi_ev_acquire_global_lock(timeout); + } else { + status = acpi_ex_system_wait_mutex(obj_desc->mutex.os_mutex, + timeout); + } + + if (ACPI_FAILURE(status)) { + + /* Includes failure from a timeout on time_desc */ + + return_ACPI_STATUS(status); + } + + /* Acquired the mutex: update mutex object */ + + obj_desc->mutex.thread_id = thread_id; + obj_desc->mutex.acquisition_depth = 1; + obj_desc->mutex.original_sync_level = 0; + obj_desc->mutex.owner_thread = NULL; /* Used only for AML Acquire() */ + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_acquire_mutex + * + * PARAMETERS: time_desc - Timeout integer + * obj_desc - Mutex object + * walk_state - Current method execution state + * + * RETURN: Status + * + * DESCRIPTION: Acquire an AML mutex + * + ******************************************************************************/ + +acpi_status +acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ex_acquire_mutex, obj_desc); + + if (!obj_desc) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Must have a valid thread state struct */ + + if (!walk_state->thread) { + ACPI_ERROR((AE_INFO, + "Cannot acquire Mutex [%4.4s], null thread info", + acpi_ut_get_node_name(obj_desc->mutex.node))); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* + * Current sync level must be less than or equal to the sync level of the + * mutex. This mechanism provides some deadlock prevention + */ + if (walk_state->thread->current_sync_level > obj_desc->mutex.sync_level) { + ACPI_ERROR((AE_INFO, + "Cannot acquire Mutex [%4.4s], current SyncLevel is too large (%u)", + acpi_ut_get_node_name(obj_desc->mutex.node), + walk_state->thread->current_sync_level)); + return_ACPI_STATUS(AE_AML_MUTEX_ORDER); + } + + status = acpi_ex_acquire_mutex_object((u16) time_desc->integer.value, + obj_desc, + walk_state->thread->thread_id); + if (ACPI_SUCCESS(status) && obj_desc->mutex.acquisition_depth == 1) { + + /* Save Thread object, original/current sync levels */ + + obj_desc->mutex.owner_thread = walk_state->thread; + obj_desc->mutex.original_sync_level = + walk_state->thread->current_sync_level; + walk_state->thread->current_sync_level = + obj_desc->mutex.sync_level; + + /* Link the mutex to the current thread for force-unlock at method exit */ + + acpi_ex_link_mutex(obj_desc, walk_state->thread); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_release_mutex_object + * + * PARAMETERS: obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Release a previously acquired Mutex, low level interface. + * Provides a common path that supports multiple releases (after + * previous multiple acquires) by the same thread. + * + * MUTEX: Interpreter must be locked + * + * NOTE: This interface is called from three places: + * 1) From acpi_ex_release_mutex, via an AML Acquire() operator + * 2) From acpi_ex_release_global_lock when an AML Field access requires the + * global lock + * 3) From the external interface, acpi_release_global_lock + * + ******************************************************************************/ + +acpi_status acpi_ex_release_mutex_object(union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_release_mutex_object); + + if (obj_desc->mutex.acquisition_depth == 0) { + return_ACPI_STATUS(AE_NOT_ACQUIRED); + } + + /* Match multiple Acquires with multiple Releases */ + + obj_desc->mutex.acquisition_depth--; + if (obj_desc->mutex.acquisition_depth != 0) { + + /* Just decrement the depth and return */ + + return_ACPI_STATUS(AE_OK); + } + + if (obj_desc->mutex.owner_thread) { + + /* Unlink the mutex from the owner's list */ + + acpi_ex_unlink_mutex(obj_desc); + obj_desc->mutex.owner_thread = NULL; + } + + /* Release the mutex, special case for Global Lock */ + + if (obj_desc == acpi_gbl_global_lock_mutex) { + status = acpi_ev_release_global_lock(); + } else { + acpi_os_release_mutex(obj_desc->mutex.os_mutex); + } + + /* Clear mutex info */ + + obj_desc->mutex.thread_id = 0; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_release_mutex + * + * PARAMETERS: obj_desc - The object descriptor for this op + * walk_state - Current method execution state + * + * RETURN: Status + * + * DESCRIPTION: Release a previously acquired Mutex. + * + ******************************************************************************/ + +acpi_status +acpi_ex_release_mutex(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + u8 previous_sync_level; + struct acpi_thread_state *owner_thread; + + ACPI_FUNCTION_TRACE(ex_release_mutex); + + if (!obj_desc) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + owner_thread = obj_desc->mutex.owner_thread; + + /* The mutex must have been previously acquired in order to release it */ + + if (!owner_thread) { + ACPI_ERROR((AE_INFO, + "Cannot release Mutex [%4.4s], not acquired", + acpi_ut_get_node_name(obj_desc->mutex.node))); + return_ACPI_STATUS(AE_AML_MUTEX_NOT_ACQUIRED); + } + + /* Must have a valid thread ID */ + + if (!walk_state->thread) { + ACPI_ERROR((AE_INFO, + "Cannot release Mutex [%4.4s], null thread info", + acpi_ut_get_node_name(obj_desc->mutex.node))); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* + * The Mutex is owned, but this thread must be the owner. + * Special case for Global Lock, any thread can release + */ + if ((owner_thread->thread_id != walk_state->thread->thread_id) && + (obj_desc != acpi_gbl_global_lock_mutex)) { + ACPI_ERROR((AE_INFO, + "Thread %u cannot release Mutex [%4.4s] acquired by thread %u", + (u32)walk_state->thread->thread_id, + acpi_ut_get_node_name(obj_desc->mutex.node), + (u32)owner_thread->thread_id)); + return_ACPI_STATUS(AE_AML_NOT_OWNER); + } + + /* + * The sync level of the mutex must be equal to the current sync level. In + * other words, the current level means that at least one mutex at that + * level is currently being held. Attempting to release a mutex of a + * different level can only mean that the mutex ordering rule is being + * violated. This behavior is clarified in ACPI 4.0 specification. + */ + if (obj_desc->mutex.sync_level != owner_thread->current_sync_level) { + ACPI_ERROR((AE_INFO, + "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %u current %u", + acpi_ut_get_node_name(obj_desc->mutex.node), + obj_desc->mutex.sync_level, + walk_state->thread->current_sync_level)); + return_ACPI_STATUS(AE_AML_MUTEX_ORDER); + } + + /* + * Get the previous sync_level from the head of the acquired mutex list. + * This handles the case where several mutexes at the same level have been + * acquired, but are not released in reverse order. + */ + previous_sync_level = + owner_thread->acquired_mutex_list->mutex.original_sync_level; + + status = acpi_ex_release_mutex_object(obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (obj_desc->mutex.acquisition_depth == 0) { + + /* Restore the previous sync_level */ + + owner_thread->current_sync_level = previous_sync_level; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_release_all_mutexes + * + * PARAMETERS: thread - Current executing thread object + * + * RETURN: Status + * + * DESCRIPTION: Release all mutexes held by this thread + * + * NOTE: This function is called as the thread is exiting the interpreter. + * Mutexes are not released when an individual control method is exited, but + * only when the parent thread actually exits the interpreter. This allows one + * method to acquire a mutex, and a different method to release it, as long as + * this is performed underneath a single parent control method. + * + ******************************************************************************/ + +void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread) +{ + union acpi_operand_object *next = thread->acquired_mutex_list; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_NAME(ex_release_all_mutexes); + + /* Traverse the list of owned mutexes, releasing each one */ + + while (next) { + obj_desc = next; + next = obj_desc->mutex.next; + + obj_desc->mutex.prev = NULL; + obj_desc->mutex.next = NULL; + obj_desc->mutex.acquisition_depth = 0; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Force-releasing held mutex: %p\n", + obj_desc)); + + /* Release the mutex, special case for Global Lock */ + + if (obj_desc == acpi_gbl_global_lock_mutex) { + + /* Ignore errors */ + + (void)acpi_ev_release_global_lock(); + } else { + acpi_os_release_mutex(obj_desc->mutex.os_mutex); + } + + /* Mark mutex unowned */ + + obj_desc->mutex.owner_thread = NULL; + obj_desc->mutex.thread_id = 0; + + /* Update Thread sync_level (Last mutex is the important one) */ + + thread->current_sync_level = + obj_desc->mutex.original_sync_level; + } +} diff --git a/kernel/drivers/acpi/acpica/exnames.c b/kernel/drivers/acpi/acpica/exnames.c new file mode 100644 index 000000000..453b00c30 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exnames.c @@ -0,0 +1,435 @@ +/****************************************************************************** + * + * Module Name: exnames - interpreter/scanner name load/execute + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exnames") + +/* Local prototypes */ +static char *acpi_ex_allocate_name_string(u32 prefix_count, u32 num_name_segs); + +static acpi_status acpi_ex_name_segment(u8 **in_aml_address, char *name_string); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_allocate_name_string + * + * PARAMETERS: prefix_count - Count of parent levels. Special cases: + * (-1)==root, 0==none + * num_name_segs - count of 4-character name segments + * + * RETURN: A pointer to the allocated string segment. This segment must + * be deleted by the caller. + * + * DESCRIPTION: Allocate a buffer for a name string. Ensure allocated name + * string is long enough, and set up prefix if any. + * + ******************************************************************************/ + +static char *acpi_ex_allocate_name_string(u32 prefix_count, u32 num_name_segs) +{ + char *temp_ptr; + char *name_string; + u32 size_needed; + + ACPI_FUNCTION_TRACE(ex_allocate_name_string); + + /* + * Allow room for all \ and ^ prefixes, all segments and a multi_name_prefix. + * Also, one byte for the null terminator. + * This may actually be somewhat longer than needed. + */ + if (prefix_count == ACPI_UINT32_MAX) { + + /* Special case for root */ + + size_needed = 1 + (ACPI_NAME_SIZE * num_name_segs) + 2 + 1; + } else { + size_needed = + prefix_count + (ACPI_NAME_SIZE * num_name_segs) + 2 + 1; + } + + /* + * Allocate a buffer for the name. + * This buffer must be deleted by the caller! + */ + name_string = ACPI_ALLOCATE(size_needed); + if (!name_string) { + ACPI_ERROR((AE_INFO, + "Could not allocate size %u", size_needed)); + return_PTR(NULL); + } + + temp_ptr = name_string; + + /* Set up Root or Parent prefixes if needed */ + + if (prefix_count == ACPI_UINT32_MAX) { + *temp_ptr++ = AML_ROOT_PREFIX; + } else { + while (prefix_count--) { + *temp_ptr++ = AML_PARENT_PREFIX; + } + } + + /* Set up Dual or Multi prefixes if needed */ + + if (num_name_segs > 2) { + + /* Set up multi prefixes */ + + *temp_ptr++ = AML_MULTI_NAME_PREFIX_OP; + *temp_ptr++ = (char)num_name_segs; + } else if (2 == num_name_segs) { + + /* Set up dual prefixes */ + + *temp_ptr++ = AML_DUAL_NAME_PREFIX; + } + + /* + * Terminate string following prefixes. acpi_ex_name_segment() will + * append the segment(s) + */ + *temp_ptr = 0; + + return_PTR(name_string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_name_segment + * + * PARAMETERS: in_aml_address - Pointer to the name in the AML code + * name_string - Where to return the name. The name is appended + * to any existing string to form a namepath + * + * RETURN: Status + * + * DESCRIPTION: Extract an ACPI name (4 bytes) from the AML byte stream + * + ******************************************************************************/ + +static acpi_status acpi_ex_name_segment(u8 ** in_aml_address, char *name_string) +{ + char *aml_address = (void *)*in_aml_address; + acpi_status status = AE_OK; + u32 index; + char char_buf[5]; + + ACPI_FUNCTION_TRACE(ex_name_segment); + + /* + * If first character is a digit, then we know that we aren't looking at a + * valid name segment + */ + char_buf[0] = *aml_address; + + if ('0' <= char_buf[0] && char_buf[0] <= '9') { + ACPI_ERROR((AE_INFO, "Invalid leading digit: %c", char_buf[0])); + return_ACPI_STATUS(AE_CTRL_PENDING); + } + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "Bytes from stream:\n")); + + for (index = 0; + (index < ACPI_NAME_SIZE) + && (acpi_ut_valid_acpi_char(*aml_address, 0)); index++) { + char_buf[index] = *aml_address++; + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, "%c\n", char_buf[index])); + } + + /* Valid name segment */ + + if (index == 4) { + + /* Found 4 valid characters */ + + char_buf[4] = '\0'; + + if (name_string) { + ACPI_STRCAT(name_string, char_buf); + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Appended to - %s\n", name_string)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "No Name string - %s\n", char_buf)); + } + } else if (index == 0) { + /* + * First character was not a valid name character, + * so we are looking at something other than a name. + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Leading character is not alpha: %02Xh (not a name)\n", + char_buf[0])); + status = AE_CTRL_PENDING; + } else { + /* + * Segment started with one or more valid characters, but fewer than + * the required 4 + */ + status = AE_AML_BAD_NAME; + ACPI_ERROR((AE_INFO, + "Bad character 0x%02x in name, at %p", + *aml_address, aml_address)); + } + + *in_aml_address = ACPI_CAST_PTR(u8, aml_address); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_get_name_string + * + * PARAMETERS: data_type - Object type to be associated with this + * name + * in_aml_address - Pointer to the namestring in the AML code + * out_name_string - Where the namestring is returned + * out_name_length - Length of the returned string + * + * RETURN: Status, namestring and length + * + * DESCRIPTION: Extract a full namepath from the AML byte stream, + * including any prefixes. + * + ******************************************************************************/ + +acpi_status +acpi_ex_get_name_string(acpi_object_type data_type, + u8 * in_aml_address, + char **out_name_string, u32 * out_name_length) +{ + acpi_status status = AE_OK; + u8 *aml_address = in_aml_address; + char *name_string = NULL; + u32 num_segments; + u32 prefix_count = 0; + u8 has_prefix = FALSE; + + ACPI_FUNCTION_TRACE_PTR(ex_get_name_string, aml_address); + + if (ACPI_TYPE_LOCAL_REGION_FIELD == data_type || + ACPI_TYPE_LOCAL_BANK_FIELD == data_type || + ACPI_TYPE_LOCAL_INDEX_FIELD == data_type) { + + /* Disallow prefixes for types associated with field_unit names */ + + name_string = acpi_ex_allocate_name_string(0, 1); + if (!name_string) { + status = AE_NO_MEMORY; + } else { + status = + acpi_ex_name_segment(&aml_address, name_string); + } + } else { + /* + * data_type is not a field name. + * Examine first character of name for root or parent prefix operators + */ + switch (*aml_address) { + case AML_ROOT_PREFIX: + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "RootPrefix(\\) at %p\n", + aml_address)); + + /* + * Remember that we have a root_prefix -- + * see comment in acpi_ex_allocate_name_string() + */ + aml_address++; + prefix_count = ACPI_UINT32_MAX; + has_prefix = TRUE; + break; + + case AML_PARENT_PREFIX: + + /* Increment past possibly multiple parent prefixes */ + + do { + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "ParentPrefix (^) at %p\n", + aml_address)); + + aml_address++; + prefix_count++; + + } while (*aml_address == AML_PARENT_PREFIX); + + has_prefix = TRUE; + break; + + default: + + /* Not a prefix character */ + + break; + } + + /* Examine first character of name for name segment prefix operator */ + + switch (*aml_address) { + case AML_DUAL_NAME_PREFIX: + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "DualNamePrefix at %p\n", + aml_address)); + + aml_address++; + name_string = + acpi_ex_allocate_name_string(prefix_count, 2); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + /* Indicate that we processed a prefix */ + + has_prefix = TRUE; + + status = + acpi_ex_name_segment(&aml_address, name_string); + if (ACPI_SUCCESS(status)) { + status = + acpi_ex_name_segment(&aml_address, + name_string); + } + break; + + case AML_MULTI_NAME_PREFIX_OP: + + ACPI_DEBUG_PRINT((ACPI_DB_LOAD, + "MultiNamePrefix at %p\n", + aml_address)); + + /* Fetch count of segments remaining in name path */ + + aml_address++; + num_segments = *aml_address; + + name_string = + acpi_ex_allocate_name_string(prefix_count, + num_segments); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + /* Indicate that we processed a prefix */ + + aml_address++; + has_prefix = TRUE; + + while (num_segments && + (status = + acpi_ex_name_segment(&aml_address, + name_string)) == AE_OK) { + num_segments--; + } + + break; + + case 0: + + /* null_name valid as of 8-12-98 ASL/AML Grammar Update */ + + if (prefix_count == ACPI_UINT32_MAX) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "NameSeg is \"\\\" followed by NULL\n")); + } + + /* Consume the NULL byte */ + + aml_address++; + name_string = + acpi_ex_allocate_name_string(prefix_count, 0); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + break; + + default: + + /* Name segment string */ + + name_string = + acpi_ex_allocate_name_string(prefix_count, 1); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + status = + acpi_ex_name_segment(&aml_address, name_string); + break; + } + } + + if (AE_CTRL_PENDING == status && has_prefix) { + + /* Ran out of segments after processing a prefix */ + + ACPI_ERROR((AE_INFO, "Malformed Name at %p", name_string)); + status = AE_AML_BAD_NAME; + } + + if (ACPI_FAILURE(status)) { + if (name_string) { + ACPI_FREE(name_string); + } + return_ACPI_STATUS(status); + } + + *out_name_string = name_string; + *out_name_length = (u32) (aml_address - in_aml_address); + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exoparg1.c b/kernel/drivers/acpi/acpica/exoparg1.c new file mode 100644 index 000000000..77930683a --- /dev/null +++ b/kernel/drivers/acpi/acpica/exoparg1.c @@ -0,0 +1,1072 @@ +/****************************************************************************** + * + * Module Name: exoparg1 - AML execution - opcodes with 1 argument + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exoparg1") + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (0 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_0A_0T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute operator with no operands, one return value + * + ******************************************************************************/ +acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *return_desc = NULL; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_0A_0T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_TIMER_OP: /* Timer () */ + + /* Create a return object of type Integer */ + + return_desc = + acpi_ut_create_integer_object(acpi_os_get_timer()); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + +cleanup: + + /* Delete return object on error */ + + if ((ACPI_FAILURE(status)) || walk_state->result_obj) { + acpi_ut_remove_reference(return_desc); + walk_state->result_obj = NULL; + } else { + /* Save the return value */ + + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_0T_0R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 1 monadic operator with numeric operand on + * object stack + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_0T_0R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_RELEASE_OP: /* Release (mutex_object) */ + + status = acpi_ex_release_mutex(operand[0], walk_state); + break; + + case AML_RESET_OP: /* Reset (event_object) */ + + status = acpi_ex_system_reset_event(operand[0]); + break; + + case AML_SIGNAL_OP: /* Signal (event_object) */ + + status = acpi_ex_system_signal_event(operand[0]); + break; + + case AML_SLEEP_OP: /* Sleep (msec_time) */ + + status = acpi_ex_system_do_sleep(operand[0]->integer.value); + break; + + case AML_STALL_OP: /* Stall (usec_time) */ + + status = + acpi_ex_system_do_stall((u32) operand[0]->integer.value); + break; + + case AML_UNLOAD_OP: /* Unload (Handle) */ + + status = acpi_ex_unload_table(operand[0]); + break; + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_1T_0R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, one target, and no + * return value. + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_1T_0R(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object **operand = &walk_state->operands[0]; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_1T_0R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_LOAD_OP: + + status = acpi_ex_load_op(operand[0], operand[1], walk_state); + break; + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + +cleanup: + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_1T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, one target, and a + * return value. + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + union acpi_operand_object *return_desc2 = NULL; + u32 temp32; + u32 i; + u64 power_of_ten; + u64 digit; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_1T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_BIT_NOT_OP: + case AML_FIND_SET_LEFT_BIT_OP: + case AML_FIND_SET_RIGHT_BIT_OP: + case AML_FROM_BCD_OP: + case AML_TO_BCD_OP: + case AML_COND_REF_OF_OP: + + /* Create a return object of type Integer for these opcodes */ + + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + switch (walk_state->opcode) { + case AML_BIT_NOT_OP: /* Not (Operand, Result) */ + + return_desc->integer.value = ~operand[0]->integer.value; + break; + + case AML_FIND_SET_LEFT_BIT_OP: /* find_set_left_bit (Operand, Result) */ + + return_desc->integer.value = operand[0]->integer.value; + + /* + * Acpi specification describes Integer type as a little + * endian unsigned value, so this boundary condition is valid. + */ + for (temp32 = 0; return_desc->integer.value && + temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) { + return_desc->integer.value >>= 1; + } + + return_desc->integer.value = temp32; + break; + + case AML_FIND_SET_RIGHT_BIT_OP: /* find_set_right_bit (Operand, Result) */ + + return_desc->integer.value = operand[0]->integer.value; + + /* + * The Acpi specification describes Integer type as a little + * endian unsigned value, so this boundary condition is valid. + */ + for (temp32 = 0; return_desc->integer.value && + temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) { + return_desc->integer.value <<= 1; + } + + /* Since the bit position is one-based, subtract from 33 (65) */ + + return_desc->integer.value = + temp32 == + 0 ? 0 : (ACPI_INTEGER_BIT_SIZE + 1) - temp32; + break; + + case AML_FROM_BCD_OP: /* from_bcd (BCDValue, Result) */ + /* + * The 64-bit ACPI integer can hold 16 4-bit BCD characters + * (if table is 32-bit, integer can hold 8 BCD characters) + * Convert each 4-bit BCD value + */ + power_of_ten = 1; + return_desc->integer.value = 0; + digit = operand[0]->integer.value; + + /* Convert each BCD digit (each is one nybble wide) */ + + for (i = 0; + (i < acpi_gbl_integer_nybble_width) && (digit > 0); + i++) { + + /* Get the least significant 4-bit BCD digit */ + + temp32 = ((u32) digit) & 0xF; + + /* Check the range of the digit */ + + if (temp32 > 9) { + ACPI_ERROR((AE_INFO, + "BCD digit too large (not decimal): 0x%X", + temp32)); + + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + + /* Sum the digit into the result with the current power of 10 */ + + return_desc->integer.value += + (((u64) temp32) * power_of_ten); + + /* Shift to next BCD digit */ + + digit >>= 4; + + /* Next power of 10 */ + + power_of_ten *= 10; + } + break; + + case AML_TO_BCD_OP: /* to_bcd (Operand, Result) */ + + return_desc->integer.value = 0; + digit = operand[0]->integer.value; + + /* Each BCD digit is one nybble wide */ + + for (i = 0; + (i < acpi_gbl_integer_nybble_width) && (digit > 0); + i++) { + (void)acpi_ut_short_divide(digit, 10, &digit, + &temp32); + + /* + * Insert the BCD digit that resides in the + * remainder from above + */ + return_desc->integer.value |= + (((u64) temp32) << ACPI_MUL_4(i)); + } + + /* Overflow if there is any data left in Digit */ + + if (digit > 0) { + ACPI_ERROR((AE_INFO, + "Integer too large to convert to BCD: 0x%8.8X%8.8X", + ACPI_FORMAT_UINT64(operand[0]-> + integer.value))); + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + break; + + case AML_COND_REF_OF_OP: /* cond_ref_of (source_object, Result) */ + /* + * This op is a little strange because the internal return value is + * different than the return value stored in the result descriptor + * (There are really two return values) + */ + if ((struct acpi_namespace_node *)operand[0] == + acpi_gbl_root_node) { + /* + * This means that the object does not exist in the namespace, + * return FALSE + */ + return_desc->integer.value = 0; + goto cleanup; + } + + /* Get the object reference, store it, and remove our reference */ + + status = acpi_ex_get_object_reference(operand[0], + &return_desc2, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + status = + acpi_ex_store(return_desc2, operand[1], walk_state); + acpi_ut_remove_reference(return_desc2); + + /* The object exists in the namespace, return TRUE */ + + return_desc->integer.value = ACPI_UINT64_MAX; + goto cleanup; + + default: + + /* No other opcodes get here */ + + break; + } + break; + + case AML_STORE_OP: /* Store (Source, Target) */ + /* + * A store operand is typically a number, string, buffer or lvalue + * Be careful about deleting the source object, + * since the object itself may have been stored. + */ + status = acpi_ex_store(operand[0], operand[1], walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* It is possible that the Store already produced a return object */ + + if (!walk_state->result_obj) { + /* + * Normally, we would remove a reference on the Operand[0] + * parameter; But since it is being used as the internal return + * object (meaning we would normally increment it), the two + * cancel out, and we simply don't do anything. + */ + walk_state->result_obj = operand[0]; + walk_state->operands[0] = NULL; /* Prevent deletion */ + } + return_ACPI_STATUS(status); + + /* + * ACPI 2.0 Opcodes + */ + case AML_COPY_OP: /* Copy (Source, Target) */ + + status = + acpi_ut_copy_iobject_to_iobject(operand[0], &return_desc, + walk_state); + break; + + case AML_TO_DECSTRING_OP: /* to_decimal_string (Data, Result) */ + + status = acpi_ex_convert_to_string(operand[0], &return_desc, + ACPI_EXPLICIT_CONVERT_DECIMAL); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference(return_desc); + } + break; + + case AML_TO_HEXSTRING_OP: /* to_hex_string (Data, Result) */ + + status = acpi_ex_convert_to_string(operand[0], &return_desc, + ACPI_EXPLICIT_CONVERT_HEX); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference(return_desc); + } + break; + + case AML_TO_BUFFER_OP: /* to_buffer (Data, Result) */ + + status = acpi_ex_convert_to_buffer(operand[0], &return_desc); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference(return_desc); + } + break; + + case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */ + + status = acpi_ex_convert_to_integer(operand[0], &return_desc, + ACPI_ANY_BASE); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference(return_desc); + } + break; + + case AML_SHIFT_LEFT_BIT_OP: /* shift_left_bit (Source, bit_num) */ + case AML_SHIFT_RIGHT_BIT_OP: /* shift_right_bit (Source, bit_num) */ + + /* These are two obsolete opcodes */ + + ACPI_ERROR((AE_INFO, + "%s is obsolete and not implemented", + acpi_ps_get_opcode_name(walk_state->opcode))); + status = AE_SUPPORT; + goto cleanup; + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + if (ACPI_SUCCESS(status)) { + + /* Store the return value computed above into the target object */ + + status = acpi_ex_store(return_desc, operand[1], walk_state); + } + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + } + + /* Save return object on success */ + + else if (!walk_state->result_obj) { + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_0T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, no target, and a return value + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *temp_desc; + union acpi_operand_object *return_desc = NULL; + acpi_status status = AE_OK; + u32 type; + u64 value; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_0T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_LNOT_OP: /* LNot (Operand) */ + + return_desc = acpi_ut_create_integer_object((u64) 0); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Set result to ONES (TRUE) if Value == 0. Note: + * return_desc->Integer.Value is initially == 0 (FALSE) from above. + */ + if (!operand[0]->integer.value) { + return_desc->integer.value = ACPI_UINT64_MAX; + } + break; + + case AML_DECREMENT_OP: /* Decrement (Operand) */ + case AML_INCREMENT_OP: /* Increment (Operand) */ + /* + * Create a new integer. Can't just get the base integer and + * increment it because it may be an Arg or Field. + */ + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Since we are expecting a Reference operand, it can be either a + * NS Node or an internal object. + */ + temp_desc = operand[0]; + if (ACPI_GET_DESCRIPTOR_TYPE(temp_desc) == + ACPI_DESC_TYPE_OPERAND) { + + /* Internal reference object - prevent deletion */ + + acpi_ut_add_reference(temp_desc); + } + + /* + * Convert the Reference operand to an Integer (This removes a + * reference on the Operand[0] object) + * + * NOTE: We use LNOT_OP here in order to force resolution of the + * reference operand to an actual integer. + */ + status = + acpi_ex_resolve_operands(AML_LNOT_OP, &temp_desc, + walk_state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While resolving operands for [%s]", + acpi_ps_get_opcode_name(walk_state-> + opcode))); + + goto cleanup; + } + + /* + * temp_desc is now guaranteed to be an Integer object -- + * Perform the actual increment or decrement + */ + if (walk_state->opcode == AML_INCREMENT_OP) { + return_desc->integer.value = + temp_desc->integer.value + 1; + } else { + return_desc->integer.value = + temp_desc->integer.value - 1; + } + + /* Finished with this Integer object */ + + acpi_ut_remove_reference(temp_desc); + + /* + * Store the result back (indirectly) through the original + * Reference object + */ + status = acpi_ex_store(return_desc, operand[0], walk_state); + break; + + case AML_TYPE_OP: /* object_type (source_object) */ + /* + * Note: The operand is not resolved at this point because we want to + * get the associated object, not its value. For example, we don't + * want to resolve a field_unit to its value, we want the actual + * field_unit object. + */ + + /* Get the type of the base object */ + + status = + acpi_ex_resolve_multiple(walk_state, operand[0], &type, + NULL); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Allocate a descriptor to hold the type. */ + + return_desc = acpi_ut_create_integer_object((u64) type); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + case AML_SIZE_OF_OP: /* size_of (source_object) */ + /* + * Note: The operand is not resolved at this point because we want to + * get the associated object, not its value. + */ + + /* Get the base object */ + + status = acpi_ex_resolve_multiple(walk_state, + operand[0], &type, + &temp_desc); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * The type of the base object must be integer, buffer, string, or + * package. All others are not supported. + * + * NOTE: Integer is not specifically supported by the ACPI spec, + * but is supported implicitly via implicit operand conversion. + * rather than bother with conversion, we just use the byte width + * global (4 or 8 bytes). + */ + switch (type) { + case ACPI_TYPE_INTEGER: + + value = acpi_gbl_integer_byte_width; + break; + + case ACPI_TYPE_STRING: + + value = temp_desc->string.length; + break; + + case ACPI_TYPE_BUFFER: + + /* Buffer arguments may not be evaluated at this point */ + + status = acpi_ds_get_buffer_arguments(temp_desc); + value = temp_desc->buffer.length; + break; + + case ACPI_TYPE_PACKAGE: + + /* Package arguments may not be evaluated at this point */ + + status = acpi_ds_get_package_arguments(temp_desc); + value = temp_desc->package.count; + break; + + default: + + ACPI_ERROR((AE_INFO, + "Operand must be Buffer/Integer/String/Package - found type %s", + acpi_ut_get_type_name(type))); + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * Now that we have the size of the object, create a result + * object to hold the value + */ + return_desc = acpi_ut_create_integer_object(value); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + case AML_REF_OF_OP: /* ref_of (source_object) */ + + status = + acpi_ex_get_object_reference(operand[0], &return_desc, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + break; + + case AML_DEREF_OF_OP: /* deref_of (obj_reference | String) */ + + /* Check for a method local or argument, or standalone String */ + + if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) == + ACPI_DESC_TYPE_NAMED) { + temp_desc = + acpi_ns_get_attached_object((struct + acpi_namespace_node *) + operand[0]); + if (temp_desc + && ((temp_desc->common.type == ACPI_TYPE_STRING) + || (temp_desc->common.type == + ACPI_TYPE_LOCAL_REFERENCE))) { + operand[0] = temp_desc; + acpi_ut_add_reference(temp_desc); + } else { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + } else { + switch ((operand[0])->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + /* + * This is a deref_of (local_x | arg_x) + * + * Must resolve/dereference the local/arg reference first + */ + switch (operand[0]->reference.class) { + case ACPI_REFCLASS_LOCAL: + case ACPI_REFCLASS_ARG: + + /* Set Operand[0] to the value of the local/arg */ + + status = + acpi_ds_method_data_get_value + (operand[0]->reference.class, + operand[0]->reference.value, + walk_state, &temp_desc); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * Delete our reference to the input object and + * point to the object just retrieved + */ + acpi_ut_remove_reference(operand[0]); + operand[0] = temp_desc; + break; + + case ACPI_REFCLASS_REFOF: + + /* Get the object to which the reference refers */ + + temp_desc = + operand[0]->reference.object; + acpi_ut_remove_reference(operand[0]); + operand[0] = temp_desc; + break; + + default: + + /* Must be an Index op - handled below */ + break; + } + break; + + case ACPI_TYPE_STRING: + + break; + + default: + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + } + + if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) != + ACPI_DESC_TYPE_NAMED) { + if ((operand[0])->common.type == ACPI_TYPE_STRING) { + /* + * This is a deref_of (String). The string is a reference + * to a named ACPI object. + * + * 1) Find the owning Node + * 2) Dereference the node to an actual object. Could be a + * Field, so we need to resolve the node to a value. + */ + status = + acpi_ns_get_node(walk_state->scope_info-> + scope.node, + operand[0]->string.pointer, + ACPI_NS_SEARCH_PARENT, + ACPI_CAST_INDIRECT_PTR + (struct + acpi_namespace_node, + &return_desc)); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + status = + acpi_ex_resolve_node_to_value + (ACPI_CAST_INDIRECT_PTR + (struct acpi_namespace_node, &return_desc), + walk_state); + goto cleanup; + } + } + + /* Operand[0] may have changed from the code above */ + + if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) == + ACPI_DESC_TYPE_NAMED) { + /* + * This is a deref_of (object_reference) + * Get the actual object from the Node (This is the dereference). + * This case may only happen when a local_x or arg_x is + * dereferenced above. + */ + return_desc = acpi_ns_get_attached_object((struct + acpi_namespace_node + *) + operand[0]); + acpi_ut_add_reference(return_desc); + } else { + /* + * This must be a reference object produced by either the + * Index() or ref_of() operator + */ + switch (operand[0]->reference.class) { + case ACPI_REFCLASS_INDEX: + /* + * The target type for the Index operator must be + * either a Buffer or a Package + */ + switch (operand[0]->reference.target_type) { + case ACPI_TYPE_BUFFER_FIELD: + + temp_desc = + operand[0]->reference.object; + + /* + * Create a new object that contains one element of the + * buffer -- the element pointed to by the index. + * + * NOTE: index into a buffer is NOT a pointer to a + * sub-buffer of the main buffer, it is only a pointer to a + * single element (byte) of the buffer! + * + * Since we are returning the value of the buffer at the + * indexed location, we don't need to add an additional + * reference to the buffer itself. + */ + return_desc = + acpi_ut_create_integer_object((u64) + temp_desc->buffer.pointer[operand[0]->reference.value]); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + case ACPI_TYPE_PACKAGE: + /* + * Return the referenced element of the package. We must + * add another reference to the referenced object, however. + */ + return_desc = + *(operand[0]->reference.where); + if (!return_desc) { + /* + * Element is NULL, do not allow the dereference. + * This provides compatibility with other ACPI + * implementations. + */ + return_ACPI_STATUS + (AE_AML_UNINITIALIZED_ELEMENT); + } + + acpi_ut_add_reference(return_desc); + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown Index TargetType 0x%X in reference object %p", + operand[0]->reference. + target_type, operand[0])); + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + case ACPI_REFCLASS_REFOF: + + return_desc = operand[0]->reference.object; + + if (ACPI_GET_DESCRIPTOR_TYPE(return_desc) == + ACPI_DESC_TYPE_NAMED) { + return_desc = + acpi_ns_get_attached_object((struct + acpi_namespace_node + *) + return_desc); + if (!return_desc) { + break; + } + + /* + * June 2013: + * buffer_fields/field_units require additional resolution + */ + switch (return_desc->common.type) { + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + status = + acpi_ex_read_data_from_field + (walk_state, return_desc, + &temp_desc); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + return_desc = temp_desc; + break; + + default: + + /* Add another reference to the object */ + + acpi_ut_add_reference + (return_desc); + break; + } + } + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown class in reference(%p) - 0x%2.2X", + operand[0], + operand[0]->reference.class)); + + status = AE_TYPE; + goto cleanup; + } + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + } + + /* Save return object on success */ + + else { + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exoparg2.c b/kernel/drivers/acpi/acpica/exoparg2.c new file mode 100644 index 000000000..fcc618aa2 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exoparg2.c @@ -0,0 +1,580 @@ +/****************************************************************************** + * + * Module Name: exoparg2 - AML execution - opcodes with 2 arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acinterp.h" +#include "acevents.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exoparg2") + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (1 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_0T_0R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with two arguments, no target, and no return + * value. + * + * ALLOCATION: Deletes both operands + * + ******************************************************************************/ +acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + struct acpi_namespace_node *node; + u32 value; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_0T_0R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the opcode */ + + switch (walk_state->opcode) { + case AML_NOTIFY_OP: /* Notify (notify_object, notify_value) */ + + /* The first operand is a namespace node */ + + node = (struct acpi_namespace_node *)operand[0]; + + /* Second value is the notify value */ + + value = (u32) operand[1]->integer.value; + + /* Are notifies allowed on this object? */ + + if (!acpi_ev_is_notify_object(node)) { + ACPI_ERROR((AE_INFO, + "Unexpected notify object type [%s]", + acpi_ut_get_type_name(node->type))); + + status = AE_AML_OPERAND_TYPE; + break; + } + + /* + * Dispatch the notify to the appropriate handler + * NOTE: the request is queued for execution after this method + * completes. The notify handlers are NOT invoked synchronously + * from this thread -- because handlers may in turn run other + * control methods. + */ + status = acpi_ev_queue_notify_request(node, value); + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_2T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute a dyadic operator (2 operands) with 2 output targets + * and one implicit return value. + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc1 = NULL; + union acpi_operand_object *return_desc2 = NULL; + acpi_status status; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_2T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Execute the opcode */ + + switch (walk_state->opcode) { + case AML_DIVIDE_OP: + + /* Divide (Dividend, Divisor, remainder_result quotient_result) */ + + return_desc1 = + acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc1) { + status = AE_NO_MEMORY; + goto cleanup; + } + + return_desc2 = + acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc2) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Quotient to return_desc1, remainder to return_desc2 */ + + status = acpi_ut_divide(operand[0]->integer.value, + operand[1]->integer.value, + &return_desc1->integer.value, + &return_desc2->integer.value); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + /* Store the results to the target reference operands */ + + status = acpi_ex_store(return_desc2, operand[2], walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + status = acpi_ex_store(return_desc1, operand[3], walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + +cleanup: + /* + * Since the remainder is not returned indirectly, remove a reference to + * it. Only the quotient is returned indirectly. + */ + acpi_ut_remove_reference(return_desc2); + + if (ACPI_FAILURE(status)) { + + /* Delete the return object */ + + acpi_ut_remove_reference(return_desc1); + } + + /* Save return object (the remainder) on success */ + + else { + walk_state->result_obj = return_desc1; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_1T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with two arguments, one target, and a return + * value. + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + u64 index; + acpi_status status = AE_OK; + acpi_size length = 0; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_1T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Execute the opcode */ + + if (walk_state->op_info->flags & AML_MATH) { + + /* All simple math opcodes (add, etc.) */ + + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + return_desc->integer.value = + acpi_ex_do_math_op(walk_state->opcode, + operand[0]->integer.value, + operand[1]->integer.value); + goto store_result_to_target; + } + + switch (walk_state->opcode) { + case AML_MOD_OP: /* Mod (Dividend, Divisor, remainder_result (ACPI 2.0) */ + + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* return_desc will contain the remainder */ + + status = acpi_ut_divide(operand[0]->integer.value, + operand[1]->integer.value, + NULL, &return_desc->integer.value); + break; + + case AML_CONCAT_OP: /* Concatenate (Data1, Data2, Result) */ + + status = acpi_ex_do_concatenate(operand[0], operand[1], + &return_desc, walk_state); + break; + + case AML_TO_STRING_OP: /* to_string (Buffer, Length, Result) (ACPI 2.0) */ + /* + * Input object is guaranteed to be a buffer at this point (it may have + * been converted.) Copy the raw buffer data to a new object of + * type String. + */ + + /* + * Get the length of the new string. It is the smallest of: + * 1) Length of the input buffer + * 2) Max length as specified in the to_string operator + * 3) Length of input buffer up to a zero byte (null terminator) + * + * NOTE: A length of zero is ok, and will create a zero-length, null + * terminated string. + */ + while ((length < operand[0]->buffer.length) && + (length < operand[1]->integer.value) && + (operand[0]->buffer.pointer[length])) { + length++; + } + + /* Allocate a new string object */ + + return_desc = acpi_ut_create_string_object(length); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Copy the raw buffer data with no transform. + * (NULL terminated already) + */ + ACPI_MEMCPY(return_desc->string.pointer, + operand[0]->buffer.pointer, length); + break; + + case AML_CONCAT_RES_OP: + + /* concatenate_res_template (Buffer, Buffer, Result) (ACPI 2.0) */ + + status = acpi_ex_concat_template(operand[0], operand[1], + &return_desc, walk_state); + break; + + case AML_INDEX_OP: /* Index (Source Index Result) */ + + /* Create the internal return object */ + + return_desc = + acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Initialize the Index reference object */ + + index = operand[1]->integer.value; + return_desc->reference.value = (u32) index; + return_desc->reference.class = ACPI_REFCLASS_INDEX; + + /* + * At this point, the Source operand is a String, Buffer, or Package. + * Verify that the index is within range. + */ + switch ((operand[0])->common.type) { + case ACPI_TYPE_STRING: + + if (index >= operand[0]->string.length) { + length = operand[0]->string.length; + status = AE_AML_STRING_LIMIT; + } + + return_desc->reference.target_type = + ACPI_TYPE_BUFFER_FIELD; + break; + + case ACPI_TYPE_BUFFER: + + if (index >= operand[0]->buffer.length) { + length = operand[0]->buffer.length; + status = AE_AML_BUFFER_LIMIT; + } + + return_desc->reference.target_type = + ACPI_TYPE_BUFFER_FIELD; + break; + + case ACPI_TYPE_PACKAGE: + + if (index >= operand[0]->package.count) { + length = operand[0]->package.count; + status = AE_AML_PACKAGE_LIMIT; + } + + return_desc->reference.target_type = ACPI_TYPE_PACKAGE; + return_desc->reference.where = + &operand[0]->package.elements[index]; + break; + + default: + + status = AE_AML_INTERNAL; + goto cleanup; + } + + /* Failure means that the Index was beyond the end of the object */ + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Index (0x%X%8.8X) is beyond end of object (length 0x%X)", + ACPI_FORMAT_UINT64(index), + (u32)length)); + goto cleanup; + } + + /* + * Save the target object and add a reference to it for the life + * of the index + */ + return_desc->reference.object = operand[0]; + acpi_ut_add_reference(operand[0]); + + /* Store the reference to the Target */ + + status = acpi_ex_store(return_desc, operand[2], walk_state); + + /* Return the reference */ + + walk_state->result_obj = return_desc; + goto cleanup; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + +store_result_to_target: + + if (ACPI_SUCCESS(status)) { + /* + * Store the result of the operation (which is now in return_desc) into + * the Target descriptor. + */ + status = acpi_ex_store(return_desc, operand[2], walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + if (!walk_state->result_obj) { + walk_state->result_obj = return_desc; + } + } + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + walk_state->result_obj = NULL; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_0T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with 2 arguments, no target, and a return value + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + acpi_status status = AE_OK; + u8 logical_result = FALSE; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_2A_0T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Create the internal return object */ + + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Execute the Opcode */ + + if (walk_state->op_info->flags & AML_LOGICAL_NUMERIC) { + + /* logical_op (Operand0, Operand1) */ + + status = acpi_ex_do_logical_numeric_op(walk_state->opcode, + operand[0]->integer. + value, + operand[1]->integer. + value, &logical_result); + goto store_logical_result; + } else if (walk_state->op_info->flags & AML_LOGICAL) { + + /* logical_op (Operand0, Operand1) */ + + status = acpi_ex_do_logical_op(walk_state->opcode, operand[0], + operand[1], &logical_result); + goto store_logical_result; + } + + switch (walk_state->opcode) { + case AML_ACQUIRE_OP: /* Acquire (mutex_object, Timeout) */ + + status = + acpi_ex_acquire_mutex(operand[1], operand[0], walk_state); + if (status == AE_TIME) { + logical_result = TRUE; /* TRUE = Acquire timed out */ + status = AE_OK; + } + break; + + case AML_WAIT_OP: /* Wait (event_object, Timeout) */ + + status = acpi_ex_system_wait_event(operand[1], operand[0]); + if (status == AE_TIME) { + logical_result = TRUE; /* TRUE, Wait timed out */ + status = AE_OK; + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + +store_logical_result: + /* + * Set return value to according to logical_result. logical TRUE (all ones) + * Default is FALSE (zero) + */ + if (logical_result) { + return_desc->integer.value = ACPI_UINT64_MAX; + } + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + } + + /* Save return object on success */ + + else { + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exoparg3.c b/kernel/drivers/acpi/acpica/exoparg3.c new file mode 100644 index 000000000..1c64a988c --- /dev/null +++ b/kernel/drivers/acpi/acpica/exoparg3.c @@ -0,0 +1,281 @@ +/****************************************************************************** + * + * Module Name: exoparg3 - AML execution - opcodes with 3 arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "acparser.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exoparg3") + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (1 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_3A_0T_0R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute Triadic operator (3 operands) + * + ******************************************************************************/ +acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + struct acpi_signal_fatal_info *fatal; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_3A_0T_0R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + switch (walk_state->opcode) { + case AML_FATAL_OP: /* Fatal (fatal_type fatal_code fatal_arg) */ + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "FatalOp: Type %X Code %X Arg %X <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", + (u32) operand[0]->integer.value, + (u32) operand[1]->integer.value, + (u32) operand[2]->integer.value)); + + fatal = ACPI_ALLOCATE(sizeof(struct acpi_signal_fatal_info)); + if (fatal) { + fatal->type = (u32) operand[0]->integer.value; + fatal->code = (u32) operand[1]->integer.value; + fatal->argument = (u32) operand[2]->integer.value; + } + + /* Always signal the OS! */ + + status = acpi_os_signal(ACPI_SIGNAL_FATAL, fatal); + + /* Might return while OS is shutting down, just continue */ + + ACPI_FREE(fatal); + goto cleanup; + + case AML_EXTERNAL_OP: + /* + * If the interpreter sees this opcode, just ignore it. The External + * op is intended for use by disassemblers in order to properly + * disassemble control method invocations. The opcode or group of + * opcodes should be surrounded by an "if (0)" clause to ensure that + * AML interpreters never see the opcode. + */ + status = AE_OK; + goto cleanup; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + +cleanup: + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_3A_1T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute Triadic operator (3 operands) + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + char *buffer = NULL; + acpi_status status = AE_OK; + u64 index; + acpi_size length; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_3A_1T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + switch (walk_state->opcode) { + case AML_MID_OP: /* Mid (Source[0], Index[1], Length[2], Result[3]) */ + /* + * Create the return object. The Source operand is guaranteed to be + * either a String or a Buffer, so just use its type. + */ + return_desc = acpi_ut_create_internal_object((operand[0])-> + common.type); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Get the Integer values from the objects */ + + index = operand[1]->integer.value; + length = (acpi_size) operand[2]->integer.value; + + /* + * If the index is beyond the length of the String/Buffer, or if the + * requested length is zero, return a zero-length String/Buffer + */ + if (index >= operand[0]->string.length) { + length = 0; + } + + /* Truncate request if larger than the actual String/Buffer */ + + else if ((index + length) > operand[0]->string.length) { + length = (acpi_size) operand[0]->string.length - + (acpi_size) index; + } + + /* Strings always have a sub-pointer, not so for buffers */ + + switch ((operand[0])->common.type) { + case ACPI_TYPE_STRING: + + /* Always allocate a new buffer for the String */ + + buffer = ACPI_ALLOCATE_ZEROED((acpi_size) length + 1); + if (!buffer) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + case ACPI_TYPE_BUFFER: + + /* If the requested length is zero, don't allocate a buffer */ + + if (length > 0) { + + /* Allocate a new buffer for the Buffer */ + + buffer = ACPI_ALLOCATE_ZEROED(length); + if (!buffer) { + status = AE_NO_MEMORY; + goto cleanup; + } + } + break; + + default: /* Should not happen */ + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + if (buffer) { + + /* We have a buffer, copy the portion requested */ + + ACPI_MEMCPY(buffer, operand[0]->string.pointer + index, + length); + } + + /* Set the length of the new String/Buffer */ + + return_desc->string.pointer = buffer; + return_desc->string.length = (u32) length; + + /* Mark buffer initialized */ + + return_desc->buffer.flags |= AOPOBJ_DATA_VALID; + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + /* Store the result in the target */ + + status = acpi_ex_store(return_desc, operand[3], walk_state); + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status) || walk_state->result_obj) { + acpi_ut_remove_reference(return_desc); + walk_state->result_obj = NULL; + } + + /* Set the return object and exit */ + + else { + walk_state->result_obj = return_desc; + } + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exoparg6.c b/kernel/drivers/acpi/acpica/exoparg6.c new file mode 100644 index 000000000..c930edda3 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exoparg6.c @@ -0,0 +1,332 @@ +/****************************************************************************** + * + * Module Name: exoparg6 - AML execution - opcodes with 6 arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "acparser.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exoparg6") + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (1 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ +/* Local prototypes */ +static u8 +acpi_ex_do_match(u32 match_op, + union acpi_operand_object *package_obj, + union acpi_operand_object *match_obj); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_match + * + * PARAMETERS: match_op - The AML match operand + * package_obj - Object from the target package + * match_obj - Object to be matched + * + * RETURN: TRUE if the match is successful, FALSE otherwise + * + * DESCRIPTION: Implements the low-level match for the ASL Match operator. + * Package elements will be implicitly converted to the type of + * the match object (Integer/Buffer/String). + * + ******************************************************************************/ + +static u8 +acpi_ex_do_match(u32 match_op, + union acpi_operand_object *package_obj, + union acpi_operand_object *match_obj) +{ + u8 logical_result = TRUE; + acpi_status status; + + /* + * Note: Since the package_obj/match_obj ordering is opposite to that of + * the standard logical operators, we have to reverse them when we call + * do_logical_op in order to make the implicit conversion rules work + * correctly. However, this means we have to flip the entire equation + * also. A bit ugly perhaps, but overall, better than fussing the + * parameters around at runtime, over and over again. + * + * Below, P[i] refers to the package element, M refers to the Match object. + */ + switch (match_op) { + case MATCH_MTR: + + /* Always true */ + + break; + + case MATCH_MEQ: + /* + * True if equal: (P[i] == M) + * Change to: (M == P[i]) + */ + status = + acpi_ex_do_logical_op(AML_LEQUAL_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + break; + + case MATCH_MLE: + /* + * True if less than or equal: (P[i] <= M) (P[i] not_greater than M) + * Change to: (M >= P[i]) (M not_less than P[i]) + */ + status = + acpi_ex_do_logical_op(AML_LLESS_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + logical_result = (u8) ! logical_result; + break; + + case MATCH_MLT: + /* + * True if less than: (P[i] < M) + * Change to: (M > P[i]) + */ + status = + acpi_ex_do_logical_op(AML_LGREATER_OP, match_obj, + package_obj, &logical_result); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + break; + + case MATCH_MGE: + /* + * True if greater than or equal: (P[i] >= M) (P[i] not_less than M) + * Change to: (M <= P[i]) (M not_greater than P[i]) + */ + status = + acpi_ex_do_logical_op(AML_LGREATER_OP, match_obj, + package_obj, &logical_result); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + logical_result = (u8) ! logical_result; + break; + + case MATCH_MGT: + /* + * True if greater than: (P[i] > M) + * Change to: (M < P[i]) + */ + status = + acpi_ex_do_logical_op(AML_LLESS_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + break; + + default: + + /* Undefined */ + + return (FALSE); + } + + return (logical_result); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_6A_0T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with 6 arguments, no target, and a return value + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state * walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + acpi_status status = AE_OK; + u64 index; + union acpi_operand_object *this_element; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_6A_0T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + switch (walk_state->opcode) { + case AML_MATCH_OP: + /* + * Match (search_pkg[0], match_op1[1], match_obj1[2], + * match_op2[3], match_obj2[4], start_index[5]) + */ + + /* Validate both Match Term Operators (MTR, MEQ, etc.) */ + + if ((operand[1]->integer.value > MAX_MATCH_OPERATOR) || + (operand[3]->integer.value > MAX_MATCH_OPERATOR)) { + ACPI_ERROR((AE_INFO, "Match operator out of range")); + status = AE_AML_OPERAND_VALUE; + goto cleanup; + } + + /* Get the package start_index, validate against the package length */ + + index = operand[5]->integer.value; + if (index >= operand[0]->package.count) { + ACPI_ERROR((AE_INFO, + "Index (0x%8.8X%8.8X) beyond package end (0x%X)", + ACPI_FORMAT_UINT64(index), + operand[0]->package.count)); + status = AE_AML_PACKAGE_LIMIT; + goto cleanup; + } + + /* Create an integer for the return value */ + /* Default return value is ACPI_UINT64_MAX if no match found */ + + return_desc = acpi_ut_create_integer_object(ACPI_UINT64_MAX); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + + } + + /* + * Examine each element until a match is found. Both match conditions + * must be satisfied for a match to occur. Within the loop, + * "continue" signifies that the current element does not match + * and the next should be examined. + * + * Upon finding a match, the loop will terminate via "break" at + * the bottom. If it terminates "normally", match_value will be + * ACPI_UINT64_MAX (Ones) (its initial value) indicating that no + * match was found. + */ + for (; index < operand[0]->package.count; index++) { + + /* Get the current package element */ + + this_element = operand[0]->package.elements[index]; + + /* Treat any uninitialized (NULL) elements as non-matching */ + + if (!this_element) { + continue; + } + + /* + * Both match conditions must be satisfied. Execution of a continue + * (proceed to next iteration of enclosing for loop) signifies a + * non-match. + */ + if (!acpi_ex_do_match((u32) operand[1]->integer.value, + this_element, operand[2])) { + continue; + } + + if (!acpi_ex_do_match((u32) operand[3]->integer.value, + this_element, operand[4])) { + continue; + } + + /* Match found: Index is the return value */ + + return_desc->integer.value = index; + break; + } + break; + + case AML_LOAD_TABLE_OP: + + status = acpi_ex_load_table_op(walk_state, &return_desc); + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + } + + /* Save return object on success */ + + else { + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exprep.c b/kernel/drivers/acpi/acpica/exprep.c new file mode 100644 index 000000000..4c2836dc8 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exprep.c @@ -0,0 +1,630 @@ +/****************************************************************************** + * + * Module Name: exprep - ACPI AML (p-code) execution - field prep utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" +#include "acdispat.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exprep") + +/* Local prototypes */ +static u32 +acpi_ex_decode_field_access(union acpi_operand_object *obj_desc, + u8 field_flags, u32 * return_byte_alignment); + +#ifdef ACPI_UNDER_DEVELOPMENT + +static u32 +acpi_ex_generate_access(u32 field_bit_offset, + u32 field_bit_length, u32 region_length); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_generate_access + * + * PARAMETERS: field_bit_offset - Start of field within parent region/buffer + * field_bit_length - Length of field in bits + * region_length - Length of parent in bytes + * + * RETURN: Field granularity (8, 16, 32 or 64) and + * byte_alignment (1, 2, 3, or 4) + * + * DESCRIPTION: Generate an optimal access width for fields defined with the + * any_acc keyword. + * + * NOTE: Need to have the region_length in order to check for boundary + * conditions (end-of-region). However, the region_length is a deferred + * operation. Therefore, to complete this implementation, the generation + * of this access width must be deferred until the region length has + * been evaluated. + * + ******************************************************************************/ + +static u32 +acpi_ex_generate_access(u32 field_bit_offset, + u32 field_bit_length, u32 region_length) +{ + u32 field_byte_length; + u32 field_byte_offset; + u32 field_byte_end_offset; + u32 access_byte_width; + u32 field_start_offset; + u32 field_end_offset; + u32 minimum_access_width = 0xFFFFFFFF; + u32 minimum_accesses = 0xFFFFFFFF; + u32 accesses; + + ACPI_FUNCTION_TRACE(ex_generate_access); + + /* Round Field start offset and length to "minimal" byte boundaries */ + + field_byte_offset = ACPI_DIV_8(ACPI_ROUND_DOWN(field_bit_offset, 8)); + field_byte_end_offset = ACPI_DIV_8(ACPI_ROUND_UP(field_bit_length + + field_bit_offset, 8)); + field_byte_length = field_byte_end_offset - field_byte_offset; + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Bit length %u, Bit offset %u\n", + field_bit_length, field_bit_offset)); + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Byte Length %u, Byte Offset %u, End Offset %u\n", + field_byte_length, field_byte_offset, + field_byte_end_offset)); + + /* + * Iterative search for the maximum access width that is both aligned + * and does not go beyond the end of the region + * + * Start at byte_acc and work upwards to qword_acc max. (1,2,4,8 bytes) + */ + for (access_byte_width = 1; access_byte_width <= 8; + access_byte_width <<= 1) { + /* + * 1) Round end offset up to next access boundary and make sure that + * this does not go beyond the end of the parent region. + * 2) When the Access width is greater than the field_byte_length, we + * are done. (This does not optimize for the perfectly aligned + * case yet). + */ + if (ACPI_ROUND_UP(field_byte_end_offset, access_byte_width) <= + region_length) { + field_start_offset = + ACPI_ROUND_DOWN(field_byte_offset, + access_byte_width) / + access_byte_width; + + field_end_offset = + ACPI_ROUND_UP((field_byte_length + + field_byte_offset), + access_byte_width) / + access_byte_width; + + accesses = field_end_offset - field_start_offset; + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "AccessWidth %u end is within region\n", + access_byte_width)); + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Field Start %u, Field End %u -- requires %u accesses\n", + field_start_offset, field_end_offset, + accesses)); + + /* Single access is optimal */ + + if (accesses <= 1) { + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Entire field can be accessed with one operation of size %u\n", + access_byte_width)); + return_VALUE(access_byte_width); + } + + /* + * Fits in the region, but requires more than one read/write. + * try the next wider access on next iteration + */ + if (accesses < minimum_accesses) { + minimum_accesses = accesses; + minimum_access_width = access_byte_width; + } + } else { + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "AccessWidth %u end is NOT within region\n", + access_byte_width)); + if (access_byte_width == 1) { + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Field goes beyond end-of-region!\n")); + + /* Field does not fit in the region at all */ + + return_VALUE(0); + } + + /* + * This width goes beyond the end-of-region, back off to + * previous access + */ + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Backing off to previous optimal access width of %u\n", + minimum_access_width)); + return_VALUE(minimum_access_width); + } + } + + /* + * Could not read/write field with one operation, + * just use max access width + */ + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Cannot access field in one operation, using width 8\n")); + return_VALUE(8); +} +#endif /* ACPI_UNDER_DEVELOPMENT */ + +/******************************************************************************* + * + * FUNCTION: acpi_ex_decode_field_access + * + * PARAMETERS: obj_desc - Field object + * field_flags - Encoded fieldflags (contains access bits) + * return_byte_alignment - Where the byte alignment is returned + * + * RETURN: Field granularity (8, 16, 32 or 64) and + * byte_alignment (1, 2, 3, or 4) + * + * DESCRIPTION: Decode the access_type bits of a field definition. + * + ******************************************************************************/ + +static u32 +acpi_ex_decode_field_access(union acpi_operand_object *obj_desc, + u8 field_flags, u32 * return_byte_alignment) +{ + u32 access; + u32 byte_alignment; + u32 bit_length; + + ACPI_FUNCTION_TRACE(ex_decode_field_access); + + access = (field_flags & AML_FIELD_ACCESS_TYPE_MASK); + + switch (access) { + case AML_FIELD_ACCESS_ANY: + +#ifdef ACPI_UNDER_DEVELOPMENT + byte_alignment = + acpi_ex_generate_access(obj_desc->common_field. + start_field_bit_offset, + obj_desc->common_field.bit_length, + 0xFFFFFFFF + /* Temp until we pass region_length as parameter */ + ); + bit_length = byte_alignment * 8; +#endif + + byte_alignment = 1; + bit_length = 8; + break; + + case AML_FIELD_ACCESS_BYTE: + case AML_FIELD_ACCESS_BUFFER: /* ACPI 2.0 (SMBus Buffer) */ + + byte_alignment = 1; + bit_length = 8; + break; + + case AML_FIELD_ACCESS_WORD: + + byte_alignment = 2; + bit_length = 16; + break; + + case AML_FIELD_ACCESS_DWORD: + + byte_alignment = 4; + bit_length = 32; + break; + + case AML_FIELD_ACCESS_QWORD: /* ACPI 2.0 */ + + byte_alignment = 8; + bit_length = 64; + break; + + default: + + /* Invalid field access type */ + + ACPI_ERROR((AE_INFO, "Unknown field access type 0x%X", access)); + return_UINT32(0); + } + + if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) { + /* + * buffer_field access can be on any byte boundary, so the + * byte_alignment is always 1 byte -- regardless of any byte_alignment + * implied by the field access type. + */ + byte_alignment = 1; + } + + *return_byte_alignment = byte_alignment; + return_UINT32(bit_length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_prep_common_field_object + * + * PARAMETERS: obj_desc - The field object + * field_flags - Access, lock_rule, and update_rule. + * The format of a field_flag is described + * in the ACPI specification + * field_attribute - Special attributes (not used) + * field_bit_position - Field start position + * field_bit_length - Field length in number of bits + * + * RETURN: Status + * + * DESCRIPTION: Initialize the areas of the field object that are common + * to the various types of fields. Note: This is very "sensitive" + * code because we are solving the general case for field + * alignment. + * + ******************************************************************************/ + +acpi_status +acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc, + u8 field_flags, + u8 field_attribute, + u32 field_bit_position, u32 field_bit_length) +{ + u32 access_bit_width; + u32 byte_alignment; + u32 nearest_byte_address; + + ACPI_FUNCTION_TRACE(ex_prep_common_field_object); + + /* + * Note: the structure being initialized is the + * ACPI_COMMON_FIELD_INFO; No structure fields outside of the common + * area are initialized by this procedure. + */ + obj_desc->common_field.field_flags = field_flags; + obj_desc->common_field.attribute = field_attribute; + obj_desc->common_field.bit_length = field_bit_length; + + /* + * Decode the access type so we can compute offsets. The access type gives + * two pieces of information - the width of each field access and the + * necessary byte_alignment (address granularity) of the access. + * + * For any_acc, the access_bit_width is the largest width that is both + * necessary and possible in an attempt to access the whole field in one + * I/O operation. However, for any_acc, the byte_alignment is always one + * byte. + * + * For all Buffer Fields, the byte_alignment is always one byte. + * + * For all other access types (Byte, Word, Dword, Qword), the Bitwidth is + * the same (equivalent) as the byte_alignment. + */ + access_bit_width = acpi_ex_decode_field_access(obj_desc, field_flags, + &byte_alignment); + if (!access_bit_width) { + return_ACPI_STATUS(AE_AML_OPERAND_VALUE); + } + + /* Setup width (access granularity) fields (values are: 1, 2, 4, 8) */ + + obj_desc->common_field.access_byte_width = (u8) + ACPI_DIV_8(access_bit_width); + + /* + * base_byte_offset is the address of the start of the field within the + * region. It is the byte address of the first *datum* (field-width data + * unit) of the field. (i.e., the first datum that contains at least the + * first *bit* of the field.) + * + * Note: byte_alignment is always either equal to the access_bit_width or 8 + * (Byte access), and it defines the addressing granularity of the parent + * region or buffer. + */ + nearest_byte_address = + ACPI_ROUND_BITS_DOWN_TO_BYTES(field_bit_position); + obj_desc->common_field.base_byte_offset = (u32) + ACPI_ROUND_DOWN(nearest_byte_address, byte_alignment); + + /* + * start_field_bit_offset is the offset of the first bit of the field within + * a field datum. + */ + obj_desc->common_field.start_field_bit_offset = (u8) + (field_bit_position - + ACPI_MUL_8(obj_desc->common_field.base_byte_offset)); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_prep_field_value + * + * PARAMETERS: info - Contains all field creation info + * + * RETURN: Status + * + * DESCRIPTION: Construct an object of type union acpi_operand_object with a + * subtype of def_field and connect it to the parent Node. + * + ******************************************************************************/ + +acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *second_desc = NULL; + acpi_status status; + u32 access_byte_width; + u32 type; + + ACPI_FUNCTION_TRACE(ex_prep_field_value); + + /* Parameter validation */ + + if (info->field_type != ACPI_TYPE_LOCAL_INDEX_FIELD) { + if (!info->region_node) { + ACPI_ERROR((AE_INFO, "Null RegionNode")); + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + type = acpi_ns_get_type(info->region_node); + if (type != ACPI_TYPE_REGION) { + ACPI_ERROR((AE_INFO, + "Needed Region, found type 0x%X (%s)", type, + acpi_ut_get_type_name(type))); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + } + + /* Allocate a new field object */ + + obj_desc = acpi_ut_create_internal_object(info->field_type); + if (!obj_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Initialize areas of the object that are common to all fields */ + + obj_desc->common_field.node = info->field_node; + status = acpi_ex_prep_common_field_object(obj_desc, + info->field_flags, + info->attribute, + info->field_bit_position, + info->field_bit_length); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_object_desc(obj_desc); + return_ACPI_STATUS(status); + } + + /* Initialize areas of the object that are specific to the field type */ + + switch (info->field_type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + + obj_desc->field.region_obj = + acpi_ns_get_attached_object(info->region_node); + + /* Fields specific to generic_serial_bus fields */ + + obj_desc->field.access_length = info->access_length; + + if (info->connection_node) { + second_desc = info->connection_node->object; + if (!(second_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = + acpi_ds_get_buffer_arguments(second_desc); + if (ACPI_FAILURE(status)) { + acpi_ut_delete_object_desc(obj_desc); + return_ACPI_STATUS(status); + } + } + + obj_desc->field.resource_buffer = + second_desc->buffer.pointer; + obj_desc->field.resource_length = + (u16)second_desc->buffer.length; + } else if (info->resource_buffer) { + obj_desc->field.resource_buffer = info->resource_buffer; + obj_desc->field.resource_length = info->resource_length; + } + + obj_desc->field.pin_number_index = info->pin_number_index; + + /* Allow full data read from EC address space */ + + if ((obj_desc->field.region_obj->region.space_id == + ACPI_ADR_SPACE_EC) + && (obj_desc->common_field.bit_length > 8)) { + access_byte_width = + ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field. + bit_length); + + /* Maximum byte width supported is 255 */ + + if (access_byte_width < 256) { + obj_desc->common_field.access_byte_width = + (u8)access_byte_width; + } + } + /* An additional reference for the container */ + + acpi_ut_add_reference(obj_desc->field.region_obj); + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "RegionField: BitOff %X, Off %X, Gran %X, Region %p\n", + obj_desc->field.start_field_bit_offset, + obj_desc->field.base_byte_offset, + obj_desc->field.access_byte_width, + obj_desc->field.region_obj)); + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + obj_desc->bank_field.value = info->bank_value; + obj_desc->bank_field.region_obj = + acpi_ns_get_attached_object(info->region_node); + obj_desc->bank_field.bank_obj = + acpi_ns_get_attached_object(info->register_node); + + /* An additional reference for the attached objects */ + + acpi_ut_add_reference(obj_desc->bank_field.region_obj); + acpi_ut_add_reference(obj_desc->bank_field.bank_obj); + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Bank Field: BitOff %X, Off %X, Gran %X, Region %p, BankReg %p\n", + obj_desc->bank_field.start_field_bit_offset, + obj_desc->bank_field.base_byte_offset, + obj_desc->field.access_byte_width, + obj_desc->bank_field.region_obj, + obj_desc->bank_field.bank_obj)); + + /* + * Remember location in AML stream of the field unit + * opcode and operands -- since the bank_value + * operands must be evaluated. + */ + second_desc = obj_desc->common.next_object; + second_desc->extra.aml_start = + ACPI_CAST_PTR(union acpi_parse_object, + info->data_register_node)->named.data; + second_desc->extra.aml_length = + ACPI_CAST_PTR(union acpi_parse_object, + info->data_register_node)->named.length; + + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + /* Get the Index and Data registers */ + + obj_desc->index_field.index_obj = + acpi_ns_get_attached_object(info->register_node); + obj_desc->index_field.data_obj = + acpi_ns_get_attached_object(info->data_register_node); + + if (!obj_desc->index_field.data_obj + || !obj_desc->index_field.index_obj) { + ACPI_ERROR((AE_INFO, + "Null Index Object during field prep")); + acpi_ut_delete_object_desc(obj_desc); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* An additional reference for the attached objects */ + + acpi_ut_add_reference(obj_desc->index_field.data_obj); + acpi_ut_add_reference(obj_desc->index_field.index_obj); + + /* + * April 2006: Changed to match MS behavior + * + * The value written to the Index register is the byte offset of the + * target field in units of the granularity of the index_field + * + * Previously, the value was calculated as an index in terms of the + * width of the Data register, as below: + * + * obj_desc->index_field.Value = (u32) + * (Info->field_bit_position / ACPI_MUL_8 ( + * obj_desc->Field.access_byte_width)); + * + * February 2006: Tried value as a byte offset: + * obj_desc->index_field.Value = (u32) + * ACPI_DIV_8 (Info->field_bit_position); + */ + obj_desc->index_field.value = + (u32) ACPI_ROUND_DOWN(ACPI_DIV_8(info->field_bit_position), + obj_desc->index_field. + access_byte_width); + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "IndexField: BitOff %X, Off %X, Value %X, Gran %X, Index %p, Data %p\n", + obj_desc->index_field.start_field_bit_offset, + obj_desc->index_field.base_byte_offset, + obj_desc->index_field.value, + obj_desc->field.access_byte_width, + obj_desc->index_field.index_obj, + obj_desc->index_field.data_obj)); + break; + + default: + + /* No other types should get here */ + + break; + } + + /* + * Store the constructed descriptor (obj_desc) into the parent Node, + * preserving the current type of that named_obj. + */ + status = acpi_ns_attach_object(info->field_node, obj_desc, + acpi_ns_get_type(info->field_node)); + + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Set NamedObj %p [%4.4s], ObjDesc %p\n", + info->field_node, + acpi_ut_get_node_name(info->field_node), obj_desc)); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exregion.c b/kernel/drivers/acpi/acpica/exregion.c new file mode 100644 index 000000000..f6c2f5499 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exregion.c @@ -0,0 +1,537 @@ +/****************************************************************************** + * + * Module Name: exregion - ACPI default op_region (address space) handlers + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exregion") + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_memory_space_handler + * + * PARAMETERS: function - Read or Write operation + * address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the System Memory address space (Op Region) + * + ******************************************************************************/ +acpi_status +acpi_ex_system_memory_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context) +{ + acpi_status status = AE_OK; + void *logical_addr_ptr = NULL; + struct acpi_mem_space_context *mem_info = region_context; + u32 length; + acpi_size map_length; + acpi_size page_boundary_map_length; +#ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED + u32 remainder; +#endif + + ACPI_FUNCTION_TRACE(ex_system_memory_space_handler); + + /* Validate and translate the bit width */ + + switch (bit_width) { + case 8: + + length = 1; + break; + + case 16: + + length = 2; + break; + + case 32: + + length = 4; + break; + + case 64: + + length = 8; + break; + + default: + + ACPI_ERROR((AE_INFO, "Invalid SystemMemory width %u", + bit_width)); + return_ACPI_STATUS(AE_AML_OPERAND_VALUE); + } + +#ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED + /* + * Hardware does not support non-aligned data transfers, we must verify + * the request. + */ + (void)acpi_ut_short_divide((u64) address, length, NULL, &remainder); + if (remainder != 0) { + return_ACPI_STATUS(AE_AML_ALIGNMENT); + } +#endif + + /* + * Does the request fit into the cached memory mapping? + * Is 1) Address below the current mapping? OR + * 2) Address beyond the current mapping? + */ + if ((address < mem_info->mapped_physical_address) || + (((u64) address + length) > ((u64) + mem_info->mapped_physical_address + + mem_info->mapped_length))) { + /* + * The request cannot be resolved by the current memory mapping; + * Delete the existing mapping and create a new one. + */ + if (mem_info->mapped_length) { + + /* Valid mapping, delete it */ + + acpi_os_unmap_memory(mem_info->mapped_logical_address, + mem_info->mapped_length); + } + + /* + * October 2009: Attempt to map from the requested address to the + * end of the region. However, we will never map more than one + * page, nor will we cross a page boundary. + */ + map_length = (acpi_size) + ((mem_info->address + mem_info->length) - address); + + /* + * If mapping the entire remaining portion of the region will cross + * a page boundary, just map up to the page boundary, do not cross. + * On some systems, crossing a page boundary while mapping regions + * can cause warnings if the pages have different attributes + * due to resource management. + * + * This has the added benefit of constraining a single mapping to + * one page, which is similar to the original code that used a 4k + * maximum window. + */ + page_boundary_map_length = (acpi_size) + (ACPI_ROUND_UP(address, ACPI_DEFAULT_PAGE_SIZE) - address); + if (page_boundary_map_length == 0) { + page_boundary_map_length = ACPI_DEFAULT_PAGE_SIZE; + } + + if (map_length > page_boundary_map_length) { + map_length = page_boundary_map_length; + } + + /* Create a new mapping starting at the address given */ + + mem_info->mapped_logical_address = + acpi_os_map_memory(address, map_length); + if (!mem_info->mapped_logical_address) { + ACPI_ERROR((AE_INFO, + "Could not map memory at 0x%8.8X%8.8X, size %u", + ACPI_FORMAT_UINT64(address), + (u32)map_length)); + mem_info->mapped_length = 0; + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Save the physical address and mapping size */ + + mem_info->mapped_physical_address = address; + mem_info->mapped_length = map_length; + } + + /* + * Generate a logical pointer corresponding to the address we want to + * access + */ + logical_addr_ptr = mem_info->mapped_logical_address + + ((u64) address - (u64) mem_info->mapped_physical_address); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "System-Memory (width %u) R/W %u Address=%8.8X%8.8X\n", + bit_width, function, ACPI_FORMAT_UINT64(address))); + + /* + * Perform the memory read or write + * + * Note: For machines that do not support non-aligned transfers, the target + * address was checked for alignment above. We do not attempt to break the + * transfer up into smaller (byte-size) chunks because the AML specifically + * asked for a transfer width that the hardware may require. + */ + switch (function) { + case ACPI_READ: + + *value = 0; + switch (bit_width) { + case 8: + + *value = (u64)ACPI_GET8(logical_addr_ptr); + break; + + case 16: + + *value = (u64)ACPI_GET16(logical_addr_ptr); + break; + + case 32: + + *value = (u64)ACPI_GET32(logical_addr_ptr); + break; + + case 64: + + *value = (u64)ACPI_GET64(logical_addr_ptr); + break; + + default: + + /* bit_width was already validated */ + + break; + } + break; + + case ACPI_WRITE: + + switch (bit_width) { + case 8: + + ACPI_SET8(logical_addr_ptr, *value); + break; + + case 16: + + ACPI_SET16(logical_addr_ptr, *value); + break; + + case 32: + + ACPI_SET32(logical_addr_ptr, *value); + break; + + case 64: + + ACPI_SET64(logical_addr_ptr, *value); + break; + + default: + + /* bit_width was already validated */ + + break; + } + break; + + default: + + status = AE_BAD_PARAMETER; + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_io_space_handler + * + * PARAMETERS: function - Read or Write operation + * address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the System IO address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_io_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context) +{ + acpi_status status = AE_OK; + u32 value32; + + ACPI_FUNCTION_TRACE(ex_system_io_space_handler); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "System-IO (width %u) R/W %u Address=%8.8X%8.8X\n", + bit_width, function, ACPI_FORMAT_UINT64(address))); + + /* Decode the function parameter */ + + switch (function) { + case ACPI_READ: + + status = acpi_hw_read_port((acpi_io_address) address, + &value32, bit_width); + *value = value32; + break; + + case ACPI_WRITE: + + status = acpi_hw_write_port((acpi_io_address) address, + (u32) * value, bit_width); + break; + + default: + + status = AE_BAD_PARAMETER; + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_pci_config_space_handler + * + * PARAMETERS: function - Read or Write operation + * address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the PCI Config address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_pci_config_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context) +{ + acpi_status status = AE_OK; + struct acpi_pci_id *pci_id; + u16 pci_register; + + ACPI_FUNCTION_TRACE(ex_pci_config_space_handler); + + /* + * The arguments to acpi_os(Read|Write)pci_configuration are: + * + * pci_segment is the PCI bus segment range 0-31 + * pci_bus is the PCI bus number range 0-255 + * pci_device is the PCI device number range 0-31 + * pci_function is the PCI device function number + * pci_register is the Config space register range 0-255 bytes + * + * value - input value for write, output address for read + * + */ + pci_id = (struct acpi_pci_id *)region_context; + pci_register = (u16) (u32) address; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Pci-Config %u (%u) Seg(%04x) Bus(%04x) Dev(%04x) Func(%04x) Reg(%04x)\n", + function, bit_width, pci_id->segment, pci_id->bus, + pci_id->device, pci_id->function, pci_register)); + + switch (function) { + case ACPI_READ: + + *value = 0; + status = acpi_os_read_pci_configuration(pci_id, pci_register, + value, bit_width); + break; + + case ACPI_WRITE: + + status = acpi_os_write_pci_configuration(pci_id, pci_register, + *value, bit_width); + break; + + default: + + status = AE_BAD_PARAMETER; + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_cmos_space_handler + * + * PARAMETERS: function - Read or Write operation + * address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the CMOS address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_cmos_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_cmos_space_handler); + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_pci_bar_space_handler + * + * PARAMETERS: function - Read or Write operation + * address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the PCI bar_target address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_pci_bar_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_pci_bar_space_handler); + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_data_table_space_handler + * + * PARAMETERS: function - Read or Write operation + * address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the Data Table address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_data_table_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + u64 *value, + void *handler_context, void *region_context) +{ + ACPI_FUNCTION_TRACE(ex_data_table_space_handler); + + /* + * Perform the memory read or write. The bit_width was already + * validated. + */ + switch (function) { + case ACPI_READ: + + ACPI_MEMCPY(ACPI_CAST_PTR(char, value), + ACPI_PHYSADDR_TO_PTR(address), + ACPI_DIV_8(bit_width)); + break; + + case ACPI_WRITE: + + ACPI_MEMCPY(ACPI_PHYSADDR_TO_PTR(address), + ACPI_CAST_PTR(char, value), ACPI_DIV_8(bit_width)); + break; + + default: + + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/exresnte.c b/kernel/drivers/acpi/acpica/exresnte.c new file mode 100644 index 000000000..c7e3b929a --- /dev/null +++ b/kernel/drivers/acpi/acpica/exresnte.c @@ -0,0 +1,279 @@ +/****************************************************************************** + * + * Module Name: exresnte - AML Interpreter object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exresnte") + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_node_to_value + * + * PARAMETERS: object_ptr - Pointer to a location that contains + * a pointer to a NS node, and will receive a + * pointer to the resolved object. + * walk_state - Current state. Valid only if executing AML + * code. NULL if simply resolving an object + * + * RETURN: Status + * + * DESCRIPTION: Resolve a Namespace node to a valued object + * + * Note: for some of the data types, the pointer attached to the Node + * can be either a pointer to an actual internal object or a pointer into the + * AML stream itself. These types are currently: + * + * ACPI_TYPE_INTEGER + * ACPI_TYPE_STRING + * ACPI_TYPE_BUFFER + * ACPI_TYPE_MUTEX + * ACPI_TYPE_PACKAGE + * + ******************************************************************************/ +acpi_status +acpi_ex_resolve_node_to_value(struct acpi_namespace_node **object_ptr, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *source_desc; + union acpi_operand_object *obj_desc = NULL; + struct acpi_namespace_node *node; + acpi_object_type entry_type; + + ACPI_FUNCTION_TRACE(ex_resolve_node_to_value); + + /* + * The stack pointer points to a struct acpi_namespace_node (Node). Get the + * object that is attached to the Node. + */ + node = *object_ptr; + source_desc = acpi_ns_get_attached_object(node); + entry_type = acpi_ns_get_type((acpi_handle) node); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Entry=%p SourceDesc=%p [%s]\n", + node, source_desc, + acpi_ut_get_type_name(entry_type))); + + if ((entry_type == ACPI_TYPE_LOCAL_ALIAS) || + (entry_type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) { + + /* There is always exactly one level of indirection */ + + node = ACPI_CAST_PTR(struct acpi_namespace_node, node->object); + source_desc = acpi_ns_get_attached_object(node); + entry_type = acpi_ns_get_type((acpi_handle) node); + *object_ptr = node; + } + + /* + * Several object types require no further processing: + * 1) Device/Thermal objects don't have a "real" subobject, return the Node + * 2) Method locals and arguments have a pseudo-Node + * 3) 10/2007: Added method type to assist with Package construction. + */ + if ((entry_type == ACPI_TYPE_DEVICE) || + (entry_type == ACPI_TYPE_THERMAL) || + (entry_type == ACPI_TYPE_METHOD) || + (node->flags & (ANOBJ_METHOD_ARG | ANOBJ_METHOD_LOCAL))) { + return_ACPI_STATUS(AE_OK); + } + + if (!source_desc) { + ACPI_ERROR((AE_INFO, "No object attached to node [%4.4s] %p", + node->name.ascii, node)); + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + /* + * Action is based on the type of the Node, which indicates the type + * of the attached object or pointer + */ + switch (entry_type) { + case ACPI_TYPE_PACKAGE: + + if (source_desc->common.type != ACPI_TYPE_PACKAGE) { + ACPI_ERROR((AE_INFO, "Object not a Package, type %s", + acpi_ut_get_object_type_name(source_desc))); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + status = acpi_ds_get_package_arguments(source_desc); + if (ACPI_SUCCESS(status)) { + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference(obj_desc); + } + break; + + case ACPI_TYPE_BUFFER: + + if (source_desc->common.type != ACPI_TYPE_BUFFER) { + ACPI_ERROR((AE_INFO, "Object not a Buffer, type %s", + acpi_ut_get_object_type_name(source_desc))); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + status = acpi_ds_get_buffer_arguments(source_desc); + if (ACPI_SUCCESS(status)) { + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference(obj_desc); + } + break; + + case ACPI_TYPE_STRING: + + if (source_desc->common.type != ACPI_TYPE_STRING) { + ACPI_ERROR((AE_INFO, "Object not a String, type %s", + acpi_ut_get_object_type_name(source_desc))); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference(obj_desc); + break; + + case ACPI_TYPE_INTEGER: + + if (source_desc->common.type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Object not a Integer, type %s", + acpi_ut_get_object_type_name(source_desc))); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference(obj_desc); + break; + + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "FieldRead Node=%p SourceDesc=%p Type=%X\n", + node, source_desc, entry_type)); + + status = + acpi_ex_read_data_from_field(walk_state, source_desc, + &obj_desc); + break; + + /* For these objects, just return the object attached to the Node */ + + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_REGION: + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference(obj_desc); + break; + + /* TYPE_ANY is untyped, and thus there is no object associated with it */ + + case ACPI_TYPE_ANY: + + ACPI_ERROR((AE_INFO, + "Untyped entry %p, no attached object!", node)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); /* Cannot be AE_TYPE */ + + case ACPI_TYPE_LOCAL_REFERENCE: + + switch (source_desc->reference.class) { + case ACPI_REFCLASS_TABLE: /* This is a ddb_handle */ + case ACPI_REFCLASS_REFOF: + case ACPI_REFCLASS_INDEX: + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference(obj_desc); + break; + + default: + + /* No named references are allowed here */ + + ACPI_ERROR((AE_INFO, + "Unsupported Reference type 0x%X", + source_desc->reference.class)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + break; + + default: + + /* Default case is for unknown types */ + + ACPI_ERROR((AE_INFO, + "Node %p - Unknown object type 0x%X", + node, entry_type)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + + } /* switch (entry_type) */ + + /* Return the object descriptor */ + + *object_ptr = (void *)obj_desc; + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exresolv.c b/kernel/drivers/acpi/acpica/exresolv.c new file mode 100644 index 000000000..b6b7f3af2 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exresolv.c @@ -0,0 +1,554 @@ +/****************************************************************************** + * + * Module Name: exresolv - AML Interpreter object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exresolv") + +/* Local prototypes */ +static acpi_status +acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_to_value + * + * PARAMETERS: **stack_ptr - Points to entry on obj_stack, which can + * be either an (union acpi_operand_object *) + * or an acpi_handle. + * walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Convert Reference objects to values + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_to_value(union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ex_resolve_to_value, stack_ptr); + + if (!stack_ptr || !*stack_ptr) { + ACPI_ERROR((AE_INFO, "Internal - null pointer")); + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + /* + * The entity pointed to by the stack_ptr can be either + * 1) A valid union acpi_operand_object, or + * 2) A struct acpi_namespace_node (named_obj) + */ + if (ACPI_GET_DESCRIPTOR_TYPE(*stack_ptr) == ACPI_DESC_TYPE_OPERAND) { + status = acpi_ex_resolve_object_to_value(stack_ptr, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (!*stack_ptr) { + ACPI_ERROR((AE_INFO, "Internal - null pointer")); + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + } + + /* + * Object on the stack may have changed if acpi_ex_resolve_object_to_value() + * was called (i.e., we can't use an _else_ here.) + */ + if (ACPI_GET_DESCRIPTOR_TYPE(*stack_ptr) == ACPI_DESC_TYPE_NAMED) { + status = + acpi_ex_resolve_node_to_value(ACPI_CAST_INDIRECT_PTR + (struct acpi_namespace_node, + stack_ptr), walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Resolved object %p\n", *stack_ptr)); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_object_to_value + * + * PARAMETERS: stack_ptr - Pointer to an internal object + * walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the value from an internal object. The Reference type + * uses the associated AML opcode to determine the value. + * + ******************************************************************************/ + +static acpi_status +acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *stack_desc; + union acpi_operand_object *obj_desc = NULL; + u8 ref_type; + + ACPI_FUNCTION_TRACE(ex_resolve_object_to_value); + + stack_desc = *stack_ptr; + + /* This is an object of type union acpi_operand_object */ + + switch (stack_desc->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + + ref_type = stack_desc->reference.class; + + switch (ref_type) { + case ACPI_REFCLASS_LOCAL: + case ACPI_REFCLASS_ARG: + /* + * Get the local from the method's state info + * Note: this increments the local's object reference count + */ + status = acpi_ds_method_data_get_value(ref_type, + stack_desc-> + reference.value, + walk_state, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Arg/Local %X] ValueObj is %p\n", + stack_desc->reference.value, + obj_desc)); + + /* + * Now we can delete the original Reference Object and + * replace it with the resolved value + */ + acpi_ut_remove_reference(stack_desc); + *stack_ptr = obj_desc; + break; + + case ACPI_REFCLASS_INDEX: + + switch (stack_desc->reference.target_type) { + case ACPI_TYPE_BUFFER_FIELD: + + /* Just return - do not dereference */ + break; + + case ACPI_TYPE_PACKAGE: + + /* If method call or copy_object - do not dereference */ + + if ((walk_state->opcode == + AML_INT_METHODCALL_OP) + || (walk_state->opcode == AML_COPY_OP)) { + break; + } + + /* Otherwise, dereference the package_index to a package element */ + + obj_desc = *stack_desc->reference.where; + if (obj_desc) { + /* + * Valid object descriptor, copy pointer to return value + * (i.e., dereference the package index) + * Delete the ref object, increment the returned object + */ + acpi_ut_remove_reference(stack_desc); + acpi_ut_add_reference(obj_desc); + *stack_ptr = obj_desc; + } else { + /* + * A NULL object descriptor means an uninitialized element of + * the package, can't dereference it + */ + ACPI_ERROR((AE_INFO, + "Attempt to dereference an Index to NULL package element Idx=%p", + stack_desc)); + status = AE_AML_UNINITIALIZED_ELEMENT; + } + break; + + default: + + /* Invalid reference object */ + + ACPI_ERROR((AE_INFO, + "Unknown TargetType 0x%X in Index/Reference object %p", + stack_desc->reference.target_type, + stack_desc)); + status = AE_AML_INTERNAL; + break; + } + break; + + case ACPI_REFCLASS_REFOF: + case ACPI_REFCLASS_DEBUG: + case ACPI_REFCLASS_TABLE: + + /* Just leave the object as-is, do not dereference */ + + break; + + case ACPI_REFCLASS_NAME: /* Reference to a named object */ + + /* Dereference the name */ + + if ((stack_desc->reference.node->type == + ACPI_TYPE_DEVICE) + || (stack_desc->reference.node->type == + ACPI_TYPE_THERMAL)) { + + /* These node types do not have 'real' subobjects */ + + *stack_ptr = (void *)stack_desc->reference.node; + } else { + /* Get the object pointed to by the namespace node */ + + *stack_ptr = + (stack_desc->reference.node)->object; + acpi_ut_add_reference(*stack_ptr); + } + + acpi_ut_remove_reference(stack_desc); + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown Reference type 0x%X in %p", + ref_type, stack_desc)); + status = AE_AML_INTERNAL; + break; + } + break; + + case ACPI_TYPE_BUFFER: + + status = acpi_ds_get_buffer_arguments(stack_desc); + break; + + case ACPI_TYPE_PACKAGE: + + status = acpi_ds_get_package_arguments(stack_desc); + break; + + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "FieldRead SourceDesc=%p Type=%X\n", + stack_desc, stack_desc->common.type)); + + status = + acpi_ex_read_data_from_field(walk_state, stack_desc, + &obj_desc); + + /* Remove a reference to the original operand, then override */ + + acpi_ut_remove_reference(*stack_ptr); + *stack_ptr = (void *)obj_desc; + break; + + default: + + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_multiple + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * operand - Starting point for resolution + * return_type - Where the object type is returned + * return_desc - Where the resolved object is returned + * + * RETURN: Status + * + * DESCRIPTION: Return the base object and type. Traverse a reference list if + * necessary to get to the base object. + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state, + union acpi_operand_object *operand, + acpi_object_type * return_type, + union acpi_operand_object **return_desc) +{ + union acpi_operand_object *obj_desc = (void *)operand; + struct acpi_namespace_node *node; + acpi_object_type type; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_ex_resolve_multiple); + + /* Operand can be either a namespace node or an operand descriptor */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { + case ACPI_DESC_TYPE_OPERAND: + + type = obj_desc->common.type; + break; + + case ACPI_DESC_TYPE_NAMED: + + type = ((struct acpi_namespace_node *)obj_desc)->type; + obj_desc = + acpi_ns_get_attached_object((struct acpi_namespace_node *) + obj_desc); + + /* If we had an Alias node, use the attached object for type info */ + + if (type == ACPI_TYPE_LOCAL_ALIAS) { + type = ((struct acpi_namespace_node *)obj_desc)->type; + obj_desc = + acpi_ns_get_attached_object((struct + acpi_namespace_node *) + obj_desc); + } + break; + + default: + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* If type is anything other than a reference, we are done */ + + if (type != ACPI_TYPE_LOCAL_REFERENCE) { + goto exit; + } + + /* + * For reference objects created via the ref_of, Index, or Load/load_table + * operators, we need to get to the base object (as per the ACPI + * specification of the object_type and size_of operators). This means + * traversing the list of possibly many nested references. + */ + while (obj_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE) { + switch (obj_desc->reference.class) { + case ACPI_REFCLASS_REFOF: + case ACPI_REFCLASS_NAME: + + /* Dereference the reference pointer */ + + if (obj_desc->reference.class == ACPI_REFCLASS_REFOF) { + node = obj_desc->reference.object; + } else { /* AML_INT_NAMEPATH_OP */ + + node = obj_desc->reference.node; + } + + /* All "References" point to a NS node */ + + if (ACPI_GET_DESCRIPTOR_TYPE(node) != + ACPI_DESC_TYPE_NAMED) { + ACPI_ERROR((AE_INFO, + "Not a namespace node %p [%s]", + node, + acpi_ut_get_descriptor_name(node))); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* Get the attached object */ + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + + /* No object, use the NS node type */ + + type = acpi_ns_get_type(node); + goto exit; + } + + /* Check for circular references */ + + if (obj_desc == operand) { + return_ACPI_STATUS(AE_AML_CIRCULAR_REFERENCE); + } + break; + + case ACPI_REFCLASS_INDEX: + + /* Get the type of this reference (index into another object) */ + + type = obj_desc->reference.target_type; + if (type != ACPI_TYPE_PACKAGE) { + goto exit; + } + + /* + * The main object is a package, we want to get the type + * of the individual package element that is referenced by + * the index. + * + * This could of course in turn be another reference object. + */ + obj_desc = *(obj_desc->reference.where); + if (!obj_desc) { + + /* NULL package elements are allowed */ + + type = 0; /* Uninitialized */ + goto exit; + } + break; + + case ACPI_REFCLASS_TABLE: + + type = ACPI_TYPE_DDB_HANDLE; + goto exit; + + case ACPI_REFCLASS_LOCAL: + case ACPI_REFCLASS_ARG: + + if (return_desc) { + status = + acpi_ds_method_data_get_value(obj_desc-> + reference. + class, + obj_desc-> + reference. + value, + walk_state, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + acpi_ut_remove_reference(obj_desc); + } else { + status = + acpi_ds_method_data_get_node(obj_desc-> + reference. + class, + obj_desc-> + reference. + value, + walk_state, + &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + type = ACPI_TYPE_ANY; + goto exit; + } + } + break; + + case ACPI_REFCLASS_DEBUG: + + /* The Debug Object is of type "DebugObject" */ + + type = ACPI_TYPE_DEBUG_OBJECT; + goto exit; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown Reference Class 0x%2.2X", + obj_desc->reference.class)); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + } + + /* + * Now we are guaranteed to have an object that has not been created + * via the ref_of or Index operators. + */ + type = obj_desc->common.type; + +exit: + /* Convert internal types to external types */ + + switch (type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + type = ACPI_TYPE_FIELD_UNIT; + break; + + case ACPI_TYPE_LOCAL_SCOPE: + + /* Per ACPI Specification, Scope is untyped */ + + type = ACPI_TYPE_ANY; + break; + + default: + + /* No change to Type required */ + + break; + } + + *return_type = type; + if (return_desc) { + *return_desc = obj_desc; + } + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/exresop.c b/kernel/drivers/acpi/acpica/exresop.c new file mode 100644 index 000000000..d2964af9a --- /dev/null +++ b/kernel/drivers/acpi/acpica/exresop.c @@ -0,0 +1,701 @@ +/****************************************************************************** + * + * Module Name: exresop - AML Interpreter operand/object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acparser.h" +#include "acinterp.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exresop") + +/* Local prototypes */ +static acpi_status +acpi_ex_check_object_type(acpi_object_type type_needed, + acpi_object_type this_type, void *object); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_check_object_type + * + * PARAMETERS: type_needed Object type needed + * this_type Actual object type + * Object Object pointer + * + * RETURN: Status + * + * DESCRIPTION: Check required type against actual type + * + ******************************************************************************/ + +static acpi_status +acpi_ex_check_object_type(acpi_object_type type_needed, + acpi_object_type this_type, void *object) +{ + ACPI_FUNCTION_ENTRY(); + + if (type_needed == ACPI_TYPE_ANY) { + + /* All types OK, so we don't perform any typechecks */ + + return (AE_OK); + } + + if (type_needed == ACPI_TYPE_LOCAL_REFERENCE) { + /* + * Allow the AML "Constant" opcodes (Zero, One, etc.) to be reference + * objects and thus allow them to be targets. (As per the ACPI + * specification, a store to a constant is a noop.) + */ + if ((this_type == ACPI_TYPE_INTEGER) && + (((union acpi_operand_object *)object)->common. + flags & AOPOBJ_AML_CONSTANT)) { + return (AE_OK); + } + } + + if (type_needed != this_type) { + ACPI_ERROR((AE_INFO, + "Needed type [%s], found [%s] %p", + acpi_ut_get_type_name(type_needed), + acpi_ut_get_type_name(this_type), object)); + + return (AE_AML_OPERAND_TYPE); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_operands + * + * PARAMETERS: opcode - Opcode being interpreted + * stack_ptr - Pointer to the operand stack to be + * resolved + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Convert multiple input operands to the types required by the + * target operator. + * + * Each 5-bit group in arg_types represents one required + * operand and indicates the required Type. The corresponding operand + * will be converted to the required type if possible, otherwise we + * abort with an exception. + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_operands(u16 opcode, + union acpi_operand_object ** stack_ptr, + struct acpi_walk_state * walk_state) +{ + union acpi_operand_object *obj_desc; + acpi_status status = AE_OK; + u8 object_type; + u32 arg_types; + const struct acpi_opcode_info *op_info; + u32 this_arg_type; + acpi_object_type type_needed; + u16 target_op = 0; + + ACPI_FUNCTION_TRACE_U32(ex_resolve_operands, opcode); + + op_info = acpi_ps_get_opcode_info(opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + return_ACPI_STATUS(AE_AML_BAD_OPCODE); + } + + arg_types = op_info->runtime_args; + if (arg_types == ARGI_INVALID_OPCODE) { + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", opcode)); + + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Opcode %X [%s] RequiredOperandTypes=%8.8X\n", + opcode, op_info->name, arg_types)); + + /* + * Normal exit is with (arg_types == 0) at end of argument list. + * Function will return an exception from within the loop upon + * finding an entry which is not (or cannot be converted + * to) the required type; if stack underflows; or upon + * finding a NULL stack entry (which should not happen). + */ + while (GET_CURRENT_ARG_TYPE(arg_types)) { + if (!stack_ptr || !*stack_ptr) { + ACPI_ERROR((AE_INFO, "Null stack entry at %p", + stack_ptr)); + + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* Extract useful items */ + + obj_desc = *stack_ptr; + + /* Decode the descriptor type */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { + case ACPI_DESC_TYPE_NAMED: + + /* Namespace Node */ + + object_type = + ((struct acpi_namespace_node *)obj_desc)->type; + + /* + * Resolve an alias object. The construction of these objects + * guarantees that there is only one level of alias indirection; + * thus, the attached object is always the aliased namespace node + */ + if (object_type == ACPI_TYPE_LOCAL_ALIAS) { + obj_desc = + acpi_ns_get_attached_object((struct + acpi_namespace_node + *)obj_desc); + *stack_ptr = obj_desc; + object_type = + ((struct acpi_namespace_node *)obj_desc)-> + type; + } + break; + + case ACPI_DESC_TYPE_OPERAND: + + /* ACPI internal object */ + + object_type = obj_desc->common.type; + + /* Check for bad acpi_object_type */ + + if (!acpi_ut_valid_object_type(object_type)) { + ACPI_ERROR((AE_INFO, + "Bad operand object type [0x%X]", + object_type)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + if (object_type == (u8) ACPI_TYPE_LOCAL_REFERENCE) { + + /* Validate the Reference */ + + switch (obj_desc->reference.class) { + case ACPI_REFCLASS_DEBUG: + + target_op = AML_DEBUG_OP; + + /*lint -fallthrough */ + + case ACPI_REFCLASS_ARG: + case ACPI_REFCLASS_LOCAL: + case ACPI_REFCLASS_INDEX: + case ACPI_REFCLASS_REFOF: + case ACPI_REFCLASS_TABLE: /* ddb_handle from LOAD_OP or LOAD_TABLE_OP */ + case ACPI_REFCLASS_NAME: /* Reference to a named object */ + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Operand is a Reference, Class [%s] %2.2X\n", + acpi_ut_get_reference_name + (obj_desc), + obj_desc->reference. + class)); + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown Reference Class 0x%2.2X in %p", + obj_desc->reference.class, + obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + } + break; + + default: + + /* Invalid descriptor */ + + ACPI_ERROR((AE_INFO, "Invalid descriptor %p [%s]", + obj_desc, + acpi_ut_get_descriptor_name(obj_desc))); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* Get one argument type, point to the next */ + + this_arg_type = GET_CURRENT_ARG_TYPE(arg_types); + INCREMENT_ARG_LIST(arg_types); + + /* + * Handle cases where the object does not need to be + * resolved to a value + */ + switch (this_arg_type) { + case ARGI_REF_OR_STRING: /* Can be a String or Reference */ + + if ((ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == + ACPI_DESC_TYPE_OPERAND) + && (obj_desc->common.type == ACPI_TYPE_STRING)) { + /* + * String found - the string references a named object and + * must be resolved to a node + */ + goto next_operand; + } + + /* + * Else not a string - fall through to the normal Reference + * case below + */ + /*lint -fallthrough */ + + case ARGI_REFERENCE: /* References: */ + case ARGI_INTEGER_REF: + case ARGI_OBJECT_REF: + case ARGI_DEVICE_REF: + case ARGI_TARGETREF: /* Allows implicit conversion rules before store */ + case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */ + case ARGI_SIMPLE_TARGET: /* Name, Local, or arg - no implicit conversion */ + /* + * Need an operand of type ACPI_TYPE_LOCAL_REFERENCE + * A Namespace Node is OK as-is + */ + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == + ACPI_DESC_TYPE_NAMED) { + goto next_operand; + } + + status = + acpi_ex_check_object_type(ACPI_TYPE_LOCAL_REFERENCE, + object_type, obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + goto next_operand; + + case ARGI_DATAREFOBJ: /* Store operator only */ + /* + * We don't want to resolve index_op reference objects during + * a store because this would be an implicit de_ref_of operation. + * Instead, we just want to store the reference object. + * -- All others must be resolved below. + */ + if ((opcode == AML_STORE_OP) && + ((*stack_ptr)->common.type == + ACPI_TYPE_LOCAL_REFERENCE) + && ((*stack_ptr)->reference.class == + ACPI_REFCLASS_INDEX)) { + goto next_operand; + } + break; + + default: + + /* All cases covered above */ + + break; + } + + /* + * Resolve this object to a value + */ + status = acpi_ex_resolve_to_value(stack_ptr, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the resolved object */ + + obj_desc = *stack_ptr; + + /* + * Check the resulting object (value) type + */ + switch (this_arg_type) { + /* + * For the simple cases, only one type of resolved object + * is allowed + */ + case ARGI_MUTEX: + + /* Need an operand of type ACPI_TYPE_MUTEX */ + + type_needed = ACPI_TYPE_MUTEX; + break; + + case ARGI_EVENT: + + /* Need an operand of type ACPI_TYPE_EVENT */ + + type_needed = ACPI_TYPE_EVENT; + break; + + case ARGI_PACKAGE: /* Package */ + + /* Need an operand of type ACPI_TYPE_PACKAGE */ + + type_needed = ACPI_TYPE_PACKAGE; + break; + + case ARGI_ANYTYPE: + + /* Any operand type will do */ + + type_needed = ACPI_TYPE_ANY; + break; + + case ARGI_DDBHANDLE: + + /* Need an operand of type ACPI_TYPE_DDB_HANDLE */ + + type_needed = ACPI_TYPE_LOCAL_REFERENCE; + break; + + /* + * The more complex cases allow multiple resolved object types + */ + case ARGI_INTEGER: + + /* + * Need an operand of type ACPI_TYPE_INTEGER, + * But we can implicitly convert from a STRING or BUFFER + * aka - "Implicit Source Operand Conversion" + */ + status = + acpi_ex_convert_to_integer(obj_desc, stack_ptr, 16); + if (ACPI_FAILURE(status)) { + if (status == AE_TYPE) { + ACPI_ERROR((AE_INFO, + "Needed [Integer/String/Buffer], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + return_ACPI_STATUS(status); + } + + if (obj_desc != *stack_ptr) { + acpi_ut_remove_reference(obj_desc); + } + goto next_operand; + + case ARGI_BUFFER: + /* + * Need an operand of type ACPI_TYPE_BUFFER, + * But we can implicitly convert from a STRING or INTEGER + * aka - "Implicit Source Operand Conversion" + */ + status = acpi_ex_convert_to_buffer(obj_desc, stack_ptr); + if (ACPI_FAILURE(status)) { + if (status == AE_TYPE) { + ACPI_ERROR((AE_INFO, + "Needed [Integer/String/Buffer], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + return_ACPI_STATUS(status); + } + + if (obj_desc != *stack_ptr) { + acpi_ut_remove_reference(obj_desc); + } + goto next_operand; + + case ARGI_STRING: + /* + * Need an operand of type ACPI_TYPE_STRING, + * But we can implicitly convert from a BUFFER or INTEGER + * aka - "Implicit Source Operand Conversion" + */ + status = acpi_ex_convert_to_string(obj_desc, stack_ptr, + ACPI_IMPLICIT_CONVERT_HEX); + if (ACPI_FAILURE(status)) { + if (status == AE_TYPE) { + ACPI_ERROR((AE_INFO, + "Needed [Integer/String/Buffer], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + return_ACPI_STATUS(status); + } + + if (obj_desc != *stack_ptr) { + acpi_ut_remove_reference(obj_desc); + } + goto next_operand; + + case ARGI_COMPUTEDATA: + + /* Need an operand of type INTEGER, STRING or BUFFER */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* Valid operand */ + break; + + default: + ACPI_ERROR((AE_INFO, + "Needed [Integer/String/Buffer], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + goto next_operand; + + case ARGI_BUFFER_OR_STRING: + + /* Need an operand of type STRING or BUFFER */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* Valid operand */ + break; + + case ACPI_TYPE_INTEGER: + + /* Highest priority conversion is to type Buffer */ + + status = + acpi_ex_convert_to_buffer(obj_desc, + stack_ptr); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (obj_desc != *stack_ptr) { + acpi_ut_remove_reference(obj_desc); + } + break; + + default: + ACPI_ERROR((AE_INFO, + "Needed [Integer/String/Buffer], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + goto next_operand; + + case ARGI_DATAOBJECT: + /* + * ARGI_DATAOBJECT is only used by the size_of operator. + * Need a buffer, string, package, or ref_of reference. + * + * The only reference allowed here is a direct reference to + * a namespace node. + */ + switch (obj_desc->common.type) { + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_LOCAL_REFERENCE: + + /* Valid operand */ + break; + + default: + + ACPI_ERROR((AE_INFO, + "Needed [Buffer/String/Package/Reference], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + goto next_operand; + + case ARGI_COMPLEXOBJ: + + /* Need a buffer or package or (ACPI 2.0) String */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* Valid operand */ + break; + + default: + + ACPI_ERROR((AE_INFO, + "Needed [Buffer/String/Package], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + goto next_operand; + + case ARGI_REGION_OR_BUFFER: /* Used by Load() only */ + + /* Need an operand of type REGION or a BUFFER (which could be a resolved region field) */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_REGION: + + /* Valid operand */ + break; + + default: + + ACPI_ERROR((AE_INFO, + "Needed [Region/Buffer], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + goto next_operand; + + case ARGI_DATAREFOBJ: + + /* Used by the Store() operator only */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REFERENCE: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + case ACPI_TYPE_DDB_HANDLE: + + /* Valid operand */ + break; + + default: + + if (acpi_gbl_enable_interpreter_slack) { + /* + * Enable original behavior of Store(), allowing any and all + * objects as the source operand. The ACPI spec does not + * allow this, however. + */ + break; + } + + if (target_op == AML_DEBUG_OP) { + + /* Allow store of any object to the Debug object */ + + break; + } + + ACPI_ERROR((AE_INFO, + "Needed Integer/Buffer/String/Package/Ref/Ddb], found [%s] %p", + acpi_ut_get_object_type_name + (obj_desc), obj_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + goto next_operand; + + default: + + /* Unknown type */ + + ACPI_ERROR((AE_INFO, + "Internal - Unknown ARGI (required operand) type 0x%X", + this_arg_type)); + + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * Make sure that the original object was resolved to the + * required object type (Simple cases only). + */ + status = acpi_ex_check_object_type(type_needed, + (*stack_ptr)->common.type, + *stack_ptr); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + +next_operand: + /* + * If more operands needed, decrement stack_ptr to point + * to next operand on stack + */ + if (GET_CURRENT_ARG_TYPE(arg_types)) { + stack_ptr--; + } + } + + ACPI_DUMP_OPERANDS(walk_state->operands, + acpi_ps_get_opcode_name(opcode), + walk_state->num_operands); + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exstore.c b/kernel/drivers/acpi/acpica/exstore.c new file mode 100644 index 000000000..a7eee2400 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exstore.c @@ -0,0 +1,557 @@ +/****************************************************************************** + * + * Module Name: exstore - AML Interpreter object store support + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exstore") + +/* Local prototypes */ +static acpi_status +acpi_ex_store_object_to_index(union acpi_operand_object *val_desc, + union acpi_operand_object *dest_desc, + struct acpi_walk_state *walk_state); + +static acpi_status +acpi_ex_store_direct_to_node(union acpi_operand_object *source_desc, + struct acpi_namespace_node *node, + struct acpi_walk_state *walk_state); + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store + * + * PARAMETERS: *source_desc - Value to be stored + * *dest_desc - Where to store it. Must be an NS node + * or union acpi_operand_object of type + * Reference; + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Store the value described by source_desc into the location + * described by dest_desc. Called by various interpreter + * functions to store the result of an operation into + * the destination operand -- not just simply the actual "Store" + * ASL operator. + * + ******************************************************************************/ + +acpi_status +acpi_ex_store(union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *ref_desc = dest_desc; + + ACPI_FUNCTION_TRACE_PTR(ex_store, dest_desc); + + /* Validate parameters */ + + if (!source_desc || !dest_desc) { + ACPI_ERROR((AE_INFO, "Null parameter")); + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + /* dest_desc can be either a namespace node or an ACPI object */ + + if (ACPI_GET_DESCRIPTOR_TYPE(dest_desc) == ACPI_DESC_TYPE_NAMED) { + /* + * Dest is a namespace node, + * Storing an object into a Named node. + */ + status = acpi_ex_store_object_to_node(source_desc, + (struct + acpi_namespace_node *) + dest_desc, walk_state, + ACPI_IMPLICIT_CONVERSION); + + return_ACPI_STATUS(status); + } + + /* Destination object must be a Reference or a Constant object */ + + switch (dest_desc->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + + break; + + case ACPI_TYPE_INTEGER: + + /* Allow stores to Constants -- a Noop as per ACPI spec */ + + if (dest_desc->common.flags & AOPOBJ_AML_CONSTANT) { + return_ACPI_STATUS(AE_OK); + } + + /*lint -fallthrough */ + + default: + + /* Destination is not a Reference object */ + + ACPI_ERROR((AE_INFO, + "Target is not a Reference or Constant object - %s [%p]", + acpi_ut_get_object_type_name(dest_desc), + dest_desc)); + + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* + * Examine the Reference class. These cases are handled: + * + * 1) Store to Name (Change the object associated with a name) + * 2) Store to an indexed area of a Buffer or Package + * 3) Store to a Method Local or Arg + * 4) Store to the debug object + */ + switch (ref_desc->reference.class) { + case ACPI_REFCLASS_REFOF: + + /* Storing an object into a Name "container" */ + + status = acpi_ex_store_object_to_node(source_desc, + ref_desc->reference. + object, walk_state, + ACPI_IMPLICIT_CONVERSION); + break; + + case ACPI_REFCLASS_INDEX: + + /* Storing to an Index (pointer into a packager or buffer) */ + + status = + acpi_ex_store_object_to_index(source_desc, ref_desc, + walk_state); + break; + + case ACPI_REFCLASS_LOCAL: + case ACPI_REFCLASS_ARG: + + /* Store to a method local/arg */ + + status = + acpi_ds_store_object_to_local(ref_desc->reference.class, + ref_desc->reference.value, + source_desc, walk_state); + break; + + case ACPI_REFCLASS_DEBUG: + /* + * Storing to the Debug object causes the value stored to be + * displayed and otherwise has no effect -- see ACPI Specification + */ + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "**** Write to Debug Object: Object %p %s ****:\n\n", + source_desc, + acpi_ut_get_object_type_name(source_desc))); + + ACPI_DEBUG_OBJECT(source_desc, 0, 0); + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown Reference Class 0x%2.2X", + ref_desc->reference.class)); + ACPI_DUMP_ENTRY(ref_desc, ACPI_LV_INFO); + + status = AE_AML_INTERNAL; + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_object_to_index + * + * PARAMETERS: *source_desc - Value to be stored + * *dest_desc - Named object to receive the value + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Store the object to indexed Buffer or Package element + * + ******************************************************************************/ + +static acpi_status +acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, + union acpi_operand_object *index_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *obj_desc; + union acpi_operand_object *new_desc; + u8 value = 0; + u32 i; + + ACPI_FUNCTION_TRACE(ex_store_object_to_index); + + /* + * Destination must be a reference pointer, and + * must point to either a buffer or a package + */ + switch (index_desc->reference.target_type) { + case ACPI_TYPE_PACKAGE: + /* + * Storing to a package element. Copy the object and replace + * any existing object with the new object. No implicit + * conversion is performed. + * + * The object at *(index_desc->Reference.Where) is the + * element within the package that is to be modified. + * The parent package object is at index_desc->Reference.Object + */ + obj_desc = *(index_desc->reference.where); + + if (source_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE && + source_desc->reference.class == ACPI_REFCLASS_TABLE) { + + /* This is a DDBHandle, just add a reference to it */ + + acpi_ut_add_reference(source_desc); + new_desc = source_desc; + } else { + /* Normal object, copy it */ + + status = + acpi_ut_copy_iobject_to_iobject(source_desc, + &new_desc, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + if (obj_desc) { + + /* Decrement reference count by the ref count of the parent package */ + + for (i = 0; i < ((union acpi_operand_object *) + index_desc->reference.object)->common. + reference_count; i++) { + acpi_ut_remove_reference(obj_desc); + } + } + + *(index_desc->reference.where) = new_desc; + + /* Increment ref count by the ref count of the parent package-1 */ + + for (i = 1; i < ((union acpi_operand_object *) + index_desc->reference.object)->common. + reference_count; i++) { + acpi_ut_add_reference(new_desc); + } + + break; + + case ACPI_TYPE_BUFFER_FIELD: + /* + * Store into a Buffer or String (not actually a real buffer_field) + * at a location defined by an Index. + * + * The first 8-bit element of the source object is written to the + * 8-bit Buffer location defined by the Index destination object, + * according to the ACPI 2.0 specification. + */ + + /* + * Make sure the target is a Buffer or String. An error should + * not happen here, since the reference_object was constructed + * by the INDEX_OP code. + */ + obj_desc = index_desc->reference.object; + if ((obj_desc->common.type != ACPI_TYPE_BUFFER) && + (obj_desc->common.type != ACPI_TYPE_STRING)) { + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* + * The assignment of the individual elements will be slightly + * different for each source type. + */ + switch (source_desc->common.type) { + case ACPI_TYPE_INTEGER: + + /* Use the least-significant byte of the integer */ + + value = (u8) (source_desc->integer.value); + break; + + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + + /* Note: Takes advantage of common string/buffer fields */ + + value = source_desc->buffer.pointer[0]; + break; + + default: + + /* All other types are invalid */ + + ACPI_ERROR((AE_INFO, + "Source must be Integer/Buffer/String type, not %s", + acpi_ut_get_object_type_name(source_desc))); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* Store the source value into the target buffer byte */ + + obj_desc->buffer.pointer[index_desc->reference.value] = value; + break; + + default: + ACPI_ERROR((AE_INFO, "Target is not a Package or BufferField")); + status = AE_AML_OPERAND_TYPE; + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_object_to_node + * + * PARAMETERS: source_desc - Value to be stored + * node - Named object to receive the value + * walk_state - Current walk state + * implicit_conversion - Perform implicit conversion (yes/no) + * + * RETURN: Status + * + * DESCRIPTION: Store the object to the named object. + * + * The Assignment of an object to a named object is handled here + * The value passed in will replace the current value (if any) + * with the input value. + * + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. A copy_object can change + * the target type, however. + * + * The implicit_conversion flag is set to NO/FALSE only when + * storing to an arg_x -- as per the rules of the ACPI spec. + * + * Assumes parameters are already validated. + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, + struct acpi_namespace_node *node, + struct acpi_walk_state *walk_state, + u8 implicit_conversion) +{ + acpi_status status = AE_OK; + union acpi_operand_object *target_desc; + union acpi_operand_object *new_desc; + acpi_object_type target_type; + + ACPI_FUNCTION_TRACE_PTR(ex_store_object_to_node, source_desc); + + /* Get current type of the node, and object attached to Node */ + + target_type = acpi_ns_get_type(node); + target_desc = acpi_ns_get_attached_object(node); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Storing %p (%s) to node %p (%s)\n", + source_desc, + acpi_ut_get_object_type_name(source_desc), node, + acpi_ut_get_type_name(target_type))); + + /* + * Resolve the source object to an actual value + * (If it is a reference object) + */ + status = acpi_ex_resolve_object(&source_desc, target_type, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Do the actual store operation */ + + switch (target_type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + /* + * The simple data types all support implicit source operand + * conversion before the store. + */ + + if ((walk_state->opcode == AML_COPY_OP) || !implicit_conversion) { + /* + * However, copy_object and Stores to arg_x do not perform + * an implicit conversion, as per the ACPI specification. + * A direct store is performed instead. + */ + status = acpi_ex_store_direct_to_node(source_desc, node, + walk_state); + break; + } + + /* Store with implicit source operand conversion support */ + + status = + acpi_ex_store_object_to_object(source_desc, target_desc, + &new_desc, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (new_desc != target_desc) { + /* + * Store the new new_desc as the new value of the Name, and set + * the Name's type to that of the value being stored in it. + * source_desc reference count is incremented by attach_object. + * + * Note: This may change the type of the node if an explicit + * store has been performed such that the node/object type + * has been changed. + */ + status = acpi_ns_attach_object(node, new_desc, + new_desc->common.type); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Store %s into %s via Convert/Attach\n", + acpi_ut_get_object_type_name + (source_desc), + acpi_ut_get_object_type_name + (new_desc))); + } + break; + + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + /* + * For all fields, always write the source data to the target + * field. Any required implicit source operand conversion is + * performed in the function below as necessary. Note, field + * objects must retain their original type permanently. + */ + status = acpi_ex_write_data_to_field(source_desc, target_desc, + &walk_state->result_obj); + break; + + default: + /* + * No conversions for all other types. Directly store a copy of + * the source object. This is the ACPI spec-defined behavior for + * the copy_object operator. + * + * NOTE: For the Store operator, this is a departure from the + * ACPI spec, which states "If conversion is impossible, abort + * the running control method". Instead, this code implements + * "If conversion is impossible, treat the Store operation as + * a CopyObject". + */ + status = acpi_ex_store_direct_to_node(source_desc, node, + walk_state); + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_direct_to_node + * + * PARAMETERS: source_desc - Value to be stored + * node - Named object to receive the value + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: "Store" an object directly to a node. This involves a copy + * and an attach. + * + ******************************************************************************/ + +static acpi_status +acpi_ex_store_direct_to_node(union acpi_operand_object *source_desc, + struct acpi_namespace_node *node, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object *new_desc; + + ACPI_FUNCTION_TRACE(ex_store_direct_to_node); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Storing [%s] (%p) directly into node [%s] (%p)" + " with no implicit conversion\n", + acpi_ut_get_object_type_name(source_desc), + source_desc, acpi_ut_get_type_name(node->type), + node)); + + /* Copy the source object to a new object */ + + status = + acpi_ut_copy_iobject_to_iobject(source_desc, &new_desc, walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Attach the new object to the node */ + + status = acpi_ns_attach_object(node, new_desc, new_desc->common.type); + acpi_ut_remove_reference(new_desc); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exstoren.c b/kernel/drivers/acpi/acpica/exstoren.c new file mode 100644 index 000000000..3101607b4 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exstoren.c @@ -0,0 +1,294 @@ +/****************************************************************************** + * + * Module Name: exstoren - AML Interpreter object store support, + * Store to Node (namespace object) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exstoren") + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_object + * + * PARAMETERS: source_desc_ptr - Pointer to the source object + * target_type - Current type of the target + * walk_state - Current walk state + * + * RETURN: Status, resolved object in source_desc_ptr. + * + * DESCRIPTION: Resolve an object. If the object is a reference, dereference + * it and return the actual object in the source_desc_ptr. + * + ******************************************************************************/ +acpi_status +acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, + acpi_object_type target_type, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *source_desc = *source_desc_ptr; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_resolve_object); + + /* Ensure we have a Target that can be stored to */ + + switch (target_type) { + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + /* + * These cases all require only Integers or values that + * can be converted to Integers (Strings or Buffers) + */ + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + /* + * Stores into a Field/Region or into a Integer/Buffer/String + * are all essentially the same. This case handles the + * "interchangeable" types Integer, String, and Buffer. + */ + if (source_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE) { + + /* Resolve a reference object first */ + + status = + acpi_ex_resolve_to_value(source_desc_ptr, + walk_state); + if (ACPI_FAILURE(status)) { + break; + } + } + + /* For copy_object, no further validation necessary */ + + if (walk_state->opcode == AML_COPY_OP) { + break; + } + + /* Must have a Integer, Buffer, or String */ + + if ((source_desc->common.type != ACPI_TYPE_INTEGER) && + (source_desc->common.type != ACPI_TYPE_BUFFER) && + (source_desc->common.type != ACPI_TYPE_STRING) && + !((source_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE) && + (source_desc->reference.class == ACPI_REFCLASS_TABLE))) { + + /* Conversion successful but still not a valid type */ + + ACPI_ERROR((AE_INFO, + "Cannot assign type %s to %s (must be type Int/Str/Buf)", + acpi_ut_get_object_type_name(source_desc), + acpi_ut_get_type_name(target_type))); + status = AE_AML_OPERAND_TYPE; + } + break; + + case ACPI_TYPE_LOCAL_ALIAS: + case ACPI_TYPE_LOCAL_METHOD_ALIAS: + /* + * All aliases should have been resolved earlier, during the + * operand resolution phase. + */ + ACPI_ERROR((AE_INFO, "Store into an unresolved Alias object")); + status = AE_AML_INTERNAL; + break; + + case ACPI_TYPE_PACKAGE: + default: + /* + * All other types than Alias and the various Fields come here, + * including the untyped case - ACPI_TYPE_ANY. + */ + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_object_to_object + * + * PARAMETERS: source_desc - Object to store + * dest_desc - Object to receive a copy of the source + * new_desc - New object if dest_desc is obsoleted + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: "Store" an object to another object. This may include + * converting the source type to the target type (implicit + * conversion), and a copy of the value of the source to + * the target. + * + * The Assignment of an object to another (not named) object + * is handled here. + * The Source passed in will replace the current value (if any) + * with the input value. + * + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. + * + * This module allows destination types of Number, String, + * Buffer, and Package. + * + * Assumes parameters are already validated. NOTE: source_desc + * resolution (from a reference object) must be performed by + * the caller if necessary. + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_object_to_object(union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc, + union acpi_operand_object **new_desc, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *actual_src_desc; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_PTR(ex_store_object_to_object, source_desc); + + actual_src_desc = source_desc; + if (!dest_desc) { + /* + * There is no destination object (An uninitialized node or + * package element), so we can simply copy the source object + * creating a new destination object + */ + status = + acpi_ut_copy_iobject_to_iobject(actual_src_desc, new_desc, + walk_state); + return_ACPI_STATUS(status); + } + + if (source_desc->common.type != dest_desc->common.type) { + /* + * The source type does not match the type of the destination. + * Perform the "implicit conversion" of the source to the current type + * of the target as per the ACPI specification. + * + * If no conversion performed, actual_src_desc = source_desc. + * Otherwise, actual_src_desc is a temporary object to hold the + * converted object. + */ + status = acpi_ex_convert_to_target_type(dest_desc->common.type, + source_desc, + &actual_src_desc, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (source_desc == actual_src_desc) { + /* + * No conversion was performed. Return the source_desc as the + * new object. + */ + *new_desc = source_desc; + return_ACPI_STATUS(AE_OK); + } + } + + /* + * We now have two objects of identical types, and we can perform a + * copy of the *value* of the source object. + */ + switch (dest_desc->common.type) { + case ACPI_TYPE_INTEGER: + + dest_desc->integer.value = actual_src_desc->integer.value; + + /* Truncate value if we are executing from a 32-bit ACPI table */ + + (void)acpi_ex_truncate_for32bit_table(dest_desc); + break; + + case ACPI_TYPE_STRING: + + status = + acpi_ex_store_string_to_string(actual_src_desc, dest_desc); + break; + + case ACPI_TYPE_BUFFER: + + status = + acpi_ex_store_buffer_to_buffer(actual_src_desc, dest_desc); + break; + + case ACPI_TYPE_PACKAGE: + + status = + acpi_ut_copy_iobject_to_iobject(actual_src_desc, &dest_desc, + walk_state); + break; + + default: + /* + * All other types come here. + */ + ACPI_WARNING((AE_INFO, "Store into type %s not implemented", + acpi_ut_get_object_type_name(dest_desc))); + + status = AE_NOT_IMPLEMENTED; + break; + } + + if (actual_src_desc != source_desc) { + + /* Delete the intermediate (temporary) source object */ + + acpi_ut_remove_reference(actual_src_desc); + } + + *new_desc = dest_desc; + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/exstorob.c b/kernel/drivers/acpi/acpica/exstorob.c new file mode 100644 index 000000000..6fa3c8d8f --- /dev/null +++ b/kernel/drivers/acpi/acpica/exstorob.c @@ -0,0 +1,220 @@ +/****************************************************************************** + * + * Module Name: exstorob - AML Interpreter object store support, store to object + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exstorob") + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_buffer_to_buffer + * + * PARAMETERS: source_desc - Source object to copy + * target_desc - Destination object of the copy + * + * RETURN: Status + * + * DESCRIPTION: Copy a buffer object to another buffer object. + * + ******************************************************************************/ +acpi_status +acpi_ex_store_buffer_to_buffer(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc) +{ + u32 length; + u8 *buffer; + + ACPI_FUNCTION_TRACE_PTR(ex_store_buffer_to_buffer, source_desc); + + /* If Source and Target are the same, just return */ + + if (source_desc == target_desc) { + return_ACPI_STATUS(AE_OK); + } + + /* We know that source_desc is a buffer by now */ + + buffer = ACPI_CAST_PTR(u8, source_desc->buffer.pointer); + length = source_desc->buffer.length; + + /* + * If target is a buffer of length zero or is a static buffer, + * allocate a new buffer of the proper length + */ + if ((target_desc->buffer.length == 0) || + (target_desc->common.flags & AOPOBJ_STATIC_POINTER)) { + target_desc->buffer.pointer = ACPI_ALLOCATE(length); + if (!target_desc->buffer.pointer) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + target_desc->buffer.length = length; + } + + /* Copy source buffer to target buffer */ + + if (length <= target_desc->buffer.length) { + + /* Clear existing buffer and copy in the new one */ + + ACPI_MEMSET(target_desc->buffer.pointer, 0, + target_desc->buffer.length); + ACPI_MEMCPY(target_desc->buffer.pointer, buffer, length); + +#ifdef ACPI_OBSOLETE_BEHAVIOR + /* + * NOTE: ACPI versions up to 3.0 specified that the buffer must be + * truncated if the string is smaller than the buffer. However, "other" + * implementations of ACPI never did this and thus became the defacto + * standard. ACPI 3.0A changes this behavior such that the buffer + * is no longer truncated. + */ + + /* + * OBSOLETE BEHAVIOR: + * If the original source was a string, we must truncate the buffer, + * according to the ACPI spec. Integer-to-Buffer and Buffer-to-Buffer + * copy must not truncate the original buffer. + */ + if (original_src_type == ACPI_TYPE_STRING) { + + /* Set the new length of the target */ + + target_desc->buffer.length = length; + } +#endif + } else { + /* Truncate the source, copy only what will fit */ + + ACPI_MEMCPY(target_desc->buffer.pointer, buffer, + target_desc->buffer.length); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Truncating source buffer from %X to %X\n", + length, target_desc->buffer.length)); + } + + /* Copy flags */ + + target_desc->buffer.flags = source_desc->buffer.flags; + target_desc->common.flags &= ~AOPOBJ_STATIC_POINTER; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_string_to_string + * + * PARAMETERS: source_desc - Source object to copy + * target_desc - Destination object of the copy + * + * RETURN: Status + * + * DESCRIPTION: Copy a String object to another String object + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_string_to_string(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc) +{ + u32 length; + u8 *buffer; + + ACPI_FUNCTION_TRACE_PTR(ex_store_string_to_string, source_desc); + + /* If Source and Target are the same, just return */ + + if (source_desc == target_desc) { + return_ACPI_STATUS(AE_OK); + } + + /* We know that source_desc is a string by now */ + + buffer = ACPI_CAST_PTR(u8, source_desc->string.pointer); + length = source_desc->string.length; + + /* + * Replace existing string value if it will fit and the string + * pointer is not a static pointer (part of an ACPI table) + */ + if ((length < target_desc->string.length) && + (!(target_desc->common.flags & AOPOBJ_STATIC_POINTER))) { + /* + * String will fit in existing non-static buffer. + * Clear old string and copy in the new one + */ + ACPI_MEMSET(target_desc->string.pointer, 0, + (acpi_size) target_desc->string.length + 1); + ACPI_MEMCPY(target_desc->string.pointer, buffer, length); + } else { + /* + * Free the current buffer, then allocate a new buffer + * large enough to hold the value + */ + if (target_desc->string.pointer && + (!(target_desc->common.flags & AOPOBJ_STATIC_POINTER))) { + + /* Only free if not a pointer into the DSDT */ + + ACPI_FREE(target_desc->string.pointer); + } + + target_desc->string.pointer = ACPI_ALLOCATE_ZEROED((acpi_size) + length + 1); + if (!target_desc->string.pointer) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + target_desc->common.flags &= ~AOPOBJ_STATIC_POINTER; + ACPI_MEMCPY(target_desc->string.pointer, buffer, length); + } + + /* Set the new target length */ + + target_desc->string.length = length; + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/exsystem.c b/kernel/drivers/acpi/acpica/exsystem.c new file mode 100644 index 000000000..05450656f --- /dev/null +++ b/kernel/drivers/acpi/acpica/exsystem.c @@ -0,0 +1,310 @@ +/****************************************************************************** + * + * Module Name: exsystem - Interface to OS services + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exsystem") + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_wait_semaphore + * + * PARAMETERS: semaphore - Semaphore to wait on + * timeout - Max time to wait + * + * RETURN: Status + * + * DESCRIPTION: Implements a semaphore wait with a check to see if the + * semaphore is available immediately. If it is not, the + * interpreter is released before waiting. + * + ******************************************************************************/ +acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ex_system_wait_semaphore); + + status = acpi_os_wait_semaphore(semaphore, 1, ACPI_DO_NOT_WAIT); + if (ACPI_SUCCESS(status)) { + return_ACPI_STATUS(status); + } + + if (status == AE_TIME) { + + /* We must wait, so unlock the interpreter */ + + acpi_ex_exit_interpreter(); + + status = acpi_os_wait_semaphore(semaphore, 1, timeout); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "*** Thread awake after blocking, %s\n", + acpi_format_exception(status))); + + /* Reacquire the interpreter */ + + acpi_ex_enter_interpreter(); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_wait_mutex + * + * PARAMETERS: mutex - Mutex to wait on + * timeout - Max time to wait + * + * RETURN: Status + * + * DESCRIPTION: Implements a mutex wait with a check to see if the + * mutex is available immediately. If it is not, the + * interpreter is released before waiting. + * + ******************************************************************************/ + +acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ex_system_wait_mutex); + + status = acpi_os_acquire_mutex(mutex, ACPI_DO_NOT_WAIT); + if (ACPI_SUCCESS(status)) { + return_ACPI_STATUS(status); + } + + if (status == AE_TIME) { + + /* We must wait, so unlock the interpreter */ + + acpi_ex_exit_interpreter(); + + status = acpi_os_acquire_mutex(mutex, timeout); + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "*** Thread awake after blocking, %s\n", + acpi_format_exception(status))); + + /* Reacquire the interpreter */ + + acpi_ex_enter_interpreter(); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_do_stall + * + * PARAMETERS: how_long - The amount of time to stall, + * in microseconds + * + * RETURN: Status + * + * DESCRIPTION: Suspend running thread for specified amount of time. + * Note: ACPI specification requires that Stall() does not + * relinquish the processor, and delays longer than 100 usec + * should use Sleep() instead. We allow stalls up to 255 usec + * for compatibility with other interpreters and existing BIOSs. + * + ******************************************************************************/ + +acpi_status acpi_ex_system_do_stall(u32 how_long) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_ENTRY(); + + if (how_long > 255) { /* 255 microseconds */ + /* + * Longer than 255 usec, this is an error + * + * (ACPI specifies 100 usec as max, but this gives some slack in + * order to support existing BIOSs) + */ + ACPI_ERROR((AE_INFO, "Time parameter is too large (%u)", + how_long)); + status = AE_AML_OPERAND_VALUE; + } else { + acpi_os_stall(how_long); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_do_sleep + * + * PARAMETERS: how_long - The amount of time to sleep, + * in milliseconds + * + * RETURN: None + * + * DESCRIPTION: Sleep the running thread for specified amount of time. + * + ******************************************************************************/ + +acpi_status acpi_ex_system_do_sleep(u64 how_long) +{ + ACPI_FUNCTION_ENTRY(); + + /* Since this thread will sleep, we must release the interpreter */ + + acpi_ex_exit_interpreter(); + + /* + * For compatibility with other ACPI implementations and to prevent + * accidental deep sleeps, limit the sleep time to something reasonable. + */ + if (how_long > ACPI_MAX_SLEEP) { + how_long = ACPI_MAX_SLEEP; + } + + acpi_os_sleep(how_long); + + /* And now we must get the interpreter again */ + + acpi_ex_enter_interpreter(); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_signal_event + * + * PARAMETERS: obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. + * + ******************************************************************************/ + +acpi_status acpi_ex_system_signal_event(union acpi_operand_object * obj_desc) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_system_signal_event); + + if (obj_desc) { + status = + acpi_os_signal_semaphore(obj_desc->event.os_semaphore, 1); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_wait_event + * + * PARAMETERS: time_desc - The 'time to delay' object descriptor + * obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. This operation is a request to wait for an + * event. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_wait_event(union acpi_operand_object *time_desc, + union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ex_system_wait_event); + + if (obj_desc) { + status = + acpi_ex_system_wait_semaphore(obj_desc->event.os_semaphore, + (u16) time_desc->integer. + value); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_reset_event + * + * PARAMETERS: obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Reset an event to a known state. + * + ******************************************************************************/ + +acpi_status acpi_ex_system_reset_event(union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + acpi_semaphore temp_semaphore; + + ACPI_FUNCTION_ENTRY(); + + /* + * We are going to simply delete the existing semaphore and + * create a new one! + */ + status = + acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0, &temp_semaphore); + if (ACPI_SUCCESS(status)) { + (void)acpi_os_delete_semaphore(obj_desc->event.os_semaphore); + obj_desc->event.os_semaphore = temp_semaphore; + } + + return (status); +} diff --git a/kernel/drivers/acpi/acpica/exutils.c b/kernel/drivers/acpi/acpica/exutils.c new file mode 100644 index 000000000..3f4225e95 --- /dev/null +++ b/kernel/drivers/acpi/acpica/exutils.c @@ -0,0 +1,406 @@ +/****************************************************************************** + * + * Module Name: exutils - interpreter/scanner utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * DEFINE_AML_GLOBALS is tested in amlcode.h + * to determine whether certain global names should be "defined" or only + * "declared" in the current compilation. This enhances maintainability + * by enabling a single header file to embody all knowledge of the names + * in question. + * + * Exactly one module of any executable should #define DEFINE_GLOBALS + * before #including the header files which use this convention. The + * names in question will be defined and initialized in that module, + * and declared as extern in all other modules which #include those + * header files. + */ + +#define DEFINE_AML_GLOBALS + +#include +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exutils") + +/* Local prototypes */ +static u32 acpi_ex_digits_needed(u64 value, u32 base); + +#ifndef ACPI_NO_METHOD_EXECUTION +/******************************************************************************* + * + * FUNCTION: acpi_ex_enter_interpreter + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Enter the interpreter execution region. Failure to enter + * the interpreter region is a fatal system error. Used in + * conjunction with exit_interpreter. + * + ******************************************************************************/ + +void acpi_ex_enter_interpreter(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ex_enter_interpreter); + + status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not acquire AML Interpreter mutex")); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_exit_interpreter + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Exit the interpreter execution region. This is the top level + * routine used to exit the interpreter when all processing has + * been completed, or when the method blocks. + * + * Cases where the interpreter is unlocked internally: + * 1) Method will be blocked on a Sleep() AML opcode + * 2) Method will be blocked on an Acquire() AML opcode + * 3) Method will be blocked on a Wait() AML opcode + * 4) Method will be blocked to acquire the global lock + * 5) Method will be blocked waiting to execute a serialized control + * method that is currently executing + * 6) About to invoke a user-installed opregion handler + * + ******************************************************************************/ + +void acpi_ex_exit_interpreter(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ex_exit_interpreter); + + status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not release AML Interpreter mutex")); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_truncate_for32bit_table + * + * PARAMETERS: obj_desc - Object to be truncated + * + * RETURN: TRUE if a truncation was performed, FALSE otherwise. + * + * DESCRIPTION: Truncate an ACPI Integer to 32 bits if the execution mode is + * 32-bit, as determined by the revision of the DSDT. + * + ******************************************************************************/ + +u8 acpi_ex_truncate_for32bit_table(union acpi_operand_object *obj_desc) +{ + + ACPI_FUNCTION_ENTRY(); + + /* + * Object must be a valid number and we must be executing + * a control method. Object could be NS node for AML_INT_NAMEPATH_OP. + */ + if ((!obj_desc) || + (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) || + (obj_desc->common.type != ACPI_TYPE_INTEGER)) { + return (FALSE); + } + + if ((acpi_gbl_integer_byte_width == 4) && + (obj_desc->integer.value > (u64)ACPI_UINT32_MAX)) { + /* + * We are executing in a 32-bit ACPI table. + * Truncate the value to 32 bits by zeroing out the upper 32-bit field + */ + obj_desc->integer.value &= (u64)ACPI_UINT32_MAX; + return (TRUE); + } + + return (FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_acquire_global_lock + * + * PARAMETERS: field_flags - Flags with Lock rule: + * always_lock or never_lock + * + * RETURN: None + * + * DESCRIPTION: Obtain the ACPI hardware Global Lock, only if the field + * flags specifiy that it is to be obtained before field access. + * + ******************************************************************************/ + +void acpi_ex_acquire_global_lock(u32 field_flags) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ex_acquire_global_lock); + + /* Only use the lock if the always_lock bit is set */ + + if (!(field_flags & AML_FIELD_LOCK_RULE_MASK)) { + return_VOID; + } + + /* Attempt to get the global lock, wait forever */ + + status = acpi_ex_acquire_mutex_object(ACPI_WAIT_FOREVER, + acpi_gbl_global_lock_mutex, + acpi_os_get_thread_id()); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not acquire Global Lock")); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_release_global_lock + * + * PARAMETERS: field_flags - Flags with Lock rule: + * always_lock or never_lock + * + * RETURN: None + * + * DESCRIPTION: Release the ACPI hardware Global Lock + * + ******************************************************************************/ + +void acpi_ex_release_global_lock(u32 field_flags) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ex_release_global_lock); + + /* Only use the lock if the always_lock bit is set */ + + if (!(field_flags & AML_FIELD_LOCK_RULE_MASK)) { + return_VOID; + } + + /* Release the global lock */ + + status = acpi_ex_release_mutex_object(acpi_gbl_global_lock_mutex); + if (ACPI_FAILURE(status)) { + + /* Report the error, but there isn't much else we can do */ + + ACPI_EXCEPTION((AE_INFO, status, + "Could not release Global Lock")); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_digits_needed + * + * PARAMETERS: value - Value to be represented + * base - Base of representation + * + * RETURN: The number of digits. + * + * DESCRIPTION: Calculate the number of digits needed to represent the Value + * in the given Base (Radix) + * + ******************************************************************************/ + +static u32 acpi_ex_digits_needed(u64 value, u32 base) +{ + u32 num_digits; + u64 current_value; + + ACPI_FUNCTION_TRACE(ex_digits_needed); + + /* u64 is unsigned, so we don't worry about a '-' prefix */ + + if (value == 0) { + return_UINT32(1); + } + + current_value = value; + num_digits = 0; + + /* Count the digits in the requested base */ + + while (current_value) { + (void)acpi_ut_short_divide(current_value, base, ¤t_value, + NULL); + num_digits++; + } + + return_UINT32(num_digits); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_eisa_id_to_string + * + * PARAMETERS: compressed_id - EISAID to be converted + * out_string - Where to put the converted string (8 bytes) + * + * RETURN: None + * + * DESCRIPTION: Convert a numeric EISAID to string representation. Return + * buffer must be large enough to hold the string. The string + * returned is always exactly of length ACPI_EISAID_STRING_SIZE + * (includes null terminator). The EISAID is always 32 bits. + * + ******************************************************************************/ + +void acpi_ex_eisa_id_to_string(char *out_string, u64 compressed_id) +{ + u32 swapped_id; + + ACPI_FUNCTION_ENTRY(); + + /* The EISAID should be a 32-bit integer */ + + if (compressed_id > ACPI_UINT32_MAX) { + ACPI_WARNING((AE_INFO, + "Expected EISAID is larger than 32 bits: 0x%8.8X%8.8X, truncating", + ACPI_FORMAT_UINT64(compressed_id))); + } + + /* Swap ID to big-endian to get contiguous bits */ + + swapped_id = acpi_ut_dword_byte_swap((u32)compressed_id); + + /* First 3 bytes are uppercase letters. Next 4 bytes are hexadecimal */ + + out_string[0] = + (char)(0x40 + (((unsigned long)swapped_id >> 26) & 0x1F)); + out_string[1] = (char)(0x40 + ((swapped_id >> 21) & 0x1F)); + out_string[2] = (char)(0x40 + ((swapped_id >> 16) & 0x1F)); + out_string[3] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 12); + out_string[4] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 8); + out_string[5] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 4); + out_string[6] = acpi_ut_hex_to_ascii_char((u64) swapped_id, 0); + out_string[7] = 0; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_integer_to_string + * + * PARAMETERS: out_string - Where to put the converted string. At least + * 21 bytes are needed to hold the largest + * possible 64-bit integer. + * value - Value to be converted + * + * RETURN: None, string + * + * DESCRIPTION: Convert a 64-bit integer to decimal string representation. + * Assumes string buffer is large enough to hold the string. The + * largest string is (ACPI_MAX64_DECIMAL_DIGITS + 1). + * + ******************************************************************************/ + +void acpi_ex_integer_to_string(char *out_string, u64 value) +{ + u32 count; + u32 digits_needed; + u32 remainder; + + ACPI_FUNCTION_ENTRY(); + + digits_needed = acpi_ex_digits_needed(value, 10); + out_string[digits_needed] = 0; + + for (count = digits_needed; count > 0; count--) { + (void)acpi_ut_short_divide(value, 10, &value, &remainder); + out_string[count - 1] = (char)('0' + remainder); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_is_valid_space_id + * + * PARAMETERS: space_id - ID to be validated + * + * RETURN: TRUE if valid/supported ID. + * + * DESCRIPTION: Validate an operation region space_ID. + * + ******************************************************************************/ + +u8 acpi_is_valid_space_id(u8 space_id) +{ + + if ((space_id >= ACPI_NUM_PREDEFINED_REGIONS) && + (space_id < ACPI_USER_REGION_BEGIN) && + (space_id != ACPI_ADR_SPACE_DATA_TABLE) && + (space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) { + return (FALSE); + } + + return (TRUE); +} + +#endif diff --git a/kernel/drivers/acpi/acpica/hwacpi.c b/kernel/drivers/acpi/acpica/hwacpi.c new file mode 100644 index 000000000..e5c5949f9 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwacpi.c @@ -0,0 +1,181 @@ +/****************************************************************************** + * + * Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwacpi") + +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/****************************************************************************** + * + * FUNCTION: acpi_hw_set_mode + * + * PARAMETERS: mode - SYS_MODE_ACPI or SYS_MODE_LEGACY + * + * RETURN: Status + * + * DESCRIPTION: Transitions the system into the requested mode. + * + ******************************************************************************/ +acpi_status acpi_hw_set_mode(u32 mode) +{ + + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_set_mode); + + /* If the Hardware Reduced flag is set, machine is always in acpi mode */ + + if (acpi_gbl_reduced_hardware) { + return_ACPI_STATUS(AE_OK); + } + + /* + * ACPI 2.0 clarified that if SMI_CMD in FADT is zero, + * system does not support mode transition. + */ + if (!acpi_gbl_FADT.smi_command) { + ACPI_ERROR((AE_INFO, + "No SMI_CMD in FADT, mode transition failed")); + return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE); + } + + /* + * ACPI 2.0 clarified the meaning of ACPI_ENABLE and ACPI_DISABLE + * in FADT: If it is zero, enabling or disabling is not supported. + * As old systems may have used zero for mode transition, + * we make sure both the numbers are zero to determine these + * transitions are not supported. + */ + if (!acpi_gbl_FADT.acpi_enable && !acpi_gbl_FADT.acpi_disable) { + ACPI_ERROR((AE_INFO, + "No ACPI mode transition supported in this system " + "(enable/disable both zero)")); + return_ACPI_STATUS(AE_OK); + } + + switch (mode) { + case ACPI_SYS_MODE_ACPI: + + /* BIOS should have disabled ALL fixed and GP events */ + + status = acpi_hw_write_port(acpi_gbl_FADT.smi_command, + (u32) acpi_gbl_FADT.acpi_enable, 8); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Attempting to enable ACPI mode\n")); + break; + + case ACPI_SYS_MODE_LEGACY: + /* + * BIOS should clear all fixed status bits and restore fixed event + * enable bits to default + */ + status = acpi_hw_write_port(acpi_gbl_FADT.smi_command, + (u32)acpi_gbl_FADT.acpi_disable, 8); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Attempting to enable Legacy (non-ACPI) mode\n")); + break; + + default: + + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not write ACPI mode change")); + return_ACPI_STATUS(status); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_get_mode + * + * PARAMETERS: none + * + * RETURN: SYS_MODE_ACPI or SYS_MODE_LEGACY + * + * DESCRIPTION: Return current operating state of system. Determined by + * querying the SCI_EN bit. + * + ******************************************************************************/ + +u32 acpi_hw_get_mode(void) +{ + acpi_status status; + u32 value; + + ACPI_FUNCTION_TRACE(hw_get_mode); + + /* If the Hardware Reduced flag is set, machine is always in acpi mode */ + + if (acpi_gbl_reduced_hardware) { + return_UINT32(ACPI_SYS_MODE_ACPI); + } + + /* + * ACPI 2.0 clarified that if SMI_CMD in FADT is zero, + * system does not support mode transition. + */ + if (!acpi_gbl_FADT.smi_command) { + return_UINT32(ACPI_SYS_MODE_ACPI); + } + + status = acpi_read_bit_register(ACPI_BITREG_SCI_ENABLE, &value); + if (ACPI_FAILURE(status)) { + return_UINT32(ACPI_SYS_MODE_LEGACY); + } + + if (value) { + return_UINT32(ACPI_SYS_MODE_ACPI); + } else { + return_UINT32(ACPI_SYS_MODE_LEGACY); + } +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/hwesleep.c b/kernel/drivers/acpi/acpica/hwesleep.c new file mode 100644 index 000000000..e5599f610 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwesleep.c @@ -0,0 +1,243 @@ +/****************************************************************************** + * + * Name: hwesleep.c - ACPI Hardware Sleep/Wake Support functions for the + * extended FADT-V5 sleep registers. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include +#include "accommon.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwesleep") + +/******************************************************************************* + * + * FUNCTION: acpi_hw_execute_sleep_method + * + * PARAMETERS: method_pathname - Pathname of method to execute + * integer_argument - Argument to pass to the method + * + * RETURN: None + * + * DESCRIPTION: Execute a sleep/wake related method with one integer argument + * and no return value. + * + ******************************************************************************/ +void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument) +{ + struct acpi_object_list arg_list; + union acpi_object arg; + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_execute_sleep_method); + + /* One argument, integer_argument; No return value expected */ + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = (u64)integer_argument; + + status = acpi_evaluate_object(NULL, method_pathname, &arg_list, NULL); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "While executing method %s", + method_pathname)); + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_extended_sleep + * + * PARAMETERS: sleep_state - Which sleep state to enter + * + * RETURN: Status + * + * DESCRIPTION: Enter a system sleep state via the extended FADT sleep + * registers (V5 FADT). + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ + +acpi_status acpi_hw_extended_sleep(u8 sleep_state) +{ + acpi_status status; + u8 sleep_type_value; + u64 sleep_status; + + ACPI_FUNCTION_TRACE(hw_extended_sleep); + + /* Extended sleep registers must be valid */ + + if (!acpi_gbl_FADT.sleep_control.address || + !acpi_gbl_FADT.sleep_status.address) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Clear wake status (WAK_STS) */ + + status = + acpi_write((u64)ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_gbl_system_awake_and_running = FALSE; + + /* Flush caches, as per ACPI specification */ + + ACPI_FLUSH_CPU_CACHE(); + + status = acpi_os_prepare_extended_sleep(sleep_state, + acpi_gbl_sleep_type_a, + acpi_gbl_sleep_type_b); + if (ACPI_SKIP(status)) + return_ACPI_STATUS(AE_OK); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(status); + + /* + * Set the SLP_TYP and SLP_EN bits. + * + * Note: We only use the first value returned by the \_Sx method + * (acpi_gbl_sleep_type_a) - As per ACPI specification. + */ + ACPI_DEBUG_PRINT((ACPI_DB_INIT, + "Entering sleep state [S%u]\n", sleep_state)); + + sleep_type_value = + ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) & + ACPI_X_SLEEP_TYPE_MASK); + + status = acpi_write((u64)(sleep_type_value | ACPI_X_SLEEP_ENABLE), + &acpi_gbl_FADT.sleep_control); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Wait for transition back to Working State */ + + do { + status = acpi_read(&sleep_status, &acpi_gbl_FADT.sleep_status); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + } while (!(((u8)sleep_status) & ACPI_X_WAKE_STATUS)); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_extended_wake_prep + * + * PARAMETERS: sleep_state - Which sleep state we just exited + * + * RETURN: Status + * + * DESCRIPTION: Perform first part of OS-independent ACPI cleanup after + * a sleep. Called with interrupts ENABLED. + * + ******************************************************************************/ + +acpi_status acpi_hw_extended_wake_prep(u8 sleep_state) +{ + acpi_status status; + u8 sleep_type_value; + + ACPI_FUNCTION_TRACE(hw_extended_wake_prep); + + status = acpi_get_sleep_type_data(ACPI_STATE_S0, + &acpi_gbl_sleep_type_a, + &acpi_gbl_sleep_type_b); + if (ACPI_SUCCESS(status)) { + sleep_type_value = + ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) & + ACPI_X_SLEEP_TYPE_MASK); + + (void)acpi_write((u64)(sleep_type_value | ACPI_X_SLEEP_ENABLE), + &acpi_gbl_FADT.sleep_control); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_extended_wake + * + * PARAMETERS: sleep_state - Which sleep state we just exited + * + * RETURN: Status + * + * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep + * Called with interrupts ENABLED. + * + ******************************************************************************/ + +acpi_status acpi_hw_extended_wake(u8 sleep_state) +{ + ACPI_FUNCTION_TRACE(hw_extended_wake); + + /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */ + + acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID; + + /* Execute the wake methods */ + + acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING); + acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state); + + /* + * Some BIOS code assumes that WAK_STS will be cleared on resume + * and use it to determine whether the system is rebooting or + * resuming. Clear WAK_STS for compatibility. + */ + (void)acpi_write((u64)ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status); + acpi_gbl_system_awake_and_running = TRUE; + + acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING); + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/hwgpe.c b/kernel/drivers/acpi/acpica/hwgpe.c new file mode 100644 index 000000000..73cfa5947 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwgpe.c @@ -0,0 +1,535 @@ +/****************************************************************************** + * + * Module Name: hwgpe - Low level GPE enable/disable/clear functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwgpe") +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/* Local prototypes */ +static acpi_status +acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); + +static acpi_status +acpi_hw_gpe_enable_write(u8 enable_mask, + struct acpi_gpe_register_info *gpe_register_info); + +/****************************************************************************** + * + * FUNCTION: acpi_hw_get_gpe_register_bit + * + * PARAMETERS: gpe_event_info - Info block for the GPE + * + * RETURN: Register mask with a one in the GPE bit position + * + * DESCRIPTION: Compute the register mask for this GPE. One bit is set in the + * correct position for the input GPE. + * + ******************************************************************************/ + +u32 acpi_hw_get_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info) +{ + + return ((u32)1 << + (gpe_event_info->gpe_number - + gpe_event_info->register_info->base_gpe_number)); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_low_set_gpe + * + * PARAMETERS: gpe_event_info - Info block for the GPE to be disabled + * action - Enable or disable + * + * RETURN: Status + * + * DESCRIPTION: Enable or disable a single GPE in the parent enable register. + * The enable_mask field of the involved GPE register must be + * updated by the caller if necessary. + * + ******************************************************************************/ + +acpi_status +acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) +{ + struct acpi_gpe_register_info *gpe_register_info; + acpi_status status; + u32 enable_mask; + u32 register_bit; + + ACPI_FUNCTION_ENTRY(); + + /* Get the info block for the entire GPE register */ + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return (AE_NOT_EXIST); + } + + /* Get current value of the enable register that contains this GPE */ + + status = acpi_hw_read(&enable_mask, &gpe_register_info->enable_address); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Set or clear just the bit that corresponds to this GPE */ + + register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); + switch (action) { + case ACPI_GPE_CONDITIONAL_ENABLE: + + /* Only enable if the corresponding enable_mask bit is set */ + + if (!(register_bit & gpe_register_info->enable_mask)) { + return (AE_BAD_PARAMETER); + } + + /*lint -fallthrough */ + + case ACPI_GPE_ENABLE: + + ACPI_SET_BIT(enable_mask, register_bit); + break; + + case ACPI_GPE_DISABLE: + + ACPI_CLEAR_BIT(enable_mask, register_bit); + break; + + default: + + ACPI_ERROR((AE_INFO, "Invalid GPE Action, %u", action)); + return (AE_BAD_PARAMETER); + } + + /* Write the updated enable mask */ + + status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_clear_gpe + * + * PARAMETERS: gpe_event_info - Info block for the GPE to be cleared + * + * RETURN: Status + * + * DESCRIPTION: Clear the status bit for a single GPE. + * + ******************************************************************************/ + +acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) +{ + struct acpi_gpe_register_info *gpe_register_info; + acpi_status status; + u32 register_bit; + + ACPI_FUNCTION_ENTRY(); + + /* Get the info block for the entire GPE register */ + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return (AE_NOT_EXIST); + } + + /* + * Write a one to the appropriate bit in the status register to + * clear this GPE. + */ + register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); + + status = acpi_hw_write(register_bit, + &gpe_register_info->status_address); + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_get_gpe_status + * + * PARAMETERS: gpe_event_info - Info block for the GPE to queried + * event_status - Where the GPE status is returned + * + * RETURN: Status + * + * DESCRIPTION: Return the status of a single GPE. + * + ******************************************************************************/ + +acpi_status +acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, + acpi_event_status *event_status) +{ + u32 in_byte; + u32 register_bit; + struct acpi_gpe_register_info *gpe_register_info; + acpi_event_status local_event_status = 0; + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + if (!event_status) { + return (AE_BAD_PARAMETER); + } + + /* GPE currently handled? */ + + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != + ACPI_GPE_DISPATCH_NONE) { + local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; + } + + /* Get the info block for the entire GPE register */ + + gpe_register_info = gpe_event_info->register_info; + + /* Get the register bitmask for this GPE */ + + register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); + + /* GPE currently enabled? (enabled for runtime?) */ + + if (register_bit & gpe_register_info->enable_for_run) { + local_event_status |= ACPI_EVENT_FLAG_ENABLED; + } + + /* GPE enabled for wake? */ + + if (register_bit & gpe_register_info->enable_for_wake) { + local_event_status |= ACPI_EVENT_FLAG_WAKE_ENABLED; + } + + /* GPE currently enabled (enable bit == 1)? */ + + status = acpi_hw_read(&in_byte, &gpe_register_info->enable_address); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (register_bit & in_byte) { + local_event_status |= ACPI_EVENT_FLAG_ENABLE_SET; + } + + /* GPE currently active (status bit == 1)? */ + + status = acpi_hw_read(&in_byte, &gpe_register_info->status_address); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (register_bit & in_byte) { + local_event_status |= ACPI_EVENT_FLAG_STATUS_SET; + } + + /* Set return value */ + + (*event_status) = local_event_status; + 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; + + gpe_register_info->enable_mask = enable_mask; + status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_disable_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Disable all GPEs within a single GPE block + * + ******************************************************************************/ + +acpi_status +acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) +{ + u32 i; + acpi_status status; + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + + /* Disable all GPEs in this register */ + + status = + acpi_hw_gpe_enable_write(0x00, + &gpe_block->register_info[i]); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_clear_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Clear status bits for all GPEs within a single GPE block + * + ******************************************************************************/ + +acpi_status +acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) +{ + u32 i; + acpi_status status; + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + + /* Clear status on all GPEs in this register */ + + status = + acpi_hw_write(0xFF, + &gpe_block->register_info[i].status_address); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_runtime_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Enable all "runtime" GPEs within a single GPE block. Includes + * combination wake/run GPEs. + * + ******************************************************************************/ + +acpi_status +acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info * gpe_block, + void *context) +{ + u32 i; + acpi_status status; + struct acpi_gpe_register_info *gpe_register_info; + + /* NOTE: assumes that all GPEs are currently disabled */ + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + gpe_register_info = &gpe_block->register_info[i]; + if (!gpe_register_info->enable_for_run) { + continue; + } + + /* Enable all "runtime" GPEs in this register */ + + status = + acpi_hw_gpe_enable_write(gpe_register_info->enable_for_run, + gpe_register_info); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_wakeup_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Enable all "wake" GPEs within a single GPE block. Includes + * combination wake/run GPEs. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context) +{ + u32 i; + acpi_status status; + struct acpi_gpe_register_info *gpe_register_info; + + /* Examine each GPE Register within the block */ + + 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 + * remaining ones. + */ + + status = + acpi_hw_gpe_enable_write(gpe_register_info->enable_for_wake, + gpe_register_info); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_disable_all_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Disable and clear all GPEs in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_hw_disable_all_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_disable_all_gpes); + + status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL); + status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL); + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_all_runtime_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_hw_enable_all_runtime_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_enable_all_runtime_gpes); + + status = acpi_ev_walk_gpe_list(acpi_hw_enable_runtime_gpe_block, NULL); + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_all_wakeup_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Enable all "wakeup" GPEs, in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_hw_enable_all_wakeup_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_enable_all_wakeup_gpes); + + status = acpi_ev_walk_gpe_list(acpi_hw_enable_wakeup_gpe_block, NULL); + return_ACPI_STATUS(status); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/hwpci.c b/kernel/drivers/acpi/acpica/hwpci.c new file mode 100644 index 000000000..c5214dec4 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwpci.c @@ -0,0 +1,421 @@ +/******************************************************************************* + * + * Module Name: hwpci - Obtain PCI bus, device, and function numbers + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("hwpci") + +/* PCI configuration space values */ +#define PCI_CFG_HEADER_TYPE_REG 0x0E +#define PCI_CFG_PRIMARY_BUS_NUMBER_REG 0x18 +#define PCI_CFG_SECONDARY_BUS_NUMBER_REG 0x19 +/* PCI header values */ +#define PCI_HEADER_TYPE_MASK 0x7F +#define PCI_TYPE_BRIDGE 0x01 +#define PCI_TYPE_CARDBUS_BRIDGE 0x02 +typedef struct acpi_pci_device { + acpi_handle device; + struct acpi_pci_device *next; + +} acpi_pci_device; + +/* Local prototypes */ + +static acpi_status +acpi_hw_build_pci_list(acpi_handle root_pci_device, + acpi_handle pci_region, + struct acpi_pci_device **return_list_head); + +static acpi_status +acpi_hw_process_pci_list(struct acpi_pci_id *pci_id, + struct acpi_pci_device *list_head); + +static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head); + +static acpi_status +acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id, + acpi_handle pci_device, + u16 *bus_number, u8 *is_bridge); + +/******************************************************************************* + * + * FUNCTION: acpi_hw_derive_pci_id + * + * PARAMETERS: pci_id - Initial values for the PCI ID. May be + * modified by this function. + * root_pci_device - A handle to a PCI device object. This + * object must be a PCI Root Bridge having a + * _HID value of either PNP0A03 or PNP0A08 + * pci_region - A handle to a PCI configuration space + * Operation Region being initialized + * + * RETURN: Status + * + * DESCRIPTION: This function derives a full PCI ID for a PCI device, + * consisting of a Segment number, Bus number, Device number, + * and function code. + * + * The PCI hardware dynamically configures PCI bus numbers + * depending on the bus topology discovered during system + * initialization. This function is invoked during configuration + * of a PCI_Config Operation Region in order to (possibly) update + * the Bus/Device/Function numbers in the pci_id with the actual + * values as determined by the hardware and operating system + * configuration. + * + * The pci_id parameter is initially populated during the Operation + * Region initialization. This function is then called, and is + * will make any necessary modifications to the Bus, Device, or + * Function number PCI ID subfields as appropriate for the + * current hardware and OS configuration. + * + * NOTE: Created 08/2010. Replaces the previous OSL acpi_os_derive_pci_id + * interface since this feature is OS-independent. This module + * specifically avoids any use of recursion by building a local + * temporary device list. + * + ******************************************************************************/ + +acpi_status +acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id, + acpi_handle root_pci_device, acpi_handle pci_region) +{ + acpi_status status; + struct acpi_pci_device *list_head = NULL; + + ACPI_FUNCTION_TRACE(hw_derive_pci_id); + + if (!pci_id) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Build a list of PCI devices, from pci_region up to root_pci_device */ + + status = + acpi_hw_build_pci_list(root_pci_device, pci_region, &list_head); + if (ACPI_SUCCESS(status)) { + + /* Walk the list, updating the PCI device/function/bus numbers */ + + status = acpi_hw_process_pci_list(pci_id, list_head); + + /* Delete the list */ + + acpi_hw_delete_pci_list(list_head); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_build_pci_list + * + * PARAMETERS: root_pci_device - A handle to a PCI device object. This + * object is guaranteed to be a PCI Root + * Bridge having a _HID value of either + * PNP0A03 or PNP0A08 + * pci_region - A handle to the PCI configuration space + * Operation Region + * return_list_head - Where the PCI device list is returned + * + * RETURN: Status + * + * DESCRIPTION: Builds a list of devices from the input PCI region up to the + * Root PCI device for this namespace subtree. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_build_pci_list(acpi_handle root_pci_device, + acpi_handle pci_region, + struct acpi_pci_device **return_list_head) +{ + acpi_handle current_device; + acpi_handle parent_device; + acpi_status status; + struct acpi_pci_device *list_element; + struct acpi_pci_device *list_head = NULL; + + /* + * Ascend namespace branch until the root_pci_device is reached, building + * a list of device nodes. Loop will exit when either the PCI device is + * found, or the root of the namespace is reached. + */ + current_device = pci_region; + while (1) { + status = acpi_get_parent(current_device, &parent_device); + if (ACPI_FAILURE(status)) { + + /* Must delete the list before exit */ + + acpi_hw_delete_pci_list(*return_list_head); + return (status); + } + + /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */ + + if (parent_device == root_pci_device) { + *return_list_head = list_head; + return (AE_OK); + } + + list_element = ACPI_ALLOCATE(sizeof(struct acpi_pci_device)); + if (!list_element) { + + /* Must delete the list before exit */ + + acpi_hw_delete_pci_list(*return_list_head); + return (AE_NO_MEMORY); + } + + /* Put new element at the head of the list */ + + list_element->next = list_head; + list_element->device = parent_device; + list_head = list_element; + + current_device = parent_device; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_process_pci_list + * + * PARAMETERS: pci_id - Initial values for the PCI ID. May be + * modified by this function. + * list_head - Device list created by + * acpi_hw_build_pci_list + * + * RETURN: Status + * + * DESCRIPTION: Walk downward through the PCI device list, getting the device + * info for each, via the PCI configuration space and updating + * the PCI ID as necessary. Deletes the list during traversal. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_process_pci_list(struct acpi_pci_id *pci_id, + struct acpi_pci_device *list_head) +{ + acpi_status status = AE_OK; + struct acpi_pci_device *info; + u16 bus_number; + u8 is_bridge = TRUE; + + ACPI_FUNCTION_NAME(hw_process_pci_list); + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Input PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X\n", + pci_id->segment, pci_id->bus, pci_id->device, + pci_id->function)); + + bus_number = pci_id->bus; + + /* + * Descend down the namespace tree, collecting PCI device, function, + * and bus numbers. bus_number is only important for PCI bridges. + * Algorithm: As we descend the tree, use the last valid PCI device, + * function, and bus numbers that are discovered, and assign them + * to the PCI ID for the target device. + */ + info = list_head; + while (info) { + status = acpi_hw_get_pci_device_info(pci_id, info->device, + &bus_number, &is_bridge); + if (ACPI_FAILURE(status)) { + return (status); + } + + info = info->next; + } + + ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, + "Output PciId: Seg %4.4X Bus %4.4X Dev %4.4X Func %4.4X " + "Status %X BusNumber %X IsBridge %X\n", + pci_id->segment, pci_id->bus, pci_id->device, + pci_id->function, status, bus_number, is_bridge)); + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_delete_pci_list + * + * PARAMETERS: list_head - Device list created by + * acpi_hw_build_pci_list + * + * RETURN: None + * + * DESCRIPTION: Free the entire PCI list. + * + ******************************************************************************/ + +static void acpi_hw_delete_pci_list(struct acpi_pci_device *list_head) +{ + struct acpi_pci_device *next; + struct acpi_pci_device *previous; + + next = list_head; + while (next) { + previous = next; + next = previous->next; + ACPI_FREE(previous); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_get_pci_device_info + * + * PARAMETERS: pci_id - Initial values for the PCI ID. May be + * modified by this function. + * pci_device - Handle for the PCI device object + * bus_number - Where a PCI bridge bus number is returned + * is_bridge - Return value, indicates if this PCI + * device is a PCI bridge + * + * RETURN: Status + * + * DESCRIPTION: Get the device info for a single PCI device object. Get the + * _ADR (contains PCI device and function numbers), and for PCI + * bridge devices, get the bus number from PCI configuration + * space. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_get_pci_device_info(struct acpi_pci_id *pci_id, + acpi_handle pci_device, + u16 *bus_number, u8 *is_bridge) +{ + acpi_status status; + acpi_object_type object_type; + u64 return_value; + u64 pci_value; + + /* We only care about objects of type Device */ + + status = acpi_get_type(pci_device, &object_type); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (object_type != ACPI_TYPE_DEVICE) { + return (AE_OK); + } + + /* We need an _ADR. Ignore device if not present */ + + status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, + pci_device, &return_value); + if (ACPI_FAILURE(status)) { + return (AE_OK); + } + + /* + * From _ADR, get the PCI Device and Function and + * update the PCI ID. + */ + pci_id->device = ACPI_HIWORD(ACPI_LODWORD(return_value)); + pci_id->function = ACPI_LOWORD(ACPI_LODWORD(return_value)); + + /* + * If the previous device was a bridge, use the previous + * device bus number + */ + if (*is_bridge) { + pci_id->bus = *bus_number; + } + + /* + * Get the bus numbers from PCI Config space: + * + * First, get the PCI header_type + */ + *is_bridge = FALSE; + status = acpi_os_read_pci_configuration(pci_id, + PCI_CFG_HEADER_TYPE_REG, + &pci_value, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* We only care about bridges (1=pci_bridge, 2=card_bus_bridge) */ + + pci_value &= PCI_HEADER_TYPE_MASK; + + if ((pci_value != PCI_TYPE_BRIDGE) && + (pci_value != PCI_TYPE_CARDBUS_BRIDGE)) { + return (AE_OK); + } + + /* Bridge: Get the Primary bus_number */ + + status = acpi_os_read_pci_configuration(pci_id, + PCI_CFG_PRIMARY_BUS_NUMBER_REG, + &pci_value, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + + *is_bridge = TRUE; + pci_id->bus = (u16)pci_value; + + /* Bridge: Get the Secondary bus_number */ + + status = acpi_os_read_pci_configuration(pci_id, + PCI_CFG_SECONDARY_BUS_NUMBER_REG, + &pci_value, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + + *bus_number = (u16)pci_value; + return (AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/hwregs.c b/kernel/drivers/acpi/acpica/hwregs.c new file mode 100644 index 000000000..dc32e7213 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwregs.c @@ -0,0 +1,668 @@ +/******************************************************************************* + * + * Module Name: hwregs - Read/write access functions for the various ACPI + * control and status registers. + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwregs") + +#if (!ACPI_REDUCED_HARDWARE) +/* Local Prototypes */ +static acpi_status +acpi_hw_read_multiple(u32 *value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b); + +static acpi_status +acpi_hw_write_multiple(u32 value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b); + +#endif /* !ACPI_REDUCED_HARDWARE */ + +/****************************************************************************** + * + * FUNCTION: acpi_hw_validate_register + * + * PARAMETERS: reg - GAS register structure + * max_bit_width - Max bit_width supported (32 or 64) + * address - Pointer to where the gas->address + * is returned + * + * RETURN: Status + * + * DESCRIPTION: Validate the contents of a GAS register. Checks the GAS + * pointer, Address, space_id, bit_width, and bit_offset. + * + ******************************************************************************/ + +acpi_status +acpi_hw_validate_register(struct acpi_generic_address *reg, + u8 max_bit_width, u64 *address) +{ + + /* Must have a valid pointer to a GAS structure */ + + if (!reg) { + return (AE_BAD_PARAMETER); + } + + /* + * Copy the target address. This handles possible alignment issues. + * Address must not be null. A null address also indicates an optional + * ACPI register that is not supported, so no error message. + */ + ACPI_MOVE_64_TO_64(address, ®->address); + if (!(*address)) { + return (AE_BAD_ADDRESS); + } + + /* Validate the space_ID */ + + if ((reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + ACPI_ERROR((AE_INFO, + "Unsupported address space: 0x%X", reg->space_id)); + return (AE_SUPPORT); + } + + /* Validate the bit_width */ + + if ((reg->bit_width != 8) && + (reg->bit_width != 16) && + (reg->bit_width != 32) && (reg->bit_width != max_bit_width)) { + ACPI_ERROR((AE_INFO, + "Unsupported register bit width: 0x%X", + reg->bit_width)); + return (AE_SUPPORT); + } + + /* Validate the bit_offset. Just a warning for now. */ + + if (reg->bit_offset != 0) { + ACPI_WARNING((AE_INFO, + "Unsupported register bit offset: 0x%X", + reg->bit_offset)); + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_read + * + * PARAMETERS: value - Where the value is returned + * reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Read from either memory or IO space. This is a 32-bit max + * version of acpi_read, used internally since the overhead of + * 64-bit values is not needed. + * + * LIMITATIONS: + * bit_width must be exactly 8, 16, or 32. + * space_ID must be system_memory or system_IO. + * bit_offset and access_width are currently ignored, as there has + * not been a need to implement these. + * + ******************************************************************************/ + +acpi_status acpi_hw_read(u32 *value, struct acpi_generic_address *reg) +{ + u64 address; + u64 value64; + acpi_status status; + + ACPI_FUNCTION_NAME(hw_read); + + /* Validate contents of the GAS register */ + + status = acpi_hw_validate_register(reg, 32, &address); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Initialize entire 32-bit return value to zero */ + + *value = 0; + + /* + * Two address spaces supported: Memory or IO. PCI_Config is + * not supported here because the GAS structure is insufficient + */ + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_read_memory((acpi_physical_address) + address, &value64, reg->bit_width); + + *value = (u32)value64; + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + + status = acpi_hw_read_port((acpi_io_address) + address, value, reg->bit_width); + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", + *value, reg->bit_width, ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_write + * + * PARAMETERS: value - Value to be written + * reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Write to either memory or IO space. This is a 32-bit max + * version of acpi_write, used internally since the overhead of + * 64-bit values is not needed. + * + ******************************************************************************/ + +acpi_status acpi_hw_write(u32 value, struct acpi_generic_address *reg) +{ + u64 address; + acpi_status status; + + ACPI_FUNCTION_NAME(hw_write); + + /* Validate contents of the GAS register */ + + status = acpi_hw_validate_register(reg, 32, &address); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Two address spaces supported: Memory or IO. PCI_Config is + * not supported here because the GAS structure is insufficient + */ + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_write_memory((acpi_physical_address) + address, (u64)value, + reg->bit_width); + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + + status = acpi_hw_write_port((acpi_io_address) + address, value, reg->bit_width); + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", + value, reg->bit_width, ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); + + return (status); +} + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_hw_clear_acpi_status + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Clears all fixed and general purpose status bits + * + ******************************************************************************/ + +acpi_status acpi_hw_clear_acpi_status(void) +{ + acpi_status status; + acpi_cpu_flags lock_flags = 0; + + ACPI_FUNCTION_TRACE(hw_clear_acpi_status); + + ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n", + ACPI_BITMASK_ALL_FIXED_STATUS, + ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address))); + + raw_spin_lock_irqsave(acpi_gbl_hardware_lock, lock_flags); + + /* Clear the fixed events in PM1 A/B */ + + status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, + ACPI_BITMASK_ALL_FIXED_STATUS); + + raw_spin_unlock_irqrestore(acpi_gbl_hardware_lock, lock_flags); + + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Clear the GPE Bits in all GPE registers in all GPE blocks */ + + status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL); + +exit: + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_get_bit_register_info + * + * PARAMETERS: register_id - Index of ACPI Register to access + * + * RETURN: The bitmask to be used when accessing the register + * + * DESCRIPTION: Map register_id into a register bitmask. + * + ******************************************************************************/ + +struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) +{ + ACPI_FUNCTION_ENTRY(); + + if (register_id > ACPI_BITREG_MAX) { + ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: 0x%X", + register_id)); + return (NULL); + } + + return (&acpi_gbl_bit_register_info[register_id]); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_write_pm1_control + * + * PARAMETERS: pm1a_control - Value to be written to PM1A control + * pm1b_control - Value to be written to PM1B control + * + * RETURN: Status + * + * DESCRIPTION: Write the PM1 A/B control registers. These registers are + * different than than the PM1 A/B status and enable registers + * in that different values can be written to the A/B registers. + * Most notably, the SLP_TYP bits can be different, as per the + * values returned from the _Sx predefined methods. + * + ******************************************************************************/ + +acpi_status acpi_hw_write_pm1_control(u32 pm1a_control, u32 pm1b_control) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_write_pm1_control); + + status = + acpi_hw_write(pm1a_control, &acpi_gbl_FADT.xpm1a_control_block); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (acpi_gbl_FADT.xpm1b_control_block.address) { + status = + acpi_hw_write(pm1b_control, + &acpi_gbl_FADT.xpm1b_control_block); + } + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_register_read + * + * PARAMETERS: register_id - ACPI Register ID + * return_value - Where the register value is returned + * + * RETURN: Status and the value read. + * + * DESCRIPTION: Read from the specified ACPI register + * + ******************************************************************************/ +acpi_status acpi_hw_register_read(u32 register_id, u32 *return_value) +{ + u32 value = 0; + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_register_read); + + switch (register_id) { + case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */ + + status = acpi_hw_read_multiple(&value, + &acpi_gbl_xpm1a_status, + &acpi_gbl_xpm1b_status); + break; + + case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */ + + status = acpi_hw_read_multiple(&value, + &acpi_gbl_xpm1a_enable, + &acpi_gbl_xpm1b_enable); + break; + + case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */ + + status = acpi_hw_read_multiple(&value, + &acpi_gbl_FADT. + xpm1a_control_block, + &acpi_gbl_FADT. + xpm1b_control_block); + + /* + * Zero the write-only bits. From the ACPI specification, "Hardware + * Write-Only Bits": "Upon reads to registers with write-only bits, + * software masks out all write-only bits." + */ + value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS; + break; + + case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ + + status = + acpi_hw_read(&value, &acpi_gbl_FADT.xpm2_control_block); + break; + + case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ + + status = acpi_hw_read(&value, &acpi_gbl_FADT.xpm_timer_block); + break; + + case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + + status = + acpi_hw_read_port(acpi_gbl_FADT.smi_command, &value, 8); + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id)); + status = AE_BAD_PARAMETER; + break; + } + + if (ACPI_SUCCESS(status)) { + *return_value = value; + } + + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_register_write + * + * PARAMETERS: register_id - ACPI Register ID + * value - The value to write + * + * RETURN: Status + * + * DESCRIPTION: Write to the specified ACPI register + * + * NOTE: In accordance with the ACPI specification, this function automatically + * preserves the value of the following bits, meaning that these bits cannot be + * changed via this interface: + * + * PM1_CONTROL[0] = SCI_EN + * PM1_CONTROL[9] + * PM1_STATUS[11] + * + * ACPI References: + * 1) Hardware Ignored Bits: When software writes to a register with ignored + * bit fields, it preserves the ignored bit fields + * 2) SCI_EN: OSPM always preserves this bit position + * + ******************************************************************************/ + +acpi_status acpi_hw_register_write(u32 register_id, u32 value) +{ + acpi_status status; + u32 read_value; + + ACPI_FUNCTION_TRACE(hw_register_write); + + switch (register_id) { + case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */ + /* + * Handle the "ignored" bit in PM1 Status. According to the ACPI + * specification, ignored bits are to be preserved when writing. + * Normally, this would mean a read/modify/write sequence. However, + * preserving a bit in the status register is different. Writing a + * one clears the status, and writing a zero preserves the status. + * Therefore, we must always write zero to the ignored bit. + * + * This behavior is clarified in the ACPI 4.0 specification. + */ + value &= ~ACPI_PM1_STATUS_PRESERVED_BITS; + + status = acpi_hw_write_multiple(value, + &acpi_gbl_xpm1a_status, + &acpi_gbl_xpm1b_status); + break; + + case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */ + + status = acpi_hw_write_multiple(value, + &acpi_gbl_xpm1a_enable, + &acpi_gbl_xpm1b_enable); + break; + + case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */ + /* + * Perform a read first to preserve certain bits (per ACPI spec) + * Note: This includes SCI_EN, we never want to change this bit + */ + status = acpi_hw_read_multiple(&read_value, + &acpi_gbl_FADT. + xpm1a_control_block, + &acpi_gbl_FADT. + xpm1b_control_block); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Insert the bits to be preserved */ + + ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS, + read_value); + + /* Now we can write the data */ + + status = acpi_hw_write_multiple(value, + &acpi_gbl_FADT. + xpm1a_control_block, + &acpi_gbl_FADT. + xpm1b_control_block); + break; + + case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ + /* + * For control registers, all reserved bits must be preserved, + * as per the ACPI spec. + */ + status = + acpi_hw_read(&read_value, + &acpi_gbl_FADT.xpm2_control_block); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Insert the bits to be preserved */ + + ACPI_INSERT_BITS(value, ACPI_PM2_CONTROL_PRESERVED_BITS, + read_value); + + status = + acpi_hw_write(value, &acpi_gbl_FADT.xpm2_control_block); + break; + + case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ + + status = acpi_hw_write(value, &acpi_gbl_FADT.xpm_timer_block); + break; + + case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + + /* SMI_CMD is currently always in IO space */ + + status = + acpi_hw_write_port(acpi_gbl_FADT.smi_command, value, 8); + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id)); + status = AE_BAD_PARAMETER; + break; + } + +exit: + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_read_multiple + * + * PARAMETERS: value - Where the register value is returned + * register_a - First ACPI register (required) + * register_b - Second ACPI register (optional) + * + * RETURN: Status + * + * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B) + * + ******************************************************************************/ + +static acpi_status +acpi_hw_read_multiple(u32 *value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b) +{ + u32 value_a = 0; + u32 value_b = 0; + acpi_status status; + + /* The first register is always required */ + + status = acpi_hw_read(&value_a, register_a); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Second register is optional */ + + if (register_b->address) { + status = acpi_hw_read(&value_b, register_b); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + /* + * OR the two return values together. No shifting or masking is necessary, + * because of how the PM1 registers are defined in the ACPI specification: + * + * "Although the bits can be split between the two register blocks (each + * register block has a unique pointer within the FADT), the bit positions + * are maintained. The register block with unimplemented bits (that is, + * those implemented in the other register block) always returns zeros, + * and writes have no side effects" + */ + *value = (value_a | value_b); + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_write_multiple + * + * PARAMETERS: value - The value to write + * register_a - First ACPI register (required) + * register_b - Second ACPI register (optional) + * + * RETURN: Status + * + * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B) + * + ******************************************************************************/ + +static acpi_status +acpi_hw_write_multiple(u32 value, + struct acpi_generic_address *register_a, + struct acpi_generic_address *register_b) +{ + acpi_status status; + + /* The first register is always required */ + + status = acpi_hw_write(value, register_a); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Second register is optional + * + * No bit shifting or clearing is necessary, because of how the PM1 + * registers are defined in the ACPI specification: + * + * "Although the bits can be split between the two register blocks (each + * register block has a unique pointer within the FADT), the bit positions + * are maintained. The register block with unimplemented bits (that is, + * those implemented in the other register block) always returns zeros, + * and writes have no side effects" + */ + if (register_b->address) { + status = acpi_hw_write(value, register_b); + } + + return (status); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/hwsleep.c b/kernel/drivers/acpi/acpica/hwsleep.c new file mode 100644 index 000000000..7d21cae6d --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwsleep.c @@ -0,0 +1,345 @@ +/****************************************************************************** + * + * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the + * original/legacy sleep/PM registers. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include +#include "accommon.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwsleep") + +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/******************************************************************************* + * + * FUNCTION: acpi_hw_legacy_sleep + * + * PARAMETERS: sleep_state - Which sleep state to enter + * + * RETURN: Status + * + * DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ +acpi_status acpi_hw_legacy_sleep(u8 sleep_state) +{ + struct acpi_bit_register_info *sleep_type_reg_info; + struct acpi_bit_register_info *sleep_enable_reg_info; + u32 pm1a_control; + u32 pm1b_control; + u32 in_value; + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_legacy_sleep); + + sleep_type_reg_info = + acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE); + sleep_enable_reg_info = + acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE); + + /* Clear wake status */ + + status = + acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Clear all fixed and general purpose status bits */ + + status = acpi_hw_clear_acpi_status(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * 1) Disable/Clear all GPEs + * 2) Enable all wakeup GPEs + */ + status = acpi_hw_disable_all_gpes(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + acpi_gbl_system_awake_and_running = FALSE; + + status = acpi_hw_enable_all_wakeup_gpes(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get current value of PM1A control */ + + status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, + &pm1a_control); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + ACPI_DEBUG_PRINT((ACPI_DB_INIT, + "Entering sleep state [S%u]\n", sleep_state)); + + /* Clear the SLP_EN and SLP_TYP fields */ + + pm1a_control &= ~(sleep_type_reg_info->access_bit_mask | + sleep_enable_reg_info->access_bit_mask); + pm1b_control = pm1a_control; + + /* Insert the SLP_TYP bits */ + + pm1a_control |= + (acpi_gbl_sleep_type_a << sleep_type_reg_info->bit_position); + pm1b_control |= + (acpi_gbl_sleep_type_b << sleep_type_reg_info->bit_position); + + /* + * We split the writes of SLP_TYP and SLP_EN to workaround + * poorly implemented hardware. + */ + + /* Write #1: write the SLP_TYP data to the PM1 Control registers */ + + status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Insert the sleep enable (SLP_EN) bit */ + + pm1a_control |= sleep_enable_reg_info->access_bit_mask; + pm1b_control |= sleep_enable_reg_info->access_bit_mask; + + /* Flush caches, as per ACPI specification */ + + ACPI_FLUSH_CPU_CACHE(); + + status = acpi_os_prepare_sleep(sleep_state, pm1a_control, + pm1b_control); + if (ACPI_SKIP(status)) + return_ACPI_STATUS(AE_OK); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(status); + /* Write #2: Write both SLP_TYP + SLP_EN */ + + status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (sleep_state > ACPI_STATE_S3) { + /* + * We wanted to sleep > S3, but it didn't happen (by virtue of the + * fact that we are still executing!) + * + * Wait ten seconds, then try again. This is to get S4/S5 to work on + * all machines. + * + * We wait so long to allow chipsets that poll this reg very slowly + * to still read the right value. Ideally, this block would go + * away entirely. + */ + acpi_os_stall(10 * ACPI_USEC_PER_SEC); + + status = acpi_hw_register_write(ACPI_REGISTER_PM1_CONTROL, + sleep_enable_reg_info-> + access_bit_mask); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* Wait for transition back to Working State */ + + do { + status = + acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + } while (!in_value); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_legacy_wake_prep + * + * PARAMETERS: sleep_state - Which sleep state we just exited + * + * RETURN: Status + * + * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a + * sleep. + * Called with interrupts ENABLED. + * + ******************************************************************************/ + +acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state) +{ + acpi_status status; + struct acpi_bit_register_info *sleep_type_reg_info; + struct acpi_bit_register_info *sleep_enable_reg_info; + u32 pm1a_control; + u32 pm1b_control; + + ACPI_FUNCTION_TRACE(hw_legacy_wake_prep); + + /* + * Set SLP_TYPE and SLP_EN to state S0. + * This is unclear from the ACPI Spec, but it is required + * by some machines. + */ + status = acpi_get_sleep_type_data(ACPI_STATE_S0, + &acpi_gbl_sleep_type_a, + &acpi_gbl_sleep_type_b); + if (ACPI_SUCCESS(status)) { + sleep_type_reg_info = + acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE); + sleep_enable_reg_info = + acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE); + + /* Get current value of PM1A control */ + + status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, + &pm1a_control); + if (ACPI_SUCCESS(status)) { + + /* Clear the SLP_EN and SLP_TYP fields */ + + pm1a_control &= ~(sleep_type_reg_info->access_bit_mask | + sleep_enable_reg_info-> + access_bit_mask); + pm1b_control = pm1a_control; + + /* Insert the SLP_TYP bits */ + + pm1a_control |= (acpi_gbl_sleep_type_a << + sleep_type_reg_info->bit_position); + pm1b_control |= (acpi_gbl_sleep_type_b << + sleep_type_reg_info->bit_position); + + /* Write the control registers and ignore any errors */ + + (void)acpi_hw_write_pm1_control(pm1a_control, + pm1b_control); + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_legacy_wake + * + * PARAMETERS: sleep_state - Which sleep state we just exited + * + * RETURN: Status + * + * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep + * Called with interrupts ENABLED. + * + ******************************************************************************/ + +acpi_status acpi_hw_legacy_wake(u8 sleep_state) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_legacy_wake); + + /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */ + + acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID; + acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING); + + /* + * GPEs must be enabled before _WAK is called as GPEs + * might get fired there + * + * Restore the GPEs: + * 1) Disable/Clear all GPEs + * 2) Enable all runtime GPEs + */ + status = acpi_hw_disable_all_gpes(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_enable_all_runtime_gpes(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Now we can execute _WAK, etc. Some machines require that the GPEs + * are enabled before the wake methods are executed. + */ + acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state); + + /* + * Some BIOS code assumes that WAK_STS will be cleared on resume + * and use it to determine whether the system is rebooting or + * resuming. Clear WAK_STS for compatibility. + */ + (void)acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, + ACPI_CLEAR_STATUS); + acpi_gbl_system_awake_and_running = TRUE; + + /* Enable power button */ + + (void) + acpi_write_bit_register(acpi_gbl_fixed_event_info + [ACPI_EVENT_POWER_BUTTON]. + enable_register_id, ACPI_ENABLE_EVENT); + + (void) + acpi_write_bit_register(acpi_gbl_fixed_event_info + [ACPI_EVENT_POWER_BUTTON]. + status_register_id, ACPI_CLEAR_STATUS); + + acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING); + return_ACPI_STATUS(status); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/hwtimer.c b/kernel/drivers/acpi/acpica/hwtimer.c new file mode 100644 index 000000000..675c709a3 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwtimer.c @@ -0,0 +1,202 @@ +/****************************************************************************** + * + * Name: hwtimer.c - ACPI Power Management Timer Interface + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwtimer") + +#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ +/****************************************************************************** + * + * FUNCTION: acpi_get_timer_resolution + * + * PARAMETERS: resolution - Where the resolution is returned + * + * RETURN: Status and timer resolution + * + * DESCRIPTION: Obtains resolution of the ACPI PM Timer (24 or 32 bits). + * + ******************************************************************************/ +acpi_status acpi_get_timer_resolution(u32 * resolution) +{ + ACPI_FUNCTION_TRACE(acpi_get_timer_resolution); + + if (!resolution) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if ((acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER) == 0) { + *resolution = 24; + } else { + *resolution = 32; + } + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_get_timer_resolution) + +/****************************************************************************** + * + * FUNCTION: acpi_get_timer + * + * PARAMETERS: ticks - Where the timer value is returned + * + * RETURN: Status and current timer value (ticks) + * + * DESCRIPTION: Obtains current value of ACPI PM Timer (in ticks). + * + ******************************************************************************/ +acpi_status acpi_get_timer(u32 * ticks) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_get_timer); + + if (!ticks) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* ACPI 5.0A: PM Timer is optional */ + + if (!acpi_gbl_FADT.xpm_timer_block.address) { + return_ACPI_STATUS(AE_SUPPORT); + } + + status = acpi_hw_read(ticks, &acpi_gbl_FADT.xpm_timer_block); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_timer) + +/****************************************************************************** + * + * FUNCTION: acpi_get_timer_duration + * + * PARAMETERS: start_ticks - Starting timestamp + * end_ticks - End timestamp + * time_elapsed - Where the elapsed time is returned + * + * RETURN: Status and time_elapsed + * + * DESCRIPTION: Computes the time elapsed (in microseconds) between two + * PM Timer time stamps, taking into account the possibility of + * rollovers, the timer resolution, and timer frequency. + * + * The PM Timer's clock ticks at roughly 3.6 times per + * _microsecond_, and its clock continues through Cx state + * transitions (unlike many CPU timestamp counters) -- making it + * a versatile and accurate timer. + * + * Note that this function accommodates only a single timer + * rollover. Thus for 24-bit timers, this function should only + * be used for calculating durations less than ~4.6 seconds + * (~20 minutes for 32-bit timers) -- calculations below: + * + * 2**24 Ticks / 3,600,000 Ticks/Sec = 4.66 sec + * 2**32 Ticks / 3,600,000 Ticks/Sec = 1193 sec or 19.88 minutes + * + ******************************************************************************/ +acpi_status +acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 * time_elapsed) +{ + acpi_status status; + u32 delta_ticks; + u64 quotient; + + ACPI_FUNCTION_TRACE(acpi_get_timer_duration); + + if (!time_elapsed) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* ACPI 5.0A: PM Timer is optional */ + + if (!acpi_gbl_FADT.xpm_timer_block.address) { + return_ACPI_STATUS(AE_SUPPORT); + } + + /* + * Compute Tick Delta: + * Handle (max one) timer rollovers on 24-bit versus 32-bit timers. + */ + if (start_ticks < end_ticks) { + delta_ticks = end_ticks - start_ticks; + } else if (start_ticks > end_ticks) { + if ((acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER) == 0) { + + /* 24-bit Timer */ + + delta_ticks = + (((0x00FFFFFF - start_ticks) + + end_ticks) & 0x00FFFFFF); + } else { + /* 32-bit Timer */ + + delta_ticks = (0xFFFFFFFF - start_ticks) + end_ticks; + } + } else { /* start_ticks == end_ticks */ + + *time_elapsed = 0; + return_ACPI_STATUS(AE_OK); + } + + /* + * Compute Duration (Requires a 64-bit multiply and divide): + * + * time_elapsed (microseconds) = + * (delta_ticks * ACPI_USEC_PER_SEC) / ACPI_PM_TIMER_FREQUENCY; + */ + status = acpi_ut_short_divide(((u64)delta_ticks) * ACPI_USEC_PER_SEC, + ACPI_PM_TIMER_FREQUENCY, "ient, NULL); + + *time_elapsed = (u32) quotient; + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_timer_duration) +#endif /* !ACPI_REDUCED_HARDWARE */ diff --git a/kernel/drivers/acpi/acpica/hwvalid.c b/kernel/drivers/acpi/acpica/hwvalid.c new file mode 100644 index 000000000..29033d714 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwvalid.c @@ -0,0 +1,328 @@ +/****************************************************************************** + * + * Module Name: hwvalid - I/O request validation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwvalid") + +/* Local prototypes */ +static acpi_status +acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width); + +/* + * Protected I/O ports. Some ports are always illegal, and some are + * conditionally illegal. This table must remain ordered by port address. + * + * The table is used to implement the Microsoft port access rules that + * first appeared in Windows XP. Some ports are always illegal, and some + * ports are only illegal if the BIOS calls _OSI with a win_XP string or + * later (meaning that the BIOS itelf is post-XP.) + * + * This provides ACPICA with the desired port protections and + * Microsoft compatibility. + * + * Description of port entries: + * DMA: DMA controller + * PIC0: Programmable Interrupt Controller (8259A) + * PIT1: System Timer 1 + * PIT2: System Timer 2 failsafe + * RTC: Real-time clock + * CMOS: Extended CMOS + * DMA1: DMA 1 page registers + * DMA1L: DMA 1 Ch 0 low page + * DMA2: DMA 2 page registers + * DMA2L: DMA 2 low page refresh + * ARBC: Arbitration control + * SETUP: Reserved system board setup + * POS: POS channel select + * PIC1: Cascaded PIC + * IDMA: ISA DMA + * ELCR: PIC edge/level registers + * PCI: PCI configuration space + */ +static const struct acpi_port_info acpi_protected_ports[] = { + {"DMA", 0x0000, 0x000F, ACPI_OSI_WIN_XP}, + {"PIC0", 0x0020, 0x0021, ACPI_ALWAYS_ILLEGAL}, + {"PIT1", 0x0040, 0x0043, ACPI_OSI_WIN_XP}, + {"PIT2", 0x0048, 0x004B, ACPI_OSI_WIN_XP}, + {"RTC", 0x0070, 0x0071, ACPI_OSI_WIN_XP}, + {"CMOS", 0x0074, 0x0076, ACPI_OSI_WIN_XP}, + {"DMA1", 0x0081, 0x0083, ACPI_OSI_WIN_XP}, + {"DMA1L", 0x0087, 0x0087, ACPI_OSI_WIN_XP}, + {"DMA2", 0x0089, 0x008B, ACPI_OSI_WIN_XP}, + {"DMA2L", 0x008F, 0x008F, ACPI_OSI_WIN_XP}, + {"ARBC", 0x0090, 0x0091, ACPI_OSI_WIN_XP}, + {"SETUP", 0x0093, 0x0094, ACPI_OSI_WIN_XP}, + {"POS", 0x0096, 0x0097, ACPI_OSI_WIN_XP}, + {"PIC1", 0x00A0, 0x00A1, ACPI_ALWAYS_ILLEGAL}, + {"IDMA", 0x00C0, 0x00DF, ACPI_OSI_WIN_XP}, + {"ELCR", 0x04D0, 0x04D1, ACPI_ALWAYS_ILLEGAL}, + {"PCI", 0x0CF8, 0x0CFF, ACPI_OSI_WIN_XP} +}; + +#define ACPI_PORT_INFO_ENTRIES ACPI_ARRAY_LENGTH (acpi_protected_ports) + +/****************************************************************************** + * + * FUNCTION: acpi_hw_validate_io_request + * + * PARAMETERS: Address Address of I/O port/register + * bit_width Number of bits (8,16,32) + * + * RETURN: Status + * + * DESCRIPTION: Validates an I/O request (address/length). Certain ports are + * always illegal and some ports are only illegal depending on + * the requests the BIOS AML code makes to the predefined + * _OSI method. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) +{ + u32 i; + u32 byte_width; + acpi_io_address last_address; + const struct acpi_port_info *port_info; + + ACPI_FUNCTION_TRACE(hw_validate_io_request); + + /* Supported widths are 8/16/32 */ + + if ((bit_width != 8) && (bit_width != 16) && (bit_width != 32)) { + ACPI_ERROR((AE_INFO, + "Bad BitWidth parameter: %8.8X", bit_width)); + return (AE_BAD_PARAMETER); + } + + port_info = acpi_protected_ports; + byte_width = ACPI_DIV_8(bit_width); + last_address = address + byte_width - 1; + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X", + ACPI_FORMAT_UINT64(address), + ACPI_FORMAT_UINT64(last_address), byte_width)); + + /* Maximum 16-bit address in I/O space */ + + if (last_address > ACPI_UINT16_MAX) { + ACPI_ERROR((AE_INFO, + "Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X", + ACPI_FORMAT_UINT64(address), byte_width)); + return_ACPI_STATUS(AE_LIMIT); + } + + /* Exit if requested address is not within the protected port table */ + + if (address > acpi_protected_ports[ACPI_PORT_INFO_ENTRIES - 1].end) { + return_ACPI_STATUS(AE_OK); + } + + /* Check request against the list of protected I/O ports */ + + for (i = 0; i < ACPI_PORT_INFO_ENTRIES; i++, port_info++) { + /* + * Check if the requested address range will write to a reserved + * port. Four cases to consider: + * + * 1) Address range is contained completely in the port address range + * 2) Address range overlaps port range at the port range start + * 3) Address range overlaps port range at the port range end + * 4) Address range completely encompasses the port range + */ + if ((address <= port_info->end) + && (last_address >= port_info->start)) { + + /* Port illegality may depend on the _OSI calls made by the BIOS */ + + if (acpi_gbl_osi_data >= port_info->osi_dependency) { + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)", + ACPI_FORMAT_UINT64(address), + byte_width, port_info->name, + port_info->start, + port_info->end)); + + return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS); + } + } + + /* Finished if address range ends before the end of this port */ + + if (last_address <= port_info->end) { + break; + } + } + + return_ACPI_STATUS(AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_read_port + * + * PARAMETERS: Address Address of I/O port/register to read + * Value Where value is placed + * Width Number of bits + * + * RETURN: Status and value read from port + * + * DESCRIPTION: Read data from an I/O port or register. This is a front-end + * to acpi_os_read_port that performs validation on both the port + * address and the length. + * + *****************************************************************************/ + +acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width) +{ + acpi_status status; + u32 one_byte; + u32 i; + + /* Truncate address to 16 bits if requested */ + + if (acpi_gbl_truncate_io_addresses) { + address &= ACPI_UINT16_MAX; + } + + /* Validate the entire request and perform the I/O */ + + status = acpi_hw_validate_io_request(address, width); + if (ACPI_SUCCESS(status)) { + status = acpi_os_read_port(address, value, width); + return (status); + } + + if (status != AE_AML_ILLEGAL_ADDRESS) { + return (status); + } + + /* + * There has been a protection violation within the request. Fall + * back to byte granularity port I/O and ignore the failing bytes. + * This provides Windows compatibility. + */ + for (i = 0, *value = 0; i < width; i += 8) { + + /* Validate and read one byte */ + + if (acpi_hw_validate_io_request(address, 8) == AE_OK) { + status = acpi_os_read_port(address, &one_byte, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + + *value |= (one_byte << i); + } + + address++; + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_write_port + * + * PARAMETERS: Address Address of I/O port/register to write + * Value Value to write + * Width Number of bits + * + * RETURN: Status + * + * DESCRIPTION: Write data to an I/O port or register. This is a front-end + * to acpi_os_write_port that performs validation on both the port + * address and the length. + * + *****************************************************************************/ + +acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width) +{ + acpi_status status; + u32 i; + + /* Truncate address to 16 bits if requested */ + + if (acpi_gbl_truncate_io_addresses) { + address &= ACPI_UINT16_MAX; + } + + /* Validate the entire request and perform the I/O */ + + status = acpi_hw_validate_io_request(address, width); + if (ACPI_SUCCESS(status)) { + status = acpi_os_write_port(address, value, width); + return (status); + } + + if (status != AE_AML_ILLEGAL_ADDRESS) { + return (status); + } + + /* + * There has been a protection violation within the request. Fall + * back to byte granularity port I/O and ignore the failing bytes. + * This provides Windows compatibility. + */ + for (i = 0; i < width; i += 8) { + + /* Validate and write one byte */ + + if (acpi_hw_validate_io_request(address, 8) == AE_OK) { + status = + acpi_os_write_port(address, (value >> i) & 0xFF, 8); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + address++; + } + + return (AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/hwxface.c b/kernel/drivers/acpi/acpica/hwxface.c new file mode 100644 index 000000000..8c017f15d --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwxface.c @@ -0,0 +1,587 @@ +/****************************************************************************** + * + * Module Name: hwxface - Public ACPICA hardware interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwxface") + +/****************************************************************************** + * + * FUNCTION: acpi_reset + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Set reset register in memory or IO space. Note: Does not + * support reset register in PCI config space, this must be + * handled separately. + * + ******************************************************************************/ +acpi_status acpi_reset(void) +{ + struct acpi_generic_address *reset_reg; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_reset); + + reset_reg = &acpi_gbl_FADT.reset_register; + + /* Check if the reset register is supported */ + + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) || + !reset_reg->address) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + if (reset_reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + /* + * For I/O space, write directly to the OSL. This bypasses the port + * validation mechanism, which may block a valid write to the reset + * register. + * + * NOTE: + * The ACPI spec requires the reset register width to be 8, so we + * hardcode it here and ignore the FADT value. This maintains + * compatibility with other ACPI implementations that have allowed + * BIOS code with bad register width values to go unnoticed. + */ + status = + acpi_os_write_port((acpi_io_address) reset_reg->address, + acpi_gbl_FADT.reset_value, + ACPI_RESET_REGISTER_WIDTH); + } else { + /* Write the reset value to the reset register */ + + status = acpi_hw_write(acpi_gbl_FADT.reset_value, reset_reg); + } + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_reset) + +/****************************************************************************** + * + * FUNCTION: acpi_read + * + * PARAMETERS: value - Where the value is returned + * reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Read from either memory or IO space. + * + * LIMITATIONS: + * bit_width must be exactly 8, 16, 32, or 64. + * space_ID must be system_memory or system_IO. + * bit_offset and access_width are currently ignored, as there has + * not been a need to implement these. + * + ******************************************************************************/ +acpi_status acpi_read(u64 *return_value, struct acpi_generic_address *reg) +{ + u32 value_lo; + u32 value_hi; + u32 width; + u64 address; + acpi_status status; + + ACPI_FUNCTION_NAME(acpi_read); + + if (!return_value) { + return (AE_BAD_PARAMETER); + } + + /* Validate contents of the GAS register. Allow 64-bit transfers */ + + status = acpi_hw_validate_register(reg, 64, &address); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Two address spaces supported: Memory or I/O. PCI_Config is + * not supported here because the GAS structure is insufficient + */ + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_read_memory((acpi_physical_address) + address, return_value, + reg->bit_width); + if (ACPI_FAILURE(status)) { + return (status); + } + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + + value_lo = 0; + value_hi = 0; + + width = reg->bit_width; + if (width == 64) { + width = 32; /* Break into two 32-bit transfers */ + } + + status = acpi_hw_read_port((acpi_io_address) + address, &value_lo, width); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (reg->bit_width == 64) { + + /* Read the top 32 bits */ + + status = acpi_hw_read_port((acpi_io_address) + (address + 4), &value_hi, + 32); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + /* Set the return value only if status is AE_OK */ + + *return_value = (value_lo | ((u64)value_hi << 32)); + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Read: %8.8X%8.8X width %2d from %8.8X%8.8X (%s)\n", + ACPI_FORMAT_UINT64(*return_value), reg->bit_width, + ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); + + return (AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_read) + +/****************************************************************************** + * + * FUNCTION: acpi_write + * + * PARAMETERS: value - Value to be written + * reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Write to either memory or IO space. + * + ******************************************************************************/ +acpi_status acpi_write(u64 value, struct acpi_generic_address *reg) +{ + u32 width; + u64 address; + acpi_status status; + + ACPI_FUNCTION_NAME(acpi_write); + + /* Validate contents of the GAS register. Allow 64-bit transfers */ + + status = acpi_hw_validate_register(reg, 64, &address); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Two address spaces supported: Memory or IO. PCI_Config is + * not supported here because the GAS structure is insufficient + */ + if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + status = acpi_os_write_memory((acpi_physical_address) + address, value, reg->bit_width); + if (ACPI_FAILURE(status)) { + return (status); + } + } else { /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + + width = reg->bit_width; + if (width == 64) { + width = 32; /* Break into two 32-bit transfers */ + } + + status = acpi_hw_write_port((acpi_io_address) + address, ACPI_LODWORD(value), + width); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (reg->bit_width == 64) { + status = acpi_hw_write_port((acpi_io_address) + (address + 4), + ACPI_HIDWORD(value), 32); + if (ACPI_FAILURE(status)) { + return (status); + } + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Wrote: %8.8X%8.8X width %2d to %8.8X%8.8X (%s)\n", + ACPI_FORMAT_UINT64(value), reg->bit_width, + ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); + + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_write) + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_read_bit_register + * + * PARAMETERS: register_id - ID of ACPI Bit Register to access + * return_value - Value that was read from the register, + * normalized to bit position zero. + * + * RETURN: Status and the value read from the specified Register. Value + * returned is normalized to bit0 (is shifted all the way right) + * + * DESCRIPTION: ACPI bit_register read function. Does not acquire the HW lock. + * + * SUPPORTS: Bit fields in PM1 Status, PM1 Enable, PM1 Control, and + * PM2 Control. + * + * Note: The hardware lock is not required when reading the ACPI bit registers + * since almost all of them are single bit and it does not matter that + * the parent hardware register can be split across two physical + * registers. The only multi-bit field is SLP_TYP in the PM1 control + * register, but this field does not cross an 8-bit boundary (nor does + * it make much sense to actually read this field.) + * + ******************************************************************************/ +acpi_status acpi_read_bit_register(u32 register_id, u32 *return_value) +{ + struct acpi_bit_register_info *bit_reg_info; + u32 register_value; + u32 value; + acpi_status status; + + ACPI_FUNCTION_TRACE_U32(acpi_read_bit_register, register_id); + + /* Get the info structure corresponding to the requested ACPI Register */ + + bit_reg_info = acpi_hw_get_bit_register_info(register_id); + if (!bit_reg_info) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Read the entire parent register */ + + status = acpi_hw_register_read(bit_reg_info->parent_register, + ®ister_value); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Normalize the value that was read, mask off other bits */ + + value = ((register_value & bit_reg_info->access_bit_mask) + >> bit_reg_info->bit_position); + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "BitReg %X, ParentReg %X, Actual %8.8X, ReturnValue %8.8X\n", + register_id, bit_reg_info->parent_register, + register_value, value)); + + *return_value = value; + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_read_bit_register) + +/******************************************************************************* + * + * FUNCTION: acpi_write_bit_register + * + * PARAMETERS: register_id - ID of ACPI Bit Register to access + * value - Value to write to the register, in bit + * position zero. The bit is automatically + * shifted to the correct position. + * + * RETURN: Status + * + * DESCRIPTION: ACPI Bit Register write function. Acquires the hardware lock + * since most operations require a read/modify/write sequence. + * + * SUPPORTS: Bit fields in PM1 Status, PM1 Enable, PM1 Control, and + * PM2 Control. + * + * Note that at this level, the fact that there may be actually two + * hardware registers (A and B - and B may not exist) is abstracted. + * + ******************************************************************************/ +acpi_status acpi_write_bit_register(u32 register_id, u32 value) +{ + struct acpi_bit_register_info *bit_reg_info; + acpi_cpu_flags lock_flags; + u32 register_value; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_U32(acpi_write_bit_register, register_id); + + /* Get the info structure corresponding to the requested ACPI Register */ + + bit_reg_info = acpi_hw_get_bit_register_info(register_id); + if (!bit_reg_info) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + raw_spin_lock_irqsave(acpi_gbl_hardware_lock, lock_flags); + + /* + * At this point, we know that the parent register is one of the + * following: PM1 Status, PM1 Enable, PM1 Control, or PM2 Control + */ + if (bit_reg_info->parent_register != ACPI_REGISTER_PM1_STATUS) { + /* + * 1) Case for PM1 Enable, PM1 Control, and PM2 Control + * + * Perform a register read to preserve the bits that we are not + * interested in + */ + status = acpi_hw_register_read(bit_reg_info->parent_register, + ®ister_value); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* + * Insert the input bit into the value that was just read + * and write the register + */ + ACPI_REGISTER_INSERT_VALUE(register_value, + bit_reg_info->bit_position, + bit_reg_info->access_bit_mask, + value); + + status = acpi_hw_register_write(bit_reg_info->parent_register, + register_value); + } else { + /* + * 2) Case for PM1 Status + * + * The Status register is different from the rest. Clear an event + * by writing 1, writing 0 has no effect. So, the only relevant + * information is the single bit we're interested in, all others + * should be written as 0 so they will be left unchanged. + */ + register_value = ACPI_REGISTER_PREPARE_BITS(value, + bit_reg_info-> + bit_position, + bit_reg_info-> + access_bit_mask); + + /* No need to write the register if value is all zeros */ + + if (register_value) { + status = + acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, + register_value); + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "BitReg %X, ParentReg %X, Value %8.8X, Actual %8.8X\n", + register_id, bit_reg_info->parent_register, value, + register_value)); + +unlock_and_exit: + + raw_spin_unlock_irqrestore(acpi_gbl_hardware_lock, lock_flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_write_bit_register) +#endif /* !ACPI_REDUCED_HARDWARE */ +/******************************************************************************* + * + * FUNCTION: acpi_get_sleep_type_data + * + * PARAMETERS: sleep_state - Numeric sleep state + * *sleep_type_a - Where SLP_TYPa is returned + * *sleep_type_b - Where SLP_TYPb is returned + * + * RETURN: Status + * + * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested + * sleep state via the appropriate \_Sx object. + * + * The sleep state package returned from the corresponding \_Sx_ object + * must contain at least one integer. + * + * March 2005: + * Added support for a package that contains two integers. This + * goes against the ACPI specification which defines this object as a + * package with one encoded DWORD integer. However, existing practice + * by many BIOS vendors is to return a package with 2 or more integer + * elements, at least one per sleep type (A/B). + * + * January 2013: + * Therefore, we must be prepared to accept a package with either a + * single integer or multiple integers. + * + * The single integer DWORD format is as follows: + * BYTE 0 - Value for the PM1A SLP_TYP register + * BYTE 1 - Value for the PM1B SLP_TYP register + * BYTE 2-3 - Reserved + * + * The dual integer format is as follows: + * Integer 0 - Value for the PM1A SLP_TYP register + * Integer 1 - Value for the PM1A SLP_TYP register + * + ******************************************************************************/ +acpi_status +acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) +{ + acpi_status status; + struct acpi_evaluate_info *info; + union acpi_operand_object **elements; + + ACPI_FUNCTION_TRACE(acpi_get_sleep_type_data); + + /* Validate parameters */ + + if ((sleep_state > ACPI_S_STATES_MAX) || !sleep_type_a || !sleep_type_b) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Allocate the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* + * Evaluate the \_Sx namespace object containing the register values + * for this state + */ + info->relative_pathname = + ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]); + status = acpi_ns_evaluate(info); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Must have a return object */ + + if (!info->return_object) { + ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]", + info->relative_pathname)); + status = AE_AML_NO_RETURN_VALUE; + goto cleanup; + } + + /* Return object must be of type Package */ + + if (info->return_object->common.type != ACPI_TYPE_PACKAGE) { + ACPI_ERROR((AE_INFO, + "Sleep State return object is not a Package")); + status = AE_AML_OPERAND_TYPE; + goto cleanup1; + } + + /* + * Any warnings about the package length or the object types have + * already been issued by the predefined name module -- there is no + * need to repeat them here. + */ + elements = info->return_object->package.elements; + switch (info->return_object->package.count) { + case 0: + + status = AE_AML_PACKAGE_LIMIT; + break; + + case 1: + + if (elements[0]->common.type != ACPI_TYPE_INTEGER) { + status = AE_AML_OPERAND_TYPE; + break; + } + + /* A valid _Sx_ package with one integer */ + + *sleep_type_a = (u8)elements[0]->integer.value; + *sleep_type_b = (u8)(elements[0]->integer.value >> 8); + break; + + case 2: + default: + + if ((elements[0]->common.type != ACPI_TYPE_INTEGER) || + (elements[1]->common.type != ACPI_TYPE_INTEGER)) { + status = AE_AML_OPERAND_TYPE; + break; + } + + /* A valid _Sx_ package with two integers */ + + *sleep_type_a = (u8)elements[0]->integer.value; + *sleep_type_b = (u8)elements[1]->integer.value; + break; + } + +cleanup1: + acpi_ut_remove_reference(info->return_object); + +cleanup: + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While evaluating Sleep State [%s]", + info->relative_pathname)); + } + + ACPI_FREE(info); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_sleep_type_data) diff --git a/kernel/drivers/acpi/acpica/hwxfsleep.c b/kernel/drivers/acpi/acpica/hwxfsleep.c new file mode 100644 index 000000000..3b3767698 --- /dev/null +++ b/kernel/drivers/acpi/acpica/hwxfsleep.c @@ -0,0 +1,432 @@ +/****************************************************************************** + * + * Name: hwxfsleep.c - ACPI Hardware Sleep/Wake External Interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwxfsleep") + +/* Local prototypes */ +static acpi_status acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id); + +/* + * Dispatch table used to efficiently branch to the various sleep + * functions. + */ +#define ACPI_SLEEP_FUNCTION_ID 0 +#define ACPI_WAKE_PREP_FUNCTION_ID 1 +#define ACPI_WAKE_FUNCTION_ID 2 + +/* Legacy functions are optional, based upon ACPI_REDUCED_HARDWARE */ + +static struct acpi_sleep_functions acpi_sleep_dispatch[] = { + {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_sleep), + acpi_hw_extended_sleep}, + {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake_prep), + acpi_hw_extended_wake_prep}, + {ACPI_HW_OPTIONAL_FUNCTION(acpi_hw_legacy_wake), acpi_hw_extended_wake} +}; + +/* + * These functions are removed for the ACPI_REDUCED_HARDWARE case: + * acpi_set_firmware_waking_vector + * acpi_set_firmware_waking_vector64 + * acpi_enter_sleep_state_s4bios + */ + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_set_firmware_waking_vector + * + * PARAMETERS: physical_address - 32-bit physical address of ACPI real mode + * entry point. + * + * RETURN: Status + * + * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS + * + ******************************************************************************/ + +acpi_status acpi_set_firmware_waking_vector(u32 physical_address) +{ + ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector); + + + /* + * According to the ACPI specification 2.0c and later, the 64-bit + * waking vector should be cleared and the 32-bit waking vector should + * be used, unless we want the wake-up code to be called by the BIOS in + * Protected Mode. Some systems (for example HP dv5-1004nr) are known + * to fail to resume if the 64-bit vector is used. + */ + + /* Set the 32-bit vector */ + + acpi_gbl_FACS->firmware_waking_vector = physical_address; + + /* Clear the 64-bit vector if it exists */ + + if ((acpi_gbl_FACS->length > 32) && (acpi_gbl_FACS->version >= 1)) { + acpi_gbl_FACS->xfirmware_waking_vector = 0; + } + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector) + +#if ACPI_MACHINE_WIDTH == 64 +/******************************************************************************* + * + * FUNCTION: acpi_set_firmware_waking_vector64 + * + * PARAMETERS: physical_address - 64-bit physical address of ACPI protected + * mode entry point. + * + * RETURN: Status + * + * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if + * it exists in the table. This function is intended for use with + * 64-bit host operating systems. + * + ******************************************************************************/ +acpi_status acpi_set_firmware_waking_vector64(u64 physical_address) +{ + ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64); + + + /* Determine if the 64-bit vector actually exists */ + + if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Clear 32-bit vector, set the 64-bit X_ vector */ + + acpi_gbl_FACS->firmware_waking_vector = 0; + acpi_gbl_FACS->xfirmware_waking_vector = physical_address; + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64) +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_enter_sleep_state_s4bios + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Perform a S4 bios request. + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ +acpi_status acpi_enter_sleep_state_s4bios(void) +{ + u32 in_value; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_s4bios); + + /* Clear the wake status bit (PM1) */ + + status = + acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_clear_acpi_status(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * 1) Disable/Clear all GPEs + * 2) Enable all wakeup GPEs + */ + status = acpi_hw_disable_all_gpes(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + acpi_gbl_system_awake_and_running = FALSE; + + status = acpi_hw_enable_all_wakeup_gpes(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_FLUSH_CPU_CACHE(); + + status = acpi_hw_write_port(acpi_gbl_FADT.smi_command, + (u32)acpi_gbl_FADT.s4_bios_request, 8); + + do { + acpi_os_stall(ACPI_USEC_PER_MSEC); + status = + acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } while (!in_value); + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios) +#endif /* !ACPI_REDUCED_HARDWARE */ +/******************************************************************************* + * + * FUNCTION: acpi_hw_sleep_dispatch + * + * PARAMETERS: sleep_state - Which sleep state to enter/exit + * function_id - Sleep, wake_prep, or Wake + * + * RETURN: Status from the invoked sleep handling function. + * + * DESCRIPTION: Dispatch a sleep/wake request to the appropriate handling + * function. + * + ******************************************************************************/ +static acpi_status acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id) +{ + acpi_status status; + struct acpi_sleep_functions *sleep_functions = + &acpi_sleep_dispatch[function_id]; + +#if (!ACPI_REDUCED_HARDWARE) + /* + * If the Hardware Reduced flag is set (from the FADT), we must + * use the extended sleep registers (FADT). Note: As per the ACPI + * specification, these extended registers are to be used for HW-reduced + * platforms only. They are not general-purpose replacements for the + * legacy PM register sleep support. + */ + if (acpi_gbl_reduced_hardware) { + status = sleep_functions->extended_function(sleep_state); + } else { + /* Legacy sleep */ + + status = sleep_functions->legacy_function(sleep_state); + } + + return (status); + +#else + /* + * For the case where reduced-hardware-only code is being generated, + * we know that only the extended sleep registers are available + */ + status = sleep_functions->extended_function(sleep_state); + return (status); + +#endif /* !ACPI_REDUCED_HARDWARE */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_enter_sleep_state_prep + * + * PARAMETERS: sleep_state - Which sleep state to enter + * + * RETURN: Status + * + * DESCRIPTION: Prepare to enter a system sleep state. + * This function must execute with interrupts enabled. + * We break sleeping into 2 stages so that OSPM can handle + * various OS-specific tasks between the two steps. + * + ******************************************************************************/ + +acpi_status acpi_enter_sleep_state_prep(u8 sleep_state) +{ + acpi_status status; + struct acpi_object_list arg_list; + union acpi_object arg; + u32 sst_value; + + ACPI_FUNCTION_TRACE(acpi_enter_sleep_state_prep); + + status = acpi_get_sleep_type_data(sleep_state, + &acpi_gbl_sleep_type_a, + &acpi_gbl_sleep_type_b); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Execute the _PTS method (Prepare To Sleep) */ + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = sleep_state; + + status = + acpi_evaluate_object(NULL, METHOD_PATHNAME__PTS, &arg_list, NULL); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + return_ACPI_STATUS(status); + } + + /* Setup the argument to the _SST method (System STatus) */ + + switch (sleep_state) { + case ACPI_STATE_S0: + + sst_value = ACPI_SST_WORKING; + break; + + case ACPI_STATE_S1: + case ACPI_STATE_S2: + case ACPI_STATE_S3: + + sst_value = ACPI_SST_SLEEPING; + break; + + case ACPI_STATE_S4: + + sst_value = ACPI_SST_SLEEP_CONTEXT; + break; + + default: + + sst_value = ACPI_SST_INDICATOR_OFF; /* Default is off */ + break; + } + + /* + * Set the system indicators to show the desired sleep state. + * _SST is an optional method (return no error if not found) + */ + acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, sst_value); + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep) + +/******************************************************************************* + * + * FUNCTION: acpi_enter_sleep_state + * + * PARAMETERS: sleep_state - Which sleep state to enter + * + * RETURN: Status + * + * DESCRIPTION: Enter a system sleep state + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ +acpi_status acpi_enter_sleep_state(u8 sleep_state) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_enter_sleep_state); + + if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) || + (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) { + ACPI_ERROR((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X", + acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b)); + return_ACPI_STATUS(AE_AML_OPERAND_VALUE); + } + + status = acpi_hw_sleep_dispatch(sleep_state, ACPI_SLEEP_FUNCTION_ID); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state) + +/******************************************************************************* + * + * FUNCTION: acpi_leave_sleep_state_prep + * + * PARAMETERS: sleep_state - Which sleep state we are exiting + * + * RETURN: Status + * + * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a + * sleep. Called with interrupts DISABLED. + * We break wake/resume into 2 stages so that OSPM can handle + * various OS-specific tasks between the two steps. + * + ******************************************************************************/ +acpi_status acpi_leave_sleep_state_prep(u8 sleep_state) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep); + + status = + acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_PREP_FUNCTION_ID); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state_prep) + +/******************************************************************************* + * + * FUNCTION: acpi_leave_sleep_state + * + * PARAMETERS: sleep_state - Which sleep state we are exiting + * + * RETURN: Status + * + * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep + * Called with interrupts ENABLED. + * + ******************************************************************************/ +acpi_status acpi_leave_sleep_state(u8 sleep_state) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_leave_sleep_state); + + status = acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_FUNCTION_ID); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_leave_sleep_state) diff --git a/kernel/drivers/acpi/acpica/nsaccess.c b/kernel/drivers/acpi/acpica/nsaccess.c new file mode 100644 index 000000000..24fa19a76 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsaccess.c @@ -0,0 +1,672 @@ +/******************************************************************************* + * + * Module Name: nsaccess - Top-level functions for accessing ACPI namespace + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acnamesp.h" +#include "acdispat.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsaccess") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_root_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Allocate and initialize the default root named objects + * + * MUTEX: Locks namespace for entire execution + * + ******************************************************************************/ +acpi_status acpi_ns_root_initialize(void) +{ + acpi_status status; + const struct acpi_predefined_names *init_val = NULL; + struct acpi_namespace_node *new_node; + union acpi_operand_object *obj_desc; + acpi_string val = NULL; + + ACPI_FUNCTION_TRACE(ns_root_initialize); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * The global root ptr is initially NULL, so a non-NULL value indicates + * that acpi_ns_root_initialize() has already been called; just return. + */ + if (acpi_gbl_root_node) { + status = AE_OK; + goto unlock_and_exit; + } + + /* + * Tell the rest of the subsystem that the root is initialized + * (This is OK because the namespace is locked) + */ + acpi_gbl_root_node = &acpi_gbl_root_node_struct; + + /* Enter the pre-defined names in the name table */ + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Entering predefined entries into namespace\n")); + + for (init_val = acpi_gbl_pre_defined_names; init_val->name; init_val++) { + + /* _OSI is optional for now, will be permanent later */ + + if (!ACPI_STRCMP(init_val->name, "_OSI") + && !acpi_gbl_create_osi_method) { + continue; + } + + status = acpi_ns_lookup(NULL, init_val->name, init_val->type, + ACPI_IMODE_LOAD_PASS2, + ACPI_NS_NO_UPSEARCH, NULL, &new_node); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not create predefined name %s", + init_val->name)); + continue; + } + + /* + * Name entered successfully. If entry in pre_defined_names[] specifies + * an initial value, create the initial value. + */ + if (init_val->val) { + status = acpi_os_predefined_override(init_val, &val); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not override predefined %s", + init_val->name)); + } + + if (!val) { + val = init_val->val; + } + + /* + * Entry requests an initial value, allocate a + * descriptor for it. + */ + obj_desc = + acpi_ut_create_internal_object(init_val->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* + * Convert value string from table entry to + * internal representation. Only types actually + * used for initial values are implemented here. + */ + switch (init_val->type) { + case ACPI_TYPE_METHOD: + + obj_desc->method.param_count = + (u8) ACPI_TO_INTEGER(val); + obj_desc->common.flags |= AOPOBJ_DATA_VALID; + +#if defined (ACPI_ASL_COMPILER) + + /* Save the parameter count for the iASL compiler */ + + new_node->value = obj_desc->method.param_count; +#else + /* Mark this as a very SPECIAL method */ + + obj_desc->method.info_flags = + ACPI_METHOD_INTERNAL_ONLY; + obj_desc->method.dispatch.implementation = + acpi_ut_osi_implementation; +#endif + break; + + case ACPI_TYPE_INTEGER: + + obj_desc->integer.value = ACPI_TO_INTEGER(val); + break; + + case ACPI_TYPE_STRING: + + /* Build an object around the static string */ + + obj_desc->string.length = (u32)ACPI_STRLEN(val); + obj_desc->string.pointer = val; + obj_desc->common.flags |= AOPOBJ_STATIC_POINTER; + break; + + case ACPI_TYPE_MUTEX: + + obj_desc->mutex.node = new_node; + obj_desc->mutex.sync_level = + (u8) (ACPI_TO_INTEGER(val) - 1); + + /* Create a mutex */ + + status = + acpi_os_create_mutex(&obj_desc->mutex. + os_mutex); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(obj_desc); + goto unlock_and_exit; + } + + /* Special case for ACPI Global Lock */ + + if (ACPI_STRCMP(init_val->name, "_GL_") == 0) { + acpi_gbl_global_lock_mutex = obj_desc; + + /* Create additional counting semaphore for global lock */ + + status = + acpi_os_create_semaphore(1, 0, + &acpi_gbl_global_lock_semaphore); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference + (obj_desc); + goto unlock_and_exit; + } + } + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unsupported initial type value 0x%X", + init_val->type)); + acpi_ut_remove_reference(obj_desc); + obj_desc = NULL; + continue; + } + + /* Store pointer to value descriptor in the Node */ + + status = acpi_ns_attach_object(new_node, obj_desc, + obj_desc->common.type); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference(obj_desc); + } + } + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + /* Save a handle to "_GPE", it is always present */ + + if (ACPI_SUCCESS(status)) { + status = acpi_ns_get_node(NULL, "\\_GPE", ACPI_NS_NO_UPSEARCH, + &acpi_gbl_fadt_gpe_device); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_lookup + * + * PARAMETERS: scope_info - Current scope info block + * pathname - Search pathname, in internal format + * (as represented in the AML stream) + * type - Type associated with name + * interpreter_mode - IMODE_LOAD_PASS2 => add name if not found + * flags - Flags describing the search restrictions + * walk_state - Current state of the walk + * return_node - Where the Node is placed (if found + * or created successfully) + * + * RETURN: Status + * + * DESCRIPTION: Find or enter the passed name in the name space. + * Log an error if name not found in Exec mode. + * + * MUTEX: Assumes namespace is locked. + * + ******************************************************************************/ + +acpi_status +acpi_ns_lookup(union acpi_generic_state *scope_info, + char *pathname, + acpi_object_type type, + acpi_interpreter_mode interpreter_mode, + u32 flags, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **return_node) +{ + acpi_status status; + char *path = pathname; + struct acpi_namespace_node *prefix_node; + struct acpi_namespace_node *current_node = NULL; + struct acpi_namespace_node *this_node = NULL; + u32 num_segments; + u32 num_carats; + acpi_name simple_name; + acpi_object_type type_to_check_for; + acpi_object_type this_search_type; + u32 search_parent_flag = ACPI_NS_SEARCH_PARENT; + u32 local_flags; + + ACPI_FUNCTION_TRACE(ns_lookup); + + if (!return_node) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + local_flags = flags & ~(ACPI_NS_ERROR_IF_FOUND | ACPI_NS_SEARCH_PARENT); + *return_node = ACPI_ENTRY_NOT_FOUND; + acpi_gbl_ns_lookup_count++; + + if (!acpi_gbl_root_node) { + return_ACPI_STATUS(AE_NO_NAMESPACE); + } + + /* Get the prefix scope. A null scope means use the root scope */ + + if ((!scope_info) || (!scope_info->scope.node)) { + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Null scope prefix, using root node (%p)\n", + acpi_gbl_root_node)); + + prefix_node = acpi_gbl_root_node; + } else { + prefix_node = scope_info->scope.node; + if (ACPI_GET_DESCRIPTOR_TYPE(prefix_node) != + ACPI_DESC_TYPE_NAMED) { + ACPI_ERROR((AE_INFO, "%p is not a namespace node [%s]", + prefix_node, + acpi_ut_get_descriptor_name(prefix_node))); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + if (!(flags & ACPI_NS_PREFIX_IS_SCOPE)) { + /* + * This node might not be a actual "scope" node (such as a + * Device/Method, etc.) It could be a Package or other object + * node. Backup up the tree to find the containing scope node. + */ + while (!acpi_ns_opens_scope(prefix_node->type) && + prefix_node->type != ACPI_TYPE_ANY) { + prefix_node = prefix_node->parent; + } + } + } + + /* Save type. TBD: may be no longer necessary */ + + type_to_check_for = type; + + /* + * Begin examination of the actual pathname + */ + if (!pathname) { + + /* A Null name_path is allowed and refers to the root */ + + num_segments = 0; + this_node = acpi_gbl_root_node; + path = ""; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Null Pathname (Zero segments), Flags=%X\n", + flags)); + } else { + /* + * Name pointer is valid (and must be in internal name format) + * + * Check for scope prefixes: + * + * As represented in the AML stream, a namepath consists of an + * optional scope prefix followed by a name segment part. + * + * If present, the scope prefix is either a Root Prefix (in + * which case the name is fully qualified), or one or more + * Parent Prefixes (in which case the name's scope is relative + * to the current scope). + */ + if (*path == (u8) AML_ROOT_PREFIX) { + + /* Pathname is fully qualified, start from the root */ + + this_node = acpi_gbl_root_node; + search_parent_flag = ACPI_NS_NO_UPSEARCH; + + /* Point to name segment part */ + + path++; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Path is absolute from root [%p]\n", + this_node)); + } else { + /* Pathname is relative to current scope, start there */ + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Searching relative to prefix scope [%4.4s] (%p)\n", + acpi_ut_get_node_name(prefix_node), + prefix_node)); + + /* + * Handle multiple Parent Prefixes (carat) by just getting + * the parent node for each prefix instance. + */ + this_node = prefix_node; + num_carats = 0; + while (*path == (u8) AML_PARENT_PREFIX) { + + /* Name is fully qualified, no search rules apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + + /* + * Point past this prefix to the name segment + * part or the next Parent Prefix + */ + path++; + + /* Backup to the parent node */ + + num_carats++; + this_node = this_node->parent; + if (!this_node) { + + /* Current scope has no parent scope */ + + ACPI_ERROR((AE_INFO, + "%s: Path has too many parent prefixes (^) " + "- reached beyond root node", + pathname)); + return_ACPI_STATUS(AE_NOT_FOUND); + } + } + + if (search_parent_flag == ACPI_NS_NO_UPSEARCH) { + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Search scope is [%4.4s], path has %u carat(s)\n", + acpi_ut_get_node_name + (this_node), num_carats)); + } + } + + /* + * Determine the number of ACPI name segments in this pathname. + * + * The segment part consists of either: + * - A Null name segment (0) + * - A dual_name_prefix followed by two 4-byte name segments + * - A multi_name_prefix followed by a byte indicating the + * number of segments and the segments themselves. + * - A single 4-byte name segment + * + * Examine the name prefix opcode, if any, to determine the number of + * segments. + */ + switch (*path) { + case 0: + /* + * Null name after a root or parent prefixes. We already + * have the correct target node and there are no name segments. + */ + num_segments = 0; + type = this_node->type; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Prefix-only Pathname (Zero name segments), Flags=%X\n", + flags)); + break; + + case AML_DUAL_NAME_PREFIX: + + /* More than one name_seg, search rules do not apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + + /* Two segments, point to first name segment */ + + num_segments = 2; + path++; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Dual Pathname (2 segments, Flags=%X)\n", + flags)); + break; + + case AML_MULTI_NAME_PREFIX_OP: + + /* More than one name_seg, search rules do not apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + + /* Extract segment count, point to first name segment */ + + path++; + num_segments = (u32) (u8) * path; + path++; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Multi Pathname (%u Segments, Flags=%X)\n", + num_segments, flags)); + break; + + default: + /* + * Not a Null name, no Dual or Multi prefix, hence there is + * only one name segment and Pathname is already pointing to it. + */ + num_segments = 1; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Simple Pathname (1 segment, Flags=%X)\n", + flags)); + break; + } + + ACPI_DEBUG_EXEC(acpi_ns_print_pathname(num_segments, path)); + } + + /* + * Search namespace for each segment of the name. Loop through and + * verify (or add to the namespace) each name segment. + * + * The object type is significant only at the last name + * segment. (We don't care about the types along the path, only + * the type of the final target object.) + */ + this_search_type = ACPI_TYPE_ANY; + current_node = this_node; + while (num_segments && current_node) { + num_segments--; + if (!num_segments) { + + /* This is the last segment, enable typechecking */ + + this_search_type = type; + + /* + * Only allow automatic parent search (search rules) if the caller + * requested it AND we have a single, non-fully-qualified name_seg + */ + if ((search_parent_flag != ACPI_NS_NO_UPSEARCH) && + (flags & ACPI_NS_SEARCH_PARENT)) { + local_flags |= ACPI_NS_SEARCH_PARENT; + } + + /* Set error flag according to caller */ + + if (flags & ACPI_NS_ERROR_IF_FOUND) { + local_flags |= ACPI_NS_ERROR_IF_FOUND; + } + } + + /* Extract one ACPI name from the front of the pathname */ + + ACPI_MOVE_32_TO_32(&simple_name, path); + + /* Try to find the single (4 character) ACPI name */ + + status = + acpi_ns_search_and_enter(simple_name, walk_state, + current_node, interpreter_mode, + this_search_type, local_flags, + &this_node); + if (ACPI_FAILURE(status)) { + if (status == AE_NOT_FOUND) { + + /* Name not found in ACPI namespace */ + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Name [%4.4s] not found in scope [%4.4s] %p\n", + (char *)&simple_name, + (char *)¤t_node->name, + current_node)); + } + + *return_node = this_node; + return_ACPI_STATUS(status); + } + + /* More segments to follow? */ + + if (num_segments > 0) { + /* + * If we have an alias to an object that opens a scope (such as a + * device or processor), we need to dereference the alias here so + * that we can access any children of the original node (via the + * remaining segments). + */ + if (this_node->type == ACPI_TYPE_LOCAL_ALIAS) { + if (!this_node->object) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + if (acpi_ns_opens_scope + (((struct acpi_namespace_node *) + this_node->object)->type)) { + this_node = + (struct acpi_namespace_node *) + this_node->object; + } + } + } + + /* Special handling for the last segment (num_segments == 0) */ + + else { + /* + * Sanity typecheck of the target object: + * + * If 1) This is the last segment (num_segments == 0) + * 2) And we are looking for a specific type + * (Not checking for TYPE_ANY) + * 3) Which is not an alias + * 4) Which is not a local type (TYPE_SCOPE) + * 5) And the type of target object is known (not TYPE_ANY) + * 6) And target object does not match what we are looking for + * + * Then we have a type mismatch. Just warn and ignore it. + */ + if ((type_to_check_for != ACPI_TYPE_ANY) && + (type_to_check_for != ACPI_TYPE_LOCAL_ALIAS) && + (type_to_check_for != ACPI_TYPE_LOCAL_METHOD_ALIAS) + && (type_to_check_for != ACPI_TYPE_LOCAL_SCOPE) + && (this_node->type != ACPI_TYPE_ANY) + && (this_node->type != type_to_check_for)) { + + /* Complain about a type mismatch */ + + ACPI_WARNING((AE_INFO, + "NsLookup: Type mismatch on %4.4s (%s), searching for (%s)", + ACPI_CAST_PTR(char, &simple_name), + acpi_ut_get_type_name(this_node-> + type), + acpi_ut_get_type_name + (type_to_check_for))); + } + + /* + * If this is the last name segment and we are not looking for a + * specific type, but the type of found object is known, use that + * type to (later) see if it opens a scope. + */ + if (type == ACPI_TYPE_ANY) { + type = this_node->type; + } + } + + /* Point to next name segment and make this node current */ + + path += ACPI_NAME_SIZE; + current_node = this_node; + } + + /* Always check if we need to open a new scope */ + + if (!(flags & ACPI_NS_DONT_OPEN_SCOPE) && (walk_state)) { + /* + * If entry is a type which opens a scope, push the new scope on the + * scope stack. + */ + if (acpi_ns_opens_scope(type)) { + status = + acpi_ds_scope_stack_push(this_node, type, + walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } + + *return_node = this_node; + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/nsalloc.c b/kernel/drivers/acpi/acpica/nsalloc.c new file mode 100644 index 000000000..e107f929d --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsalloc.c @@ -0,0 +1,526 @@ +/******************************************************************************* + * + * Module Name: nsalloc - Namespace allocation and deletion utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsalloc") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_create_node + * + * PARAMETERS: name - Name of the new node (4 char ACPI name) + * + * RETURN: New namespace node (Null on failure) + * + * DESCRIPTION: Create a namespace node + * + ******************************************************************************/ +struct acpi_namespace_node *acpi_ns_create_node(u32 name) +{ + struct acpi_namespace_node *node; +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + u32 temp; +#endif + + ACPI_FUNCTION_TRACE(ns_create_node); + + node = acpi_os_acquire_object(acpi_gbl_namespace_cache); + if (!node) { + return_PTR(NULL); + } + + ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_allocated++); + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + temp = acpi_gbl_ns_node_list->total_allocated - + acpi_gbl_ns_node_list->total_freed; + if (temp > acpi_gbl_ns_node_list->max_occupied) { + acpi_gbl_ns_node_list->max_occupied = temp; + } +#endif + + node->name.integer = name; + ACPI_SET_DESCRIPTOR_TYPE(node, ACPI_DESC_TYPE_NAMED); + return_PTR(node); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_node + * + * PARAMETERS: node - Node to be deleted + * + * RETURN: None + * + * DESCRIPTION: Delete a namespace node. All node deletions must come through + * here. Detaches any attached objects, including any attached + * data. If a handler is associated with attached data, it is + * invoked before the node is deleted. + * + ******************************************************************************/ + +void acpi_ns_delete_node(struct acpi_namespace_node *node) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *next_desc; + + ACPI_FUNCTION_NAME(ns_delete_node); + + /* Detach an object if there is one */ + + acpi_ns_detach_object(node); + + /* + * Delete an attached data object list if present (objects that were + * attached via acpi_attach_data). Note: After any normal object is + * detached above, the only possible remaining object(s) are data + * objects, in a linked list. + */ + obj_desc = node->object; + while (obj_desc && (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) { + + /* Invoke the attached data deletion handler if present */ + + if (obj_desc->data.handler) { + obj_desc->data.handler(node, obj_desc->data.pointer); + } + + next_desc = obj_desc->common.next_object; + acpi_ut_remove_reference(obj_desc); + obj_desc = next_desc; + } + + /* Special case for the statically allocated root node */ + + if (node == acpi_gbl_root_node) { + return; + } + + /* Now we can delete the node */ + + (void)acpi_os_release_object(acpi_gbl_namespace_cache, node); + + ACPI_MEM_TRACKING(acpi_gbl_ns_node_list->total_freed++); + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Node %p, Remaining %X\n", + node, acpi_gbl_current_node_count)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_remove_node + * + * PARAMETERS: node - Node to be removed/deleted + * + * RETURN: None + * + * DESCRIPTION: Remove (unlink) and delete a namespace node + * + ******************************************************************************/ + +void acpi_ns_remove_node(struct acpi_namespace_node *node) +{ + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *prev_node; + struct acpi_namespace_node *next_node; + + ACPI_FUNCTION_TRACE_PTR(ns_remove_node, node); + + parent_node = node->parent; + + prev_node = NULL; + next_node = parent_node->child; + + /* Find the node that is the previous peer in the parent's child list */ + + while (next_node != node) { + prev_node = next_node; + next_node = next_node->peer; + } + + if (prev_node) { + + /* Node is not first child, unlink it */ + + prev_node->peer = node->peer; + } else { + /* + * Node is first child (has no previous peer). + * Link peer list to parent + */ + parent_node->child = node->peer; + } + + /* Delete the node and any attached objects */ + + acpi_ns_delete_node(node); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_install_node + * + * PARAMETERS: walk_state - Current state of the walk + * parent_node - The parent of the new Node + * node - The new Node to install + * type - ACPI object type of the new Node + * + * RETURN: None + * + * DESCRIPTION: Initialize a new namespace node and install it amongst + * its peers. + * + * Note: Current namespace lookup is linear search. This appears + * to be sufficient as namespace searches consume only a small + * fraction of the execution time of the ACPI subsystem. + * + ******************************************************************************/ + +void acpi_ns_install_node(struct acpi_walk_state *walk_state, struct acpi_namespace_node *parent_node, /* Parent */ + struct acpi_namespace_node *node, /* New Child */ + acpi_object_type type) +{ + acpi_owner_id owner_id = 0; + struct acpi_namespace_node *child_node; + + ACPI_FUNCTION_TRACE(ns_install_node); + + if (walk_state) { + /* + * Get the owner ID from the Walk state. The owner ID is used to + * track table deletion and deletion of objects created by methods. + */ + owner_id = walk_state->owner_id; + + if ((walk_state->method_desc) && + (parent_node != walk_state->method_node)) { + /* + * A method is creating a new node that is not a child of the + * method (it is non-local). Mark the executing method as having + * modified the namespace. This is used for cleanup when the + * method exits. + */ + walk_state->method_desc->method.info_flags |= + ACPI_METHOD_MODIFIED_NAMESPACE; + } + } + + /* Link the new entry into the parent and existing children */ + + node->peer = NULL; + node->parent = parent_node; + child_node = parent_node->child; + + if (!child_node) { + parent_node->child = node; + } else { + /* Add node to the end of the peer list */ + + while (child_node->peer) { + child_node = child_node->peer; + } + + child_node->peer = node; + } + + /* Init the new entry */ + + node->owner_id = owner_id; + node->type = (u8) type; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "%4.4s (%s) [Node %p Owner %X] added to %4.4s (%s) [Node %p]\n", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type), node, owner_id, + acpi_ut_get_node_name(parent_node), + acpi_ut_get_type_name(parent_node->type), + parent_node)); + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_children + * + * PARAMETERS: parent_node - Delete this objects children + * + * RETURN: None. + * + * DESCRIPTION: Delete all children of the parent object. In other words, + * deletes a "scope". + * + ******************************************************************************/ + +void acpi_ns_delete_children(struct acpi_namespace_node *parent_node) +{ + struct acpi_namespace_node *next_node; + struct acpi_namespace_node *node_to_delete; + + ACPI_FUNCTION_TRACE_PTR(ns_delete_children, parent_node); + + if (!parent_node) { + return_VOID; + } + + /* Deallocate all children at this level */ + + next_node = parent_node->child; + while (next_node) { + + /* Grandchildren should have all been deleted already */ + + if (next_node->child) { + ACPI_ERROR((AE_INFO, "Found a grandchild! P=%p C=%p", + parent_node, next_node)); + } + + /* + * Delete this child node and move on to the next child in the list. + * No need to unlink the node since we are deleting the entire branch. + */ + node_to_delete = next_node; + next_node = next_node->peer; + acpi_ns_delete_node(node_to_delete); + }; + + /* Clear the parent's child pointer */ + + parent_node->child = NULL; + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_namespace_subtree + * + * PARAMETERS: parent_node - Root of the subtree to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Delete a subtree of the namespace. This includes all objects + * stored within the subtree. + * + ******************************************************************************/ + +void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) +{ + struct acpi_namespace_node *child_node = NULL; + u32 level = 1; + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_delete_namespace_subtree); + + if (!parent_node) { + return_VOID; + } + + /* Lock namespace for possible update */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_VOID; + } + + /* + * Traverse the tree of objects until we bubble back up + * to where we started. + */ + while (level > 0) { + + /* Get the next node in this scope (NULL if none) */ + + child_node = acpi_ns_get_next_node(parent_node, child_node); + if (child_node) { + + /* Found a child node - detach any attached object */ + + acpi_ns_detach_object(child_node); + + /* Check if this node has any children */ + + if (child_node->child) { + /* + * There is at least one child of this node, + * visit the node + */ + level++; + parent_node = child_node; + child_node = NULL; + } + } else { + /* + * No more children of this parent node. + * Move up to the grandparent. + */ + level--; + + /* + * Now delete all of the children of this parent + * all at the same time. + */ + acpi_ns_delete_children(parent_node); + + /* New "last child" is this parent node */ + + child_node = parent_node; + + /* Move up the tree to the grandparent */ + + parent_node = parent_node->parent; + } + } + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_namespace_by_owner + * + * PARAMETERS: owner_id - All nodes with this owner will be deleted + * + * RETURN: Status + * + * DESCRIPTION: Delete entries within the namespace that are owned by a + * specific ID. Used to delete entire ACPI tables. All + * reference counts are updated. + * + * MUTEX: Locks namespace during deletion walk. + * + ******************************************************************************/ + +void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) +{ + struct acpi_namespace_node *child_node; + struct acpi_namespace_node *deletion_node; + struct acpi_namespace_node *parent_node; + u32 level; + acpi_status status; + + ACPI_FUNCTION_TRACE_U32(ns_delete_namespace_by_owner, owner_id); + + if (owner_id == 0) { + return_VOID; + } + + /* Lock namespace for possible update */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_VOID; + } + + deletion_node = NULL; + parent_node = acpi_gbl_root_node; + child_node = NULL; + level = 1; + + /* + * Traverse the tree of nodes until we bubble back up + * to where we started. + */ + while (level > 0) { + /* + * Get the next child of this parent node. When child_node is NULL, + * the first child of the parent is returned + */ + child_node = acpi_ns_get_next_node(parent_node, child_node); + + if (deletion_node) { + acpi_ns_delete_children(deletion_node); + acpi_ns_remove_node(deletion_node); + deletion_node = NULL; + } + + if (child_node) { + if (child_node->owner_id == owner_id) { + + /* Found a matching child node - detach any attached object */ + + acpi_ns_detach_object(child_node); + } + + /* Check if this node has any children */ + + if (child_node->child) { + /* + * There is at least one child of this node, + * visit the node + */ + level++; + parent_node = child_node; + child_node = NULL; + } else if (child_node->owner_id == owner_id) { + deletion_node = child_node; + } + } else { + /* + * No more children of this parent node. + * Move up to the grandparent. + */ + level--; + if (level != 0) { + if (parent_node->owner_id == owner_id) { + deletion_node = parent_node; + } + } + + /* New "last child" is this parent node */ + + child_node = parent_node; + + /* Move up the tree to the grandparent */ + + parent_node = parent_node->parent; + } + } + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/nsarguments.c b/kernel/drivers/acpi/acpica/nsarguments.c new file mode 100644 index 000000000..5d347a71b --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsarguments.c @@ -0,0 +1,294 @@ +/****************************************************************************** + * + * Module Name: nsarguments - Validation of args for ACPI predefined methods + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsarguments") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_argument_types + * + * PARAMETERS: info - Method execution information block + * + * RETURN: None + * + * DESCRIPTION: Check the incoming argument count and all argument types + * against the argument type list for a predefined name. + * + ******************************************************************************/ +void acpi_ns_check_argument_types(struct acpi_evaluate_info *info) +{ + u16 arg_type_list; + u8 arg_count; + u8 arg_type; + u8 user_arg_type; + u32 i; + + /* If not a predefined name, cannot typecheck args */ + + if (!info->predefined) { + return; + } + + arg_type_list = info->predefined->info.argument_list; + arg_count = METHOD_GET_ARG_COUNT(arg_type_list); + + /* Typecheck all arguments */ + + for (i = 0; ((i < arg_count) && (i < info->param_count)); i++) { + arg_type = METHOD_GET_NEXT_TYPE(arg_type_list); + user_arg_type = info->parameters[i]->common.type; + + if (user_arg_type != arg_type) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + ACPI_WARN_ALWAYS, + "Argument #%u type mismatch - " + "Found [%s], ACPI requires [%s]", + (i + 1), + acpi_ut_get_type_name + (user_arg_type), + acpi_ut_get_type_name(arg_type))); + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_acpi_compliance + * + * PARAMETERS: pathname - Full pathname to the node (for error msgs) + * node - Namespace node for the method/object + * predefined - Pointer to entry in predefined name table + * + * RETURN: None + * + * DESCRIPTION: Check that the declared parameter count (in ASL/AML) for a + * predefined name is what is expected (matches what is defined in + * the ACPI specification for this predefined name.) + * + ******************************************************************************/ + +void +acpi_ns_check_acpi_compliance(char *pathname, + struct acpi_namespace_node *node, + const union acpi_predefined_info *predefined) +{ + u32 aml_param_count; + u32 required_param_count; + + if (!predefined) { + return; + } + + /* Get the ACPI-required arg count from the predefined info table */ + + required_param_count = + METHOD_GET_ARG_COUNT(predefined->info.argument_list); + + /* + * If this object is not a control method, we can check if the ACPI + * spec requires that it be a method. + */ + if (node->type != ACPI_TYPE_METHOD) { + if (required_param_count > 0) { + + /* Object requires args, must be implemented as a method */ + + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Object (%s) must be a control method with %u arguments", + acpi_ut_get_type_name(node-> + type), + required_param_count)); + } else if (!required_param_count + && !predefined->info.expected_btypes) { + + /* Object requires no args and no return value, must be a method */ + + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Object (%s) must be a control method " + "with no arguments and no return value", + acpi_ut_get_type_name(node-> + type))); + } + + return; + } + + /* + * This is a control method. + * Check that the ASL/AML-defined parameter count for this method + * matches the ACPI-required parameter count + * + * Some methods are allowed to have a "minimum" number of args (_SCP) + * because their definition in ACPI has changed over time. + * + * Note: These are BIOS errors in the declaration of the object + */ + aml_param_count = node->object->method.param_count; + + if (aml_param_count < required_param_count) { + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Insufficient arguments - " + "ASL declared %u, ACPI requires %u", + aml_param_count, + required_param_count)); + } else if ((aml_param_count > required_param_count) + && !(predefined->info. + argument_list & ARG_COUNT_IS_MINIMUM)) { + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Excess arguments - " + "ASL declared %u, ACPI requires %u", + aml_param_count, + required_param_count)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_argument_count + * + * PARAMETERS: pathname - Full pathname to the node (for error msgs) + * node - Namespace node for the method/object + * user_param_count - Number of args passed in by the caller + * predefined - Pointer to entry in predefined name table + * + * RETURN: None + * + * DESCRIPTION: Check that incoming argument count matches the declared + * parameter count (in the ASL/AML) for an object. + * + ******************************************************************************/ + +void +acpi_ns_check_argument_count(char *pathname, + struct acpi_namespace_node *node, + u32 user_param_count, + const union acpi_predefined_info *predefined) +{ + u32 aml_param_count; + u32 required_param_count; + + if (!predefined) { + /* + * Not a predefined name. Check the incoming user argument count + * against the count that is specified in the method/object. + */ + if (node->type != ACPI_TYPE_METHOD) { + if (user_param_count) { + ACPI_INFO_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "%u arguments were passed to a non-method ACPI object (%s)", + user_param_count, + acpi_ut_get_type_name + (node->type))); + } + + return; + } + + /* + * This is a control method. Check the parameter count. + * We can only check the incoming argument count against the + * argument count declared for the method in the ASL/AML. + * + * Emit a message if too few or too many arguments have been passed + * by the caller. + * + * Note: Too many arguments will not cause the method to + * fail. However, the method will fail if there are too few + * arguments and the method attempts to use one of the missing ones. + */ + aml_param_count = node->object->method.param_count; + + if (user_param_count < aml_param_count) { + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Insufficient arguments - " + "Caller passed %u, method requires %u", + user_param_count, + aml_param_count)); + } else if (user_param_count > aml_param_count) { + ACPI_INFO_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Excess arguments - " + "Caller passed %u, method requires %u", + user_param_count, + aml_param_count)); + } + + return; + } + + /* + * This is a predefined name. Validate the user-supplied parameter + * count against the ACPI specification. We don't validate against + * the method itself because what is important here is that the + * caller is in conformance with the spec. (The arg count for the + * method was checked against the ACPI spec earlier.) + * + * Some methods are allowed to have a "minimum" number of args (_SCP) + * because their definition in ACPI has changed over time. + */ + required_param_count = + METHOD_GET_ARG_COUNT(predefined->info.argument_list); + + if (user_param_count < required_param_count) { + ACPI_WARN_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Insufficient arguments - " + "Caller passed %u, ACPI requires %u", + user_param_count, required_param_count)); + } else if ((user_param_count > required_param_count) && + !(predefined->info.argument_list & ARG_COUNT_IS_MINIMUM)) { + ACPI_INFO_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Excess arguments - " + "Caller passed %u, ACPI requires %u", + user_param_count, required_param_count)); + } +} diff --git a/kernel/drivers/acpi/acpica/nsconvert.c b/kernel/drivers/acpi/acpica/nsconvert.c new file mode 100644 index 000000000..1a8b39c8d --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsconvert.c @@ -0,0 +1,446 @@ +/****************************************************************************** + * + * Module Name: nsconvert - Object conversions for objects returned by + * predefined methods + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acinterp.h" +#include "acpredef.h" +#include "amlresrc.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsconvert") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_integer + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a String/Buffer object to an Integer. + * + ******************************************************************************/ +acpi_status +acpi_ns_convert_to_integer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + acpi_status status; + u64 value = 0; + u32 i; + + switch (original_object->common.type) { + case ACPI_TYPE_STRING: + + /* String-to-Integer conversion */ + + status = acpi_ut_strtoul64(original_object->string.pointer, + ACPI_ANY_BASE, &value); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + case ACPI_TYPE_BUFFER: + + /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */ + + if (original_object->buffer.length > 8) { + return (AE_AML_OPERAND_TYPE); + } + + /* Extract each buffer byte to create the integer */ + + for (i = 0; i < original_object->buffer.length; i++) { + value |= + ((u64)original_object->buffer. + pointer[i] << (i * 8)); + } + break; + + default: + + return (AE_AML_OPERAND_TYPE); + } + + new_object = acpi_ut_create_integer_object(value); + if (!new_object) { + return (AE_NO_MEMORY); + } + + *return_object = new_object; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_string + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a Integer/Buffer object to a String. + * + ******************************************************************************/ + +acpi_status +acpi_ns_convert_to_string(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + acpi_size length; + acpi_status status; + + switch (original_object->common.type) { + case ACPI_TYPE_INTEGER: + /* + * Integer-to-String conversion. Commonly, convert + * an integer of value 0 to a NULL string. The last element of + * _BIF and _BIX packages occasionally need this fix. + */ + if (original_object->integer.value == 0) { + + /* Allocate a new NULL string object */ + + new_object = acpi_ut_create_string_object(0); + if (!new_object) { + return (AE_NO_MEMORY); + } + } else { + status = + acpi_ex_convert_to_string(original_object, + &new_object, + ACPI_IMPLICIT_CONVERT_HEX); + if (ACPI_FAILURE(status)) { + return (status); + } + } + break; + + case ACPI_TYPE_BUFFER: + /* + * Buffer-to-String conversion. Use a to_string + * conversion, no transform performed on the buffer data. The best + * example of this is the _BIF method, where the string data from + * the battery is often (incorrectly) returned as buffer object(s). + */ + length = 0; + while ((length < original_object->buffer.length) && + (original_object->buffer.pointer[length])) { + length++; + } + + /* Allocate a new string object */ + + new_object = acpi_ut_create_string_object(length); + if (!new_object) { + return (AE_NO_MEMORY); + } + + /* + * Copy the raw buffer data with no transform. String is already NULL + * terminated at Length+1. + */ + ACPI_MEMCPY(new_object->string.pointer, + original_object->buffer.pointer, length); + break; + + default: + + return (AE_AML_OPERAND_TYPE); + } + + *return_object = new_object; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_buffer + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer. + * + ******************************************************************************/ + +acpi_status +acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + acpi_status status; + union acpi_operand_object **elements; + u32 *dword_buffer; + u32 count; + u32 i; + + switch (original_object->common.type) { + case ACPI_TYPE_INTEGER: + /* + * Integer-to-Buffer conversion. + * Convert the Integer to a packed-byte buffer. _MAT and other + * objects need this sometimes, if a read has been performed on a + * Field object that is less than or equal to the global integer + * size (32 or 64 bits). + */ + status = + acpi_ex_convert_to_buffer(original_object, &new_object); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + case ACPI_TYPE_STRING: + + /* String-to-Buffer conversion. Simple data copy */ + + new_object = + acpi_ut_create_buffer_object(original_object->string. + length); + if (!new_object) { + return (AE_NO_MEMORY); + } + + ACPI_MEMCPY(new_object->buffer.pointer, + original_object->string.pointer, + original_object->string.length); + break; + + case ACPI_TYPE_PACKAGE: + /* + * This case is often seen for predefined names that must return a + * Buffer object with multiple DWORD integers within. For example, + * _FDE and _GTM. The Package can be converted to a Buffer. + */ + + /* All elements of the Package must be integers */ + + elements = original_object->package.elements; + count = original_object->package.count; + + for (i = 0; i < count; i++) { + if ((!*elements) || + ((*elements)->common.type != ACPI_TYPE_INTEGER)) { + return (AE_AML_OPERAND_TYPE); + } + elements++; + } + + /* Create the new buffer object to replace the Package */ + + new_object = acpi_ut_create_buffer_object(ACPI_MUL_4(count)); + if (!new_object) { + return (AE_NO_MEMORY); + } + + /* Copy the package elements (integers) to the buffer as DWORDs */ + + elements = original_object->package.elements; + dword_buffer = ACPI_CAST_PTR(u32, new_object->buffer.pointer); + + for (i = 0; i < count; i++) { + *dword_buffer = (u32)(*elements)->integer.value; + dword_buffer++; + elements++; + } + break; + + default: + + return (AE_AML_OPERAND_TYPE); + } + + *return_object = new_object; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_unicode + * + * PARAMETERS: original_object - ASCII String Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a String object to a Unicode string Buffer. + * + ******************************************************************************/ + +acpi_status +acpi_ns_convert_to_unicode(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + char *ascii_string; + u16 *unicode_buffer; + u32 unicode_length; + u32 i; + + if (!original_object) { + return (AE_OK); + } + + /* If a Buffer was returned, it must be at least two bytes long */ + + if (original_object->common.type == ACPI_TYPE_BUFFER) { + if (original_object->buffer.length < 2) { + return (AE_AML_OPERAND_VALUE); + } + + *return_object = NULL; + return (AE_OK); + } + + /* + * The original object is an ASCII string. Convert this string to + * a unicode buffer. + */ + ascii_string = original_object->string.pointer; + unicode_length = (original_object->string.length * 2) + 2; + + /* Create a new buffer object for the Unicode data */ + + new_object = acpi_ut_create_buffer_object(unicode_length); + if (!new_object) { + return (AE_NO_MEMORY); + } + + unicode_buffer = ACPI_CAST_PTR(u16, new_object->buffer.pointer); + + /* Convert ASCII to Unicode */ + + for (i = 0; i < original_object->string.length; i++) { + unicode_buffer[i] = (u16)ascii_string[i]; + } + + *return_object = new_object; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_resource + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful + * + * DESCRIPTION: Attempt to convert a Integer object to a resource_template + * Buffer. + * + ******************************************************************************/ + +acpi_status +acpi_ns_convert_to_resource(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + u8 *buffer; + + /* + * We can fix the following cases for an expected resource template: + * 1. No return value (interpreter slack mode is disabled) + * 2. A "Return (Zero)" statement + * 3. A "Return empty buffer" statement + * + * We will return a buffer containing a single end_tag + * resource descriptor. + */ + if (original_object) { + switch (original_object->common.type) { + case ACPI_TYPE_INTEGER: + + /* We can only repair an Integer==0 */ + + if (original_object->integer.value) { + return (AE_AML_OPERAND_TYPE); + } + break; + + case ACPI_TYPE_BUFFER: + + if (original_object->buffer.length) { + + /* Additional checks can be added in the future */ + + *return_object = NULL; + return (AE_OK); + } + break; + + case ACPI_TYPE_STRING: + default: + + return (AE_AML_OPERAND_TYPE); + } + } + + /* Create the new buffer object for the resource descriptor */ + + new_object = acpi_ut_create_buffer_object(2); + if (!new_object) { + return (AE_NO_MEMORY); + } + + buffer = ACPI_CAST_PTR(u8, new_object->buffer.pointer); + + /* Initialize the Buffer with a single end_tag descriptor */ + + buffer[0] = (ACPI_RESOURCE_NAME_END_TAG | ASL_RDESC_END_TAG_SIZE); + buffer[1] = 0x00; + + *return_object = new_object; + return (AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/nsdump.c b/kernel/drivers/acpi/acpica/nsdump.c new file mode 100644 index 000000000..d25939350 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsdump.c @@ -0,0 +1,886 @@ +/****************************************************************************** + * + * Module Name: nsdump - table dumping routines for debug + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsdump") + +/* Local prototypes */ +#ifdef ACPI_OBSOLETE_FUNCTIONS +void acpi_ns_dump_root_devices(void); + +static acpi_status +acpi_ns_dump_one_device(acpi_handle obj_handle, + u32 level, void *context, void **return_value); +#endif + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +#ifdef ACPI_FUTURE_USAGE +static acpi_status +acpi_ns_dump_one_object_path(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +static acpi_status +acpi_ns_get_max_depth(acpi_handle obj_handle, + u32 level, void *context, void **return_value); +#endif /* ACPI_FUTURE_USAGE */ + +/******************************************************************************* + * + * FUNCTION: acpi_ns_print_pathname + * + * PARAMETERS: num_segments - Number of ACPI name segments + * pathname - The compressed (internal) path + * + * RETURN: None + * + * DESCRIPTION: Print an object's full namespace pathname + * + ******************************************************************************/ + +void acpi_ns_print_pathname(u32 num_segments, char *pathname) +{ + u32 i; + + ACPI_FUNCTION_NAME(ns_print_pathname); + + /* Check if debug output enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_NAMES, ACPI_NAMESPACE)) { + return; + } + + /* Print the entire name */ + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "[")); + + while (num_segments) { + for (i = 0; i < 4; i++) { + ACPI_IS_PRINT(pathname[i]) ? + acpi_os_printf("%c", pathname[i]) : + acpi_os_printf("?"); + } + + pathname += ACPI_NAME_SIZE; + num_segments--; + if (num_segments) { + acpi_os_printf("."); + } + } + + acpi_os_printf("]\n"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_pathname + * + * PARAMETERS: handle - Object + * msg - Prefix message + * level - Desired debug level + * component - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Print an object's full namespace pathname + * Manages allocation/freeing of a pathname buffer + * + ******************************************************************************/ + +void +acpi_ns_dump_pathname(acpi_handle handle, char *msg, u32 level, u32 component) +{ + + ACPI_FUNCTION_TRACE(ns_dump_pathname); + + /* Do this only if the requested debug level and component are enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(level, component)) { + return_VOID; + } + + /* Convert handle to a full pathname and print it (with supplied message) */ + + acpi_ns_print_node_pathname(handle, msg); + acpi_os_printf("\n"); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_one_object + * + * PARAMETERS: obj_handle - Node to be dumped + * level - Nesting level of the handle + * context - Passed into walk_namespace + * return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Dump a single Node + * This procedure is a user_function called by acpi_ns_walk_namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ns_dump_one_object(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + struct acpi_walk_info *info = (struct acpi_walk_info *)context; + struct acpi_namespace_node *this_node; + union acpi_operand_object *obj_desc = NULL; + acpi_object_type obj_type; + acpi_object_type type; + u32 bytes_to_dump; + u32 dbg_level; + u32 i; + + ACPI_FUNCTION_NAME(ns_dump_one_object); + + /* Is output enabled? */ + + if (!(acpi_dbg_level & info->debug_level)) { + return (AE_OK); + } + + if (!obj_handle) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Null object handle\n")); + return (AE_OK); + } + + this_node = acpi_ns_validate_handle(obj_handle); + if (!this_node) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid object handle %p\n", + obj_handle)); + return (AE_OK); + } + + type = this_node->type; + + /* Check if the owner matches */ + + if ((info->owner_id != ACPI_OWNER_ID_MAX) && + (info->owner_id != this_node->owner_id)) { + return (AE_OK); + } + + if (!(info->display_type & ACPI_DISPLAY_SHORT)) { + + /* Indent the object according to the level */ + + acpi_os_printf("%2d%*s", (u32) level - 1, (int)level * 2, " "); + + /* Check the node type and name */ + + if (type > ACPI_TYPE_LOCAL_MAX) { + ACPI_WARNING((AE_INFO, + "Invalid ACPI Object Type 0x%08X", type)); + } + + acpi_os_printf("%4.4s", acpi_ut_get_node_name(this_node)); + } + + /* Now we can print out the pertinent information */ + + acpi_os_printf(" %-12s %p %2.2X ", + acpi_ut_get_type_name(type), this_node, + this_node->owner_id); + + dbg_level = acpi_dbg_level; + acpi_dbg_level = 0; + obj_desc = acpi_ns_get_attached_object(this_node); + acpi_dbg_level = dbg_level; + + /* Temp nodes are those nodes created by a control method */ + + if (this_node->flags & ANOBJ_TEMPORARY) { + acpi_os_printf("(T) "); + } + + switch (info->display_type & ACPI_DISPLAY_MASK) { + case ACPI_DISPLAY_SUMMARY: + + if (!obj_desc) { + + /* No attached object. Some types should always have an object */ + + switch (type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_METHOD: + + acpi_os_printf(""); + break; + + default: + + break; + } + + acpi_os_printf("\n"); + return (AE_OK); + } + + switch (type) { + case ACPI_TYPE_PROCESSOR: + + acpi_os_printf("ID %02X Len %02X Addr %8.8X%8.8X\n", + obj_desc->processor.proc_id, + obj_desc->processor.length, + ACPI_FORMAT_UINT64(obj_desc->processor. + address)); + break; + + case ACPI_TYPE_DEVICE: + + acpi_os_printf("Notify Object: %p\n", obj_desc); + break; + + case ACPI_TYPE_METHOD: + + acpi_os_printf("Args %X Len %.4X Aml %p\n", + (u32) obj_desc->method.param_count, + obj_desc->method.aml_length, + obj_desc->method.aml_start); + break; + + case ACPI_TYPE_INTEGER: + + acpi_os_printf("= %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(obj_desc->integer. + value)); + break; + + case ACPI_TYPE_PACKAGE: + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + acpi_os_printf("Elements %.2X\n", + obj_desc->package.count); + } else { + acpi_os_printf("[Length not yet evaluated]\n"); + } + break; + + case ACPI_TYPE_BUFFER: + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + acpi_os_printf("Len %.2X", + obj_desc->buffer.length); + + /* Dump some of the buffer */ + + if (obj_desc->buffer.length > 0) { + acpi_os_printf(" ="); + for (i = 0; + (i < obj_desc->buffer.length + && i < 12); i++) { + acpi_os_printf(" %.2hX", + obj_desc->buffer. + pointer[i]); + } + } + acpi_os_printf("\n"); + } else { + acpi_os_printf("[Length not yet evaluated]\n"); + } + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("Len %.2X ", obj_desc->string.length); + acpi_ut_print_string(obj_desc->string.pointer, 32); + acpi_os_printf("\n"); + break; + + case ACPI_TYPE_REGION: + + acpi_os_printf("[%s]", + acpi_ut_get_region_name(obj_desc->region. + space_id)); + if (obj_desc->region.flags & AOPOBJ_DATA_VALID) { + acpi_os_printf(" Addr %8.8X%8.8X Len %.4X\n", + ACPI_FORMAT_UINT64(obj_desc-> + region. + address), + obj_desc->region.length); + } else { + acpi_os_printf + (" [Address/Length not yet evaluated]\n"); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[%s]\n", + acpi_ut_get_reference_name(obj_desc)); + break; + + case ACPI_TYPE_BUFFER_FIELD: + + if (obj_desc->buffer_field.buffer_obj && + obj_desc->buffer_field.buffer_obj->buffer.node) { + acpi_os_printf("Buf [%4.4s]", + acpi_ut_get_node_name(obj_desc-> + buffer_field. + buffer_obj-> + buffer. + node)); + } + break; + + case ACPI_TYPE_LOCAL_REGION_FIELD: + + acpi_os_printf("Rgn [%4.4s]", + acpi_ut_get_node_name(obj_desc-> + common_field. + region_obj->region. + node)); + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + acpi_os_printf("Rgn [%4.4s] Bnk [%4.4s]", + acpi_ut_get_node_name(obj_desc-> + common_field. + region_obj->region. + node), + acpi_ut_get_node_name(obj_desc-> + bank_field. + bank_obj-> + common_field. + node)); + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + acpi_os_printf("Idx [%4.4s] Dat [%4.4s]", + acpi_ut_get_node_name(obj_desc-> + index_field. + index_obj-> + common_field.node), + acpi_ut_get_node_name(obj_desc-> + index_field. + data_obj-> + common_field. + node)); + break; + + case ACPI_TYPE_LOCAL_ALIAS: + case ACPI_TYPE_LOCAL_METHOD_ALIAS: + + acpi_os_printf("Target %4.4s (%p)\n", + acpi_ut_get_node_name(obj_desc), + obj_desc); + break; + + default: + + acpi_os_printf("Object %p\n", obj_desc); + break; + } + + /* Common field handling */ + + switch (type) { + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + acpi_os_printf(" Off %.3X Len %.2X Acc %.2hd\n", + (obj_desc->common_field. + base_byte_offset * 8) + + + obj_desc->common_field. + start_field_bit_offset, + obj_desc->common_field.bit_length, + obj_desc->common_field. + access_byte_width); + break; + + default: + + break; + } + break; + + case ACPI_DISPLAY_OBJECTS: + + acpi_os_printf("O:%p", obj_desc); + if (!obj_desc) { + + /* No attached object, we are done */ + + acpi_os_printf("\n"); + return (AE_OK); + } + + acpi_os_printf("(R%u)", obj_desc->common.reference_count); + + switch (type) { + case ACPI_TYPE_METHOD: + + /* Name is a Method and its AML offset/length are set */ + + acpi_os_printf(" M:%p-%X\n", obj_desc->method.aml_start, + obj_desc->method.aml_length); + break; + + case ACPI_TYPE_INTEGER: + + acpi_os_printf(" I:%8.8X8.8%X\n", + ACPI_FORMAT_UINT64(obj_desc->integer. + value)); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf(" S:%p-%X\n", obj_desc->string.pointer, + obj_desc->string.length); + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf(" B:%p-%X\n", obj_desc->buffer.pointer, + obj_desc->buffer.length); + break; + + default: + + acpi_os_printf("\n"); + break; + } + break; + + default: + acpi_os_printf("\n"); + break; + } + + /* If debug turned off, done */ + + if (!(acpi_dbg_level & ACPI_LV_VALUES)) { + return (AE_OK); + } + + /* If there is an attached object, display it */ + + dbg_level = acpi_dbg_level; + acpi_dbg_level = 0; + obj_desc = acpi_ns_get_attached_object(this_node); + acpi_dbg_level = dbg_level; + + /* Dump attached objects */ + + while (obj_desc) { + obj_type = ACPI_TYPE_INVALID; + acpi_os_printf("Attached Object %p: ", obj_desc); + + /* Decode the type of attached object and dump the contents */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { + case ACPI_DESC_TYPE_NAMED: + + acpi_os_printf("(Ptr to Node)\n"); + bytes_to_dump = sizeof(struct acpi_namespace_node); + ACPI_DUMP_BUFFER(obj_desc, bytes_to_dump); + break; + + case ACPI_DESC_TYPE_OPERAND: + + obj_type = obj_desc->common.type; + + if (obj_type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf + ("(Pointer to ACPI Object type %.2X [UNKNOWN])\n", + obj_type); + bytes_to_dump = 32; + } else { + acpi_os_printf + ("(Pointer to ACPI Object type %.2X [%s])\n", + obj_type, acpi_ut_get_type_name(obj_type)); + bytes_to_dump = + sizeof(union acpi_operand_object); + } + + ACPI_DUMP_BUFFER(obj_desc, bytes_to_dump); + break; + + default: + + break; + } + + /* If value is NOT an internal object, we are done */ + + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != + ACPI_DESC_TYPE_OPERAND) { + goto cleanup; + } + + /* Valid object, get the pointer to next level, if any */ + + switch (obj_type) { + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + /* + * NOTE: takes advantage of common fields between string/buffer + */ + bytes_to_dump = obj_desc->string.length; + obj_desc = (void *)obj_desc->string.pointer; + acpi_os_printf("(Buffer/String pointer %p length %X)\n", + obj_desc, bytes_to_dump); + ACPI_DUMP_BUFFER(obj_desc, bytes_to_dump); + goto cleanup; + + case ACPI_TYPE_BUFFER_FIELD: + + obj_desc = + (union acpi_operand_object *)obj_desc->buffer_field. + buffer_obj; + break; + + case ACPI_TYPE_PACKAGE: + + obj_desc = (void *)obj_desc->package.elements; + break; + + case ACPI_TYPE_METHOD: + + obj_desc = (void *)obj_desc->method.aml_start; + break; + + case ACPI_TYPE_LOCAL_REGION_FIELD: + + obj_desc = (void *)obj_desc->field.region_obj; + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + obj_desc = (void *)obj_desc->bank_field.region_obj; + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + obj_desc = (void *)obj_desc->index_field.index_obj; + break; + + default: + + goto cleanup; + } + + obj_type = ACPI_TYPE_INVALID; /* Terminate loop after next pass */ + } + +cleanup: + acpi_os_printf("\n"); + return (AE_OK); +} + +#ifdef ACPI_FUTURE_USAGE +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_objects + * + * PARAMETERS: type - Object type to be dumped + * display_type - 0 or ACPI_DISPLAY_SUMMARY + * max_depth - Maximum depth of dump. Use ACPI_UINT32_MAX + * for an effectively unlimited depth. + * owner_id - Dump only objects owned by this ID. Use + * ACPI_UINT32_MAX to match all owners. + * start_handle - Where in namespace to start/end search + * + * RETURN: None + * + * DESCRIPTION: Dump typed objects within the loaded namespace. Uses + * acpi_ns_walk_namespace in conjunction with acpi_ns_dump_one_object. + * + ******************************************************************************/ + +void +acpi_ns_dump_objects(acpi_object_type type, + u8 display_type, + u32 max_depth, + acpi_owner_id owner_id, acpi_handle start_handle) +{ + struct acpi_walk_info info; + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + /* + * Just lock the entire namespace for the duration of the dump. + * We don't want any changes to the namespace during this time, + * especially the temporary nodes since we are going to display + * them also. + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not acquire namespace mutex\n"); + return; + } + + info.debug_level = ACPI_LV_TABLES; + info.owner_id = owner_id; + info.display_type = display_type; + + (void)acpi_ns_walk_namespace(type, start_handle, max_depth, + ACPI_NS_WALK_NO_UNLOCK | + ACPI_NS_WALK_TEMP_NODES, + acpi_ns_dump_one_object, NULL, + (void *)&info, NULL); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); +} +#endif /* ACPI_FUTURE_USAGE */ + +#ifdef ACPI_FUTURE_USAGE +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_one_object_path, acpi_ns_get_max_depth + * + * PARAMETERS: obj_handle - Node to be dumped + * level - Nesting level of the handle + * context - Passed into walk_namespace + * return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Dump the full pathname to a namespace object. acp_ns_get_max_depth + * computes the maximum nesting depth in the namespace tree, in + * order to simplify formatting in acpi_ns_dump_one_object_path. + * These procedures are user_functions called by acpi_ns_walk_namespace. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_dump_one_object_path(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + u32 max_level = *((u32 *)context); + char *pathname; + struct acpi_namespace_node *node; + int path_indent; + + if (!obj_handle) { + return (AE_OK); + } + + node = acpi_ns_validate_handle(obj_handle); + if (!node) { + + /* Ignore bad node during namespace walk */ + + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + + path_indent = 1; + if (level <= max_level) { + path_indent = max_level - level + 1; + } + + acpi_os_printf("%2d%*s%-12s%*s", + level, level, " ", acpi_ut_get_type_name(node->type), + path_indent, " "); + + acpi_os_printf("%s\n", &pathname[1]); + ACPI_FREE(pathname); + return (AE_OK); +} + +static acpi_status +acpi_ns_get_max_depth(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + u32 *max_level = (u32 *)context; + + if (level > *max_level) { + *max_level = level; + } + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_object_paths + * + * PARAMETERS: type - Object type to be dumped + * display_type - 0 or ACPI_DISPLAY_SUMMARY + * max_depth - Maximum depth of dump. Use ACPI_UINT32_MAX + * for an effectively unlimited depth. + * owner_id - Dump only objects owned by this ID. Use + * ACPI_UINT32_MAX to match all owners. + * start_handle - Where in namespace to start/end search + * + * RETURN: None + * + * DESCRIPTION: Dump full object pathnames within the loaded namespace. Uses + * acpi_ns_walk_namespace in conjunction with acpi_ns_dump_one_object_path. + * + ******************************************************************************/ + +void +acpi_ns_dump_object_paths(acpi_object_type type, + u8 display_type, + u32 max_depth, + acpi_owner_id owner_id, acpi_handle start_handle) +{ + acpi_status status; + u32 max_level = 0; + + ACPI_FUNCTION_ENTRY(); + + /* + * Just lock the entire namespace for the duration of the dump. + * We don't want any changes to the namespace during this time, + * especially the temporary nodes since we are going to display + * them also. + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not acquire namespace mutex\n"); + return; + } + + /* Get the max depth of the namespace tree, for formatting later */ + + (void)acpi_ns_walk_namespace(type, start_handle, max_depth, + ACPI_NS_WALK_NO_UNLOCK | + ACPI_NS_WALK_TEMP_NODES, + acpi_ns_get_max_depth, NULL, + (void *)&max_level, NULL); + + /* Now dump the entire namespace */ + + (void)acpi_ns_walk_namespace(type, start_handle, max_depth, + ACPI_NS_WALK_NO_UNLOCK | + ACPI_NS_WALK_TEMP_NODES, + acpi_ns_dump_one_object_path, NULL, + (void *)&max_level, NULL); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); +} +#endif /* ACPI_FUTURE_USAGE */ + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_entry + * + * PARAMETERS: handle - Node to be dumped + * debug_level - Output level + * + * RETURN: None + * + * DESCRIPTION: Dump a single Node + * + ******************************************************************************/ + +void acpi_ns_dump_entry(acpi_handle handle, u32 debug_level) +{ + struct acpi_walk_info info; + + ACPI_FUNCTION_ENTRY(); + + info.debug_level = debug_level; + info.owner_id = ACPI_OWNER_ID_MAX; + info.display_type = ACPI_DISPLAY_SUMMARY; + + (void)acpi_ns_dump_one_object(handle, 1, &info, NULL); +} + +#ifdef ACPI_ASL_COMPILER +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_tables + * + * PARAMETERS: search_base - Root of subtree to be dumped, or + * NS_ALL to dump the entire namespace + * max_depth - Maximum depth of dump. Use INT_MAX + * for an effectively unlimited depth. + * + * RETURN: None + * + * DESCRIPTION: Dump the name space, or a portion of it. + * + ******************************************************************************/ + +void acpi_ns_dump_tables(acpi_handle search_base, u32 max_depth) +{ + acpi_handle search_handle = search_base; + + ACPI_FUNCTION_TRACE(ns_dump_tables); + + if (!acpi_gbl_root_node) { + /* + * If the name space has not been initialized, + * there is nothing to dump. + */ + ACPI_DEBUG_PRINT((ACPI_DB_TABLES, + "namespace not initialized!\n")); + return_VOID; + } + + if (ACPI_NS_ALL == search_base) { + + /* Entire namespace */ + + search_handle = acpi_gbl_root_node; + ACPI_DEBUG_PRINT((ACPI_DB_TABLES, "\\\n")); + } + + acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_OBJECTS, max_depth, + ACPI_OWNER_ID_MAX, search_handle); + return_VOID; +} +#endif +#endif diff --git a/kernel/drivers/acpi/acpica/nsdumpdv.c b/kernel/drivers/acpi/acpica/nsdumpdv.c new file mode 100644 index 000000000..7dc367e6f --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsdumpdv.c @@ -0,0 +1,141 @@ +/****************************************************************************** + * + * Module Name: nsdump - table dumping routines for debug + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include + +/* TBD: This entire module is apparently obsolete and should be removed */ + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsdumpdv") +#ifdef ACPI_OBSOLETE_FUNCTIONS +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +#include "acnamesp.h" +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_one_device + * + * PARAMETERS: handle - Node to be dumped + * level - Nesting level of the handle + * context - Passed into walk_namespace + * return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Dump a single Node that represents a device + * This procedure is a user_function called by acpi_ns_walk_namespace. + * + ******************************************************************************/ +static acpi_status +acpi_ns_dump_one_device(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + struct acpi_buffer buffer; + struct acpi_device_info *info; + acpi_status status; + u32 i; + + ACPI_FUNCTION_NAME(ns_dump_one_device); + + status = + acpi_ns_dump_one_object(obj_handle, level, context, return_value); + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_get_object_info(obj_handle, &buffer); + if (ACPI_SUCCESS(status)) { + info = buffer.pointer; + for (i = 0; i < level; i++) { + ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES, " ")); + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES, + " HID: %s, ADR: %8.8X%8.8X, Status: %X\n", + info->hardware_id.value, + ACPI_FORMAT_UINT64(info->address), + info->current_status)); + ACPI_FREE(info); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_root_devices + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Dump all objects of type "device" + * + ******************************************************************************/ + +void acpi_ns_dump_root_devices(void) +{ + acpi_handle sys_bus_handle; + acpi_status status; + + ACPI_FUNCTION_NAME(ns_dump_root_devices); + + /* Only dump the table if tracing is enabled */ + + if (!(ACPI_LV_TABLES & acpi_dbg_level)) { + return; + } + + status = acpi_get_handle(NULL, METHOD_NAME__SB_, &sys_bus_handle); + if (ACPI_FAILURE(status)) { + return; + } + + ACPI_DEBUG_PRINT((ACPI_DB_TABLES, + "Display of all devices in the namespace:\n")); + + status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, sys_bus_handle, + ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, + acpi_ns_dump_one_device, NULL, NULL, + NULL); +} + +#endif +#endif diff --git a/kernel/drivers/acpi/acpica/nseval.c b/kernel/drivers/acpi/acpica/nseval.c new file mode 100644 index 000000000..7bcc68f57 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nseval.c @@ -0,0 +1,494 @@ +/******************************************************************************* + * + * Module Name: nseval - Object evaluation, includes control method execution + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acinterp.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nseval") + +/* Local prototypes */ +static void +acpi_ns_exec_module_code(union acpi_operand_object *method_obj, + struct acpi_evaluate_info *info); + +/******************************************************************************* + * + * FUNCTION: acpi_ns_evaluate + * + * PARAMETERS: info - Evaluation info block, contains: + * prefix_node - Prefix or Method/Object Node to execute + * relative_path - Name of method to execute, If NULL, the + * Node is the object to execute + * parameters - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * parameter_type - Type of Parameter list + * return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * flags - ACPI_IGNORE_RETURN_VALUE to delete return + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method or return the current value of an + * ACPI namespace object. + * + * MUTEX: Locks interpreter + * + ******************************************************************************/ + +acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_evaluate); + + if (!info) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (!info->node) { + /* + * Get the actual namespace node for the target object if we + * need to. Handles these cases: + * + * 1) Null node, valid pathname from root (absolute path) + * 2) Node and valid pathname (path relative to Node) + * 3) Node, Null pathname + */ + status = + acpi_ns_get_node(info->prefix_node, info->relative_pathname, + ACPI_NS_NO_UPSEARCH, &info->node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* + * For a method alias, we must grab the actual method node so that + * proper scoping context will be established before execution. + */ + if (acpi_ns_get_type(info->node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) { + info->node = + ACPI_CAST_PTR(struct acpi_namespace_node, + info->node->object); + } + + /* Complete the info block initialization */ + + info->return_object = NULL; + info->node_flags = info->node->flags; + info->obj_desc = acpi_ns_get_attached_object(info->node); + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s [%p] Value %p\n", + info->relative_pathname, info->node, + acpi_ns_get_attached_object(info->node))); + + /* Get info if we have a predefined name (_HID, etc.) */ + + info->predefined = + acpi_ut_match_predefined_method(info->node->name.ascii); + + /* Get the full pathname to the object, for use in warning messages */ + + info->full_pathname = acpi_ns_get_external_pathname(info->node); + if (!info->full_pathname) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Count the number of arguments being passed in */ + + info->param_count = 0; + if (info->parameters) { + while (info->parameters[info->param_count]) { + info->param_count++; + } + + /* Warn on impossible argument count */ + + if (info->param_count > ACPI_METHOD_NUM_ARGS) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + ACPI_WARN_ALWAYS, + "Excess arguments (%u) - using only %u", + info->param_count, + ACPI_METHOD_NUM_ARGS)); + + info->param_count = ACPI_METHOD_NUM_ARGS; + } + } + + /* + * For predefined names: Check that the declared argument count + * matches the ACPI spec -- otherwise this is a BIOS error. + */ + acpi_ns_check_acpi_compliance(info->full_pathname, info->node, + info->predefined); + + /* + * For all names: Check that the incoming argument count for + * this method/object matches the actual ASL/AML definition. + */ + acpi_ns_check_argument_count(info->full_pathname, info->node, + info->param_count, info->predefined); + + /* For predefined names: Typecheck all incoming arguments */ + + acpi_ns_check_argument_types(info); + + /* + * Three major evaluation cases: + * + * 1) Object types that cannot be evaluated by definition + * 2) The object is a control method -- execute it + * 3) The object is not a method -- just return it's current value + */ + switch (acpi_ns_get_type(info->node)) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_REGION: + case ACPI_TYPE_THERMAL: + case ACPI_TYPE_LOCAL_SCOPE: + /* + * 1) Disallow evaluation of certain object types. For these, + * object evaluation is undefined and not supported. + */ + ACPI_ERROR((AE_INFO, + "%s: Evaluation of object type [%s] is not supported", + info->full_pathname, + acpi_ut_get_type_name(info->node->type))); + + status = AE_TYPE; + goto cleanup; + + case ACPI_TYPE_METHOD: + /* + * 2) Object is a control method - execute it + */ + + /* Verify that there is a method object associated with this node */ + + if (!info->obj_desc) { + ACPI_ERROR((AE_INFO, + "%s: Method has no attached sub-object", + info->full_pathname)); + status = AE_NULL_OBJECT; + goto cleanup; + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "**** Execute method [%s] at AML address %p length %X\n", + info->full_pathname, + info->obj_desc->method.aml_start + 1, + info->obj_desc->method.aml_length - 1)); + + /* + * Any namespace deletion must acquire both the namespace and + * interpreter locks to ensure that no thread is using the portion of + * the namespace that is being deleted. + * + * Execute the method via the interpreter. The interpreter is locked + * here before calling into the AML parser + */ + acpi_ex_enter_interpreter(); + status = acpi_ps_execute_method(info); + acpi_ex_exit_interpreter(); + break; + + default: + /* + * 3) All other non-method objects -- get the current object value + */ + + /* + * Some objects require additional resolution steps (e.g., the Node + * may be a field that must be read, etc.) -- we can't just grab + * the object out of the node. + * + * Use resolve_node_to_value() to get the associated value. + * + * NOTE: we can get away with passing in NULL for a walk state because + * the Node is guaranteed to not be a reference to either a method + * local or a method argument (because this interface is never called + * from a running method.) + * + * Even though we do not directly invoke the interpreter for object + * resolution, we must lock it because we could access an op_region. + * The op_region access code assumes that the interpreter is locked. + */ + acpi_ex_enter_interpreter(); + + /* TBD: resolve_node_to_value has a strange interface, fix */ + + info->return_object = + ACPI_CAST_PTR(union acpi_operand_object, info->node); + + status = + acpi_ex_resolve_node_to_value(ACPI_CAST_INDIRECT_PTR + (struct acpi_namespace_node, + &info->return_object), NULL); + acpi_ex_exit_interpreter(); + + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "Returned object %p [%s]\n", + info->return_object, + acpi_ut_get_object_type_name(info-> + return_object))); + + status = AE_CTRL_RETURN_VALUE; /* Always has a "return value" */ + break; + } + + /* + * For predefined names, check the return value against the ACPI + * specification. Some incorrect return value types are repaired. + */ + (void)acpi_ns_check_return_value(info->node, info, info->param_count, + status, &info->return_object); + + /* Check if there is a return value that must be dealt with */ + + if (status == AE_CTRL_RETURN_VALUE) { + + /* If caller does not want the return value, delete it */ + + if (info->flags & ACPI_IGNORE_RETURN_VALUE) { + acpi_ut_remove_reference(info->return_object); + info->return_object = NULL; + } + + /* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */ + + status = AE_OK; + } + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "*** Completed evaluation of object %s ***\n", + info->relative_pathname)); + +cleanup: + /* + * Namespace was unlocked by the handling acpi_ns* function, so we + * just free the pathname and return + */ + ACPI_FREE(info->full_pathname); + info->full_pathname = NULL; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_exec_module_code_list + * + * PARAMETERS: None + * + * RETURN: None. Exceptions during method execution are ignored, since + * we cannot abort a table load. + * + * DESCRIPTION: Execute all elements of the global module-level code list. + * Each element is executed as a single control method. + * + ******************************************************************************/ + +void acpi_ns_exec_module_code_list(void) +{ + union acpi_operand_object *prev; + union acpi_operand_object *next; + struct acpi_evaluate_info *info; + u32 method_count = 0; + + ACPI_FUNCTION_TRACE(ns_exec_module_code_list); + + /* Exit now if the list is empty */ + + next = acpi_gbl_module_code_list; + if (!next) { + return_VOID; + } + + /* Allocate the evaluation information block */ + + info = ACPI_ALLOCATE(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_VOID; + } + + /* Walk the list, executing each "method" */ + + while (next) { + prev = next; + next = next->method.mutex; + + /* Clear the link field and execute the method */ + + prev->method.mutex = NULL; + acpi_ns_exec_module_code(prev, info); + method_count++; + + /* Delete the (temporary) method object */ + + acpi_ut_remove_reference(prev); + } + + ACPI_INFO((AE_INFO, + "Executed %u blocks of module-level executable AML code", + method_count)); + + ACPI_FREE(info); + acpi_gbl_module_code_list = NULL; + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_exec_module_code + * + * PARAMETERS: method_obj - Object container for the module-level code + * info - Info block for method evaluation + * + * RETURN: None. Exceptions during method execution are ignored, since + * we cannot abort a table load. + * + * DESCRIPTION: Execute a control method containing a block of module-level + * executable AML code. The control method is temporarily + * installed to the root node, then evaluated. + * + ******************************************************************************/ + +static void +acpi_ns_exec_module_code(union acpi_operand_object *method_obj, + struct acpi_evaluate_info *info) +{ + union acpi_operand_object *parent_obj; + struct acpi_namespace_node *parent_node; + acpi_object_type type; + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_exec_module_code); + + /* + * Get the parent node. We cheat by using the next_object field + * of the method object descriptor. + */ + parent_node = ACPI_CAST_PTR(struct acpi_namespace_node, + method_obj->method.next_object); + type = acpi_ns_get_type(parent_node); + + /* + * Get the region handler and save it in the method object. We may need + * this if an operation region declaration causes a _REG method to be run. + * + * We can't do this in acpi_ps_link_module_code because + * acpi_gbl_root_node->Object is NULL at PASS1. + */ + if ((type == ACPI_TYPE_DEVICE) && parent_node->object) { + method_obj->method.dispatch.handler = + parent_node->object->device.handler; + } + + /* Must clear next_object (acpi_ns_attach_object needs the field) */ + + method_obj->method.next_object = NULL; + + /* Initialize the evaluation information block */ + + ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info)); + info->prefix_node = parent_node; + + /* + * Get the currently attached parent object. Add a reference, because the + * ref count will be decreased when the method object is installed to + * the parent node. + */ + parent_obj = acpi_ns_get_attached_object(parent_node); + if (parent_obj) { + acpi_ut_add_reference(parent_obj); + } + + /* Install the method (module-level code) in the parent node */ + + status = acpi_ns_attach_object(parent_node, method_obj, + ACPI_TYPE_METHOD); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Execute the parent node as a control method */ + + status = acpi_ns_evaluate(info); + + ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Executed module-level code at %p\n", + method_obj->method.aml_start)); + + /* Delete a possible implicit return value (in slack mode) */ + + if (info->return_object) { + acpi_ut_remove_reference(info->return_object); + } + + /* Detach the temporary method object */ + + acpi_ns_detach_object(parent_node); + + /* Restore the original parent object */ + + if (parent_obj) { + status = acpi_ns_attach_object(parent_node, parent_obj, type); + } else { + parent_node->type = (u8)type; + } + +exit: + if (parent_obj) { + acpi_ut_remove_reference(parent_obj); + } + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/nsinit.c b/kernel/drivers/acpi/acpica/nsinit.c new file mode 100644 index 000000000..4a85c4517 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsinit.c @@ -0,0 +1,607 @@ +/****************************************************************************** + * + * Module Name: nsinit - namespace initialization + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdispat.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsinit") + +/* Local prototypes */ +static acpi_status +acpi_ns_init_one_object(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +static acpi_status +acpi_ns_init_one_device(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +static acpi_status +acpi_ns_find_ini_methods(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +/******************************************************************************* + * + * FUNCTION: acpi_ns_initialize_objects + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Walk the entire namespace and perform any necessary + * initialization on the objects found therein + * + ******************************************************************************/ + +acpi_status acpi_ns_initialize_objects(void) +{ + acpi_status status; + struct acpi_init_walk_info info; + + ACPI_FUNCTION_TRACE(ns_initialize_objects); + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "**** Starting initialization of namespace objects ****\n")); + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + "Completing Region/Field/Buffer/Package initialization:\n")); + + /* Set all init info to zero */ + + ACPI_MEMSET(&info, 0, sizeof(struct acpi_init_walk_info)); + + /* Walk entire namespace from the supplied root */ + + status = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_ns_init_one_object, + NULL, &info, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace")); + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + " Initialized %u/%u Regions %u/%u Fields %u/%u " + "Buffers %u/%u Packages (%u nodes)\n", + info.op_region_init, info.op_region_count, + info.field_init, info.field_count, + info.buffer_init, info.buffer_count, + info.package_init, info.package_count, + info.object_count)); + + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, + "%u Control Methods found\n%u Op Regions found\n", + info.method_count, info.op_region_count)); + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_initialize_devices + * + * PARAMETERS: None + * + * RETURN: acpi_status + * + * DESCRIPTION: Walk the entire namespace and initialize all ACPI devices. + * This means running _INI on all present devices. + * + * Note: We install PCI config space handler on region access, + * not here. + * + ******************************************************************************/ + +acpi_status acpi_ns_initialize_devices(void) +{ + acpi_status status; + struct acpi_device_walk_info info; + + ACPI_FUNCTION_TRACE(ns_initialize_devices); + + /* Init counters */ + + info.device_count = 0; + info.num_STA = 0; + info.num_INI = 0; + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + "Initializing Device/Processor/Thermal objects " + "and executing _INI/_STA methods:\n")); + + /* Tree analysis: find all subtrees that contain _INI methods */ + + status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, FALSE, + acpi_ns_find_ini_methods, NULL, &info, + NULL); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + /* Allocate the evaluation information block */ + + info.evaluate_info = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info.evaluate_info) { + status = AE_NO_MEMORY; + goto error_exit; + } + + /* + * Execute the "global" _INI method that may appear at the root. This + * support is provided for Windows compatibility (Vista+) and is not + * part of the ACPI specification. + */ + info.evaluate_info->prefix_node = acpi_gbl_root_node; + info.evaluate_info->relative_pathname = METHOD_NAME__INI; + info.evaluate_info->parameters = NULL; + info.evaluate_info->flags = ACPI_IGNORE_RETURN_VALUE; + + status = acpi_ns_evaluate(info.evaluate_info); + if (ACPI_SUCCESS(status)) { + info.num_INI++; + } + + /* Walk namespace to execute all _INIs on present devices */ + + status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, FALSE, + acpi_ns_init_one_device, NULL, &info, + NULL); + + /* + * Any _OSI requests should be completed by now. If the BIOS has + * requested any Windows OSI strings, we will always truncate + * I/O addresses to 16 bits -- for Windows compatibility. + */ + if (acpi_gbl_osi_data >= ACPI_OSI_WIN_2000) { + acpi_gbl_truncate_io_addresses = TRUE; + } + + ACPI_FREE(info.evaluate_info); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + " Executed %u _INI methods requiring %u _STA executions " + "(examined %u objects)\n", + info.num_INI, info.num_STA, info.device_count)); + + return_ACPI_STATUS(status); + +error_exit: + ACPI_EXCEPTION((AE_INFO, status, "During device initialization")); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_init_one_object + * + * PARAMETERS: obj_handle - Node + * level - Current nesting level + * context - Points to a init info struct + * return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every object + * within the namespace. + * + * Currently, the only objects that require initialization are: + * 1) Methods + * 2) Op Regions + * + ******************************************************************************/ + +static acpi_status +acpi_ns_init_one_object(acpi_handle obj_handle, + u32 level, void *context, void **return_value) +{ + acpi_object_type type; + acpi_status status = AE_OK; + struct acpi_init_walk_info *info = + (struct acpi_init_walk_info *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_NAME(ns_init_one_object); + + info->object_count++; + + /* And even then, we are only interested in a few object types */ + + type = acpi_ns_get_type(obj_handle); + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return (AE_OK); + } + + /* Increment counters for object types we are looking for */ + + switch (type) { + case ACPI_TYPE_REGION: + + info->op_region_count++; + break; + + case ACPI_TYPE_BUFFER_FIELD: + + info->field_count++; + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + info->field_count++; + break; + + case ACPI_TYPE_BUFFER: + + info->buffer_count++; + break; + + case ACPI_TYPE_PACKAGE: + + info->package_count++; + break; + + default: + + /* No init required, just exit now */ + + return (AE_OK); + } + + /* If the object is already initialized, nothing else to do */ + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return (AE_OK); + } + + /* Must lock the interpreter before executing AML code */ + + acpi_ex_enter_interpreter(); + + /* + * Each of these types can contain executable AML code within the + * declaration. + */ + switch (type) { + case ACPI_TYPE_REGION: + + info->op_region_init++; + status = acpi_ds_get_region_arguments(obj_desc); + break; + + case ACPI_TYPE_BUFFER_FIELD: + + info->field_init++; + status = acpi_ds_get_buffer_field_arguments(obj_desc); + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + info->field_init++; + status = acpi_ds_get_bank_field_arguments(obj_desc); + break; + + case ACPI_TYPE_BUFFER: + + info->buffer_init++; + status = acpi_ds_get_buffer_arguments(obj_desc); + break; + + case ACPI_TYPE_PACKAGE: + + info->package_init++; + status = acpi_ds_get_package_arguments(obj_desc); + break; + + default: + + /* No other types can get here */ + + break; + } + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not execute arguments for [%4.4s] (%s)", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(type))); + } + + /* + * We ignore errors from above, and always return OK, since we don't want + * to abort the walk on any single error. + */ + acpi_ex_exit_interpreter(); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_find_ini_methods + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: acpi_status + * + * DESCRIPTION: Called during namespace walk. Finds objects named _INI under + * device/processor/thermal objects, and marks the entire subtree + * with a SUBTREE_HAS_INI flag. This flag is used during the + * subsequent device initialization walk to avoid entire subtrees + * that do not contain an _INI. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_find_ini_methods(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_device_walk_info *info = + ACPI_CAST_PTR(struct acpi_device_walk_info, context); + struct acpi_namespace_node *node; + struct acpi_namespace_node *parent_node; + + /* Keep count of device/processor/thermal objects */ + + node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + if ((node->type == ACPI_TYPE_DEVICE) || + (node->type == ACPI_TYPE_PROCESSOR) || + (node->type == ACPI_TYPE_THERMAL)) { + info->device_count++; + return (AE_OK); + } + + /* We are only looking for methods named _INI */ + + if (!ACPI_COMPARE_NAME(node->name.ascii, METHOD_NAME__INI)) { + return (AE_OK); + } + + /* + * The only _INI methods that we care about are those that are + * present under Device, Processor, and Thermal objects. + */ + parent_node = node->parent; + switch (parent_node->type) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + /* Mark parent and bubble up the INI present flag to the root */ + + while (parent_node) { + parent_node->flags |= ANOBJ_SUBTREE_HAS_INI; + parent_node = parent_node->parent; + } + break; + + default: + + break; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_init_one_device + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: acpi_status + * + * DESCRIPTION: This is called once per device soon after ACPI is enabled + * to initialize each device. It determines if the device is + * present, and if so, calls _INI. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_init_one_device(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_device_walk_info *walk_info = + ACPI_CAST_PTR(struct acpi_device_walk_info, context); + struct acpi_evaluate_info *info = walk_info->evaluate_info; + u32 flags; + acpi_status status; + struct acpi_namespace_node *device_node; + + ACPI_FUNCTION_TRACE(ns_init_one_device); + + /* We are interested in Devices, Processors and thermal_zones only */ + + device_node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + if ((device_node->type != ACPI_TYPE_DEVICE) && + (device_node->type != ACPI_TYPE_PROCESSOR) && + (device_node->type != ACPI_TYPE_THERMAL)) { + return_ACPI_STATUS(AE_OK); + } + + /* + * Because of an earlier namespace analysis, all subtrees that contain an + * _INI method are tagged. + * + * If this device subtree does not contain any _INI methods, we + * can exit now and stop traversing this entire subtree. + */ + if (!(device_node->flags & ANOBJ_SUBTREE_HAS_INI)) { + return_ACPI_STATUS(AE_CTRL_DEPTH); + } + + /* + * Run _STA to determine if this device is present and functioning. We + * must know this information for two important reasons (from ACPI spec): + * + * 1) We can only run _INI if the device is present. + * 2) We must abort the device tree walk on this subtree if the device is + * not present and is not functional (we will not examine the children) + * + * The _STA method is not required to be present under the device, we + * assume the device is present if _STA does not exist. + */ + ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname + (ACPI_TYPE_METHOD, device_node, METHOD_NAME__STA)); + + status = acpi_ut_execute_STA(device_node, &flags); + if (ACPI_FAILURE(status)) { + + /* Ignore error and move on to next device */ + + return_ACPI_STATUS(AE_OK); + } + + /* + * Flags == -1 means that _STA was not found. In this case, we assume that + * the device is both present and functional. + * + * From the ACPI spec, description of _STA: + * + * "If a device object (including the processor object) does not have an + * _STA object, then OSPM assumes that all of the above bits are set (in + * other words, the device is present, ..., and functioning)" + */ + if (flags != ACPI_UINT32_MAX) { + walk_info->num_STA++; + } + + /* + * Examine the PRESENT and FUNCTIONING status bits + * + * Note: ACPI spec does not seem to specify behavior for the present but + * not functioning case, so we assume functioning if present. + */ + if (!(flags & ACPI_STA_DEVICE_PRESENT)) { + + /* Device is not present, we must examine the Functioning bit */ + + if (flags & ACPI_STA_DEVICE_FUNCTIONING) { + /* + * Device is not present but is "functioning". In this case, + * we will not run _INI, but we continue to examine the children + * of this device. + * + * From the ACPI spec, description of _STA: (note - no mention + * of whether to run _INI or not on the device in question) + * + * "_STA may return bit 0 clear (not present) with bit 3 set + * (device is functional). This case is used to indicate a valid + * device for which no device driver should be loaded (for example, + * a bridge device.) Children of this device may be present and + * valid. OSPM should continue enumeration below a device whose + * _STA returns this bit combination" + */ + return_ACPI_STATUS(AE_OK); + } else { + /* + * Device is not present and is not functioning. We must abort the + * walk of this subtree immediately -- don't look at the children + * of such a device. + * + * From the ACPI spec, description of _INI: + * + * "If the _STA method indicates that the device is not present, + * OSPM will not run the _INI and will not examine the children + * of the device for _INI methods" + */ + return_ACPI_STATUS(AE_CTRL_DEPTH); + } + } + + /* + * The device is present or is assumed present if no _STA exists. + * Run the _INI if it exists (not required to exist) + * + * Note: We know there is an _INI within this subtree, but it may not be + * under this particular device, it may be lower in the branch. + */ + ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname + (ACPI_TYPE_METHOD, device_node, METHOD_NAME__INI)); + + ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info)); + info->prefix_node = device_node; + info->relative_pathname = METHOD_NAME__INI; + info->parameters = NULL; + info->flags = ACPI_IGNORE_RETURN_VALUE; + + status = acpi_ns_evaluate(info); + + if (ACPI_SUCCESS(status)) { + walk_info->num_INI++; + } +#ifdef ACPI_DEBUG_OUTPUT + else if (status != AE_NOT_FOUND) { + + /* Ignore error and move on to next device */ + + char *scope_name = acpi_ns_get_external_pathname(info->node); + + ACPI_EXCEPTION((AE_INFO, status, "during %s._INI execution", + scope_name)); + ACPI_FREE(scope_name); + } +#endif + + /* Ignore errors from above */ + + status = AE_OK; + + /* + * The _INI method has been run if present; call the Global Initialization + * Handler for this device. + */ + if (acpi_gbl_init_handler) { + status = + acpi_gbl_init_handler(device_node, ACPI_INIT_DEVICE_INI); + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/nsload.c b/kernel/drivers/acpi/acpica/nsload.c new file mode 100644 index 000000000..bd6cd4a81 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsload.c @@ -0,0 +1,314 @@ +/****************************************************************************** + * + * Module Name: nsload - namespace loading/expanding/contracting procedures + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdispat.h" +#include "actables.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsload") + +/* Local prototypes */ +#ifdef ACPI_FUTURE_IMPLEMENTATION +acpi_status acpi_ns_unload_namespace(acpi_handle handle); + +static acpi_status acpi_ns_delete_subtree(acpi_handle start_handle); +#endif + +#ifndef ACPI_NO_METHOD_EXECUTION +/******************************************************************************* + * + * FUNCTION: acpi_ns_load_table + * + * PARAMETERS: table_index - Index for table to be loaded + * node - Owning NS node + * + * RETURN: Status + * + * DESCRIPTION: Load one ACPI table into the namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_load_table); + + /* + * Parse the table and load the namespace with all named + * objects found within. Control methods are NOT parsed + * at this time. In fact, the control methods cannot be + * parsed until the entire namespace is loaded, because + * if a control method makes a forward reference (call) + * to another control method, we can't continue parsing + * because we don't know how many arguments to parse next! + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* If table already loaded into namespace, just return */ + + if (acpi_tb_is_table_loaded(table_index)) { + status = AE_ALREADY_EXISTS; + goto unlock; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "**** Loading table into namespace ****\n")); + + status = acpi_tb_allocate_owner_id(table_index); + if (ACPI_FAILURE(status)) { + goto unlock; + } + + status = acpi_ns_parse_table(table_index, node); + if (ACPI_SUCCESS(status)) { + acpi_tb_set_table_loaded_flag(table_index, TRUE); + } else { + (void)acpi_tb_release_owner_id(table_index); + } + +unlock: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Now we can parse the control methods. We always parse + * them here for a sanity check, and if configured for + * just-in-time parsing, we delete the control method + * parse trees. + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "**** Begin Table Object Initialization\n")); + + status = acpi_ds_initialize_objects(table_index, node); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "**** Completed Table Object Initialization\n")); + + return_ACPI_STATUS(status); +} + +#ifdef ACPI_OBSOLETE_FUNCTIONS +/******************************************************************************* + * + * FUNCTION: acpi_load_namespace + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Load the name space from what ever is pointed to by DSDT. + * (DSDT points to either the BIOS or a buffer.) + * + ******************************************************************************/ + +acpi_status acpi_ns_load_namespace(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_load_name_space); + + /* There must be at least a DSDT installed */ + + if (acpi_gbl_DSDT == NULL) { + ACPI_ERROR((AE_INFO, "DSDT is not in memory")); + return_ACPI_STATUS(AE_NO_ACPI_TABLES); + } + + /* + * Load the namespace. The DSDT is required, + * but the SSDT and PSDT tables are optional. + */ + status = acpi_ns_load_table_by_type(ACPI_TABLE_ID_DSDT); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Ignore exceptions from these */ + + (void)acpi_ns_load_table_by_type(ACPI_TABLE_ID_SSDT); + (void)acpi_ns_load_table_by_type(ACPI_TABLE_ID_PSDT); + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT, + "ACPI Namespace successfully loaded at root %p\n", + acpi_gbl_root_node)); + + return_ACPI_STATUS(status); +} +#endif + +#ifdef ACPI_FUTURE_IMPLEMENTATION +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_subtree + * + * PARAMETERS: start_handle - Handle in namespace where search begins + * + * RETURNS Status + * + * DESCRIPTION: Walks the namespace starting at the given handle and deletes + * all objects, entries, and scopes in the entire subtree. + * + * Namespace/Interpreter should be locked or the subsystem should + * be in shutdown before this routine is called. + * + ******************************************************************************/ + +static acpi_status acpi_ns_delete_subtree(acpi_handle start_handle) +{ + acpi_status status; + acpi_handle child_handle; + acpi_handle parent_handle; + acpi_handle next_child_handle; + acpi_handle dummy; + u32 level; + + ACPI_FUNCTION_TRACE(ns_delete_subtree); + + parent_handle = start_handle; + child_handle = NULL; + level = 1; + + /* + * Traverse the tree of objects until we bubble back up + * to where we started. + */ + while (level > 0) { + + /* Attempt to get the next object in this scope */ + + status = acpi_get_next_object(ACPI_TYPE_ANY, parent_handle, + child_handle, &next_child_handle); + + child_handle = next_child_handle; + + /* Did we get a new object? */ + + if (ACPI_SUCCESS(status)) { + + /* Check if this object has any children */ + + if (ACPI_SUCCESS + (acpi_get_next_object + (ACPI_TYPE_ANY, child_handle, NULL, &dummy))) { + /* + * There is at least one child of this object, + * visit the object + */ + level++; + parent_handle = child_handle; + child_handle = NULL; + } + } else { + /* + * No more children in this object, go back up to + * the object's parent + */ + level--; + + /* Delete all children now */ + + acpi_ns_delete_children(child_handle); + + child_handle = parent_handle; + status = acpi_get_parent(parent_handle, &parent_handle); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + } + + /* Now delete the starting object, and we are done */ + + acpi_ns_remove_node(child_handle); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_unload_name_space + * + * PARAMETERS: handle - Root of namespace subtree to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Shrinks the namespace, typically in response to an undocking + * event. Deletes an entire subtree starting from (and + * including) the given handle. + * + ******************************************************************************/ + +acpi_status acpi_ns_unload_namespace(acpi_handle handle) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_unload_name_space); + + /* Parameter validation */ + + if (!acpi_gbl_root_node) { + return_ACPI_STATUS(AE_NO_NAMESPACE); + } + + if (!handle) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* This function does the real work */ + + status = acpi_ns_delete_subtree(handle); + + return_ACPI_STATUS(status); +} +#endif +#endif diff --git a/kernel/drivers/acpi/acpica/nsnames.c b/kernel/drivers/acpi/acpica/nsnames.c new file mode 100644 index 000000000..d293d9748 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsnames.c @@ -0,0 +1,266 @@ +/******************************************************************************* + * + * Module Name: nsnames - Name manipulation and search + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsnames") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_build_external_path + * + * PARAMETERS: node - NS node whose pathname is needed + * size - Size of the pathname + * *name_buffer - Where to return the pathname + * + * RETURN: Status + * Places the pathname into the name_buffer, in external format + * (name segments separated by path separators) + * + * DESCRIPTION: Generate a full pathaname + * + ******************************************************************************/ +acpi_status +acpi_ns_build_external_path(struct acpi_namespace_node *node, + acpi_size size, char *name_buffer) +{ + acpi_size index; + struct acpi_namespace_node *parent_node; + + ACPI_FUNCTION_ENTRY(); + + /* Special case for root */ + + index = size - 1; + if (index < ACPI_NAME_SIZE) { + name_buffer[0] = AML_ROOT_PREFIX; + name_buffer[1] = 0; + return (AE_OK); + } + + /* Store terminator byte, then build name backwards */ + + parent_node = node; + name_buffer[index] = 0; + + while ((index > ACPI_NAME_SIZE) && (parent_node != acpi_gbl_root_node)) { + index -= ACPI_NAME_SIZE; + + /* Put the name into the buffer */ + + ACPI_MOVE_32_TO_32((name_buffer + index), &parent_node->name); + parent_node = parent_node->parent; + + /* Prefix name with the path separator */ + + index--; + name_buffer[index] = ACPI_PATH_SEPARATOR; + } + + /* Overwrite final separator with the root prefix character */ + + name_buffer[index] = AML_ROOT_PREFIX; + + if (index != 0) { + ACPI_ERROR((AE_INFO, + "Could not construct external pathname; index=%u, size=%u, Path=%s", + (u32) index, (u32) size, &name_buffer[size])); + + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_external_pathname + * + * PARAMETERS: node - Namespace node whose pathname is needed + * + * RETURN: Pointer to storage containing the fully qualified name of + * the node, In external format (name segments separated by path + * separators.) + * + * DESCRIPTION: Used to obtain the full pathname to a namespace node, usually + * for error and debug statements. + * + ******************************************************************************/ + +char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node) +{ + acpi_status status; + char *name_buffer; + acpi_size size; + + ACPI_FUNCTION_TRACE_PTR(ns_get_external_pathname, node); + + /* Calculate required buffer size based on depth below root */ + + size = acpi_ns_get_pathname_length(node); + if (!size) { + return_PTR(NULL); + } + + /* Allocate a buffer to be returned to caller */ + + name_buffer = ACPI_ALLOCATE_ZEROED(size); + if (!name_buffer) { + ACPI_ERROR((AE_INFO, "Could not allocate %u bytes", (u32)size)); + return_PTR(NULL); + } + + /* Build the path in the allocated buffer */ + + status = acpi_ns_build_external_path(node, size, name_buffer); + if (ACPI_FAILURE(status)) { + ACPI_FREE(name_buffer); + return_PTR(NULL); + } + + return_PTR(name_buffer); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_pathname_length + * + * PARAMETERS: node - Namespace node + * + * RETURN: Length of path, including prefix + * + * DESCRIPTION: Get the length of the pathname string for this node + * + ******************************************************************************/ + +acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node) +{ + acpi_size size; + struct acpi_namespace_node *next_node; + + ACPI_FUNCTION_ENTRY(); + + /* + * Compute length of pathname as 5 * number of name segments. + * Go back up the parent tree to the root + */ + size = 0; + next_node = node; + + while (next_node && (next_node != acpi_gbl_root_node)) { + if (ACPI_GET_DESCRIPTOR_TYPE(next_node) != ACPI_DESC_TYPE_NAMED) { + ACPI_ERROR((AE_INFO, + "Invalid Namespace Node (%p) while traversing namespace", + next_node)); + return (0); + } + size += ACPI_PATH_SEGMENT_LENGTH; + next_node = next_node->parent; + } + + if (!size) { + size = 1; /* Root node case */ + } + + return (size + 1); /* +1 for null string terminator */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_handle_to_pathname + * + * PARAMETERS: target_handle - Handle of named object whose name is + * to be found + * buffer - Where the pathname is returned + * + * RETURN: Status, Buffer is filled with pathname if status is AE_OK + * + * DESCRIPTION: Build and return a full namespace pathname + * + ******************************************************************************/ + +acpi_status +acpi_ns_handle_to_pathname(acpi_handle target_handle, + struct acpi_buffer * buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + acpi_size required_size; + + ACPI_FUNCTION_TRACE_PTR(ns_handle_to_pathname, target_handle); + + node = acpi_ns_validate_handle(target_handle); + if (!node) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Determine size required for the caller buffer */ + + required_size = acpi_ns_get_pathname_length(node); + if (!required_size) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer(buffer, required_size); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Build the path in the caller buffer */ + + status = + acpi_ns_build_external_path(node, required_size, buffer->pointer); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s [%X]\n", + (char *)buffer->pointer, (u32) required_size)); + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/nsobject.c b/kernel/drivers/acpi/acpica/nsobject.c new file mode 100644 index 000000000..677bc9330 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsobject.c @@ -0,0 +1,464 @@ +/******************************************************************************* + * + * Module Name: nsobject - Utilities for objects attached to namespace + * table entries + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsobject") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_attach_object + * + * PARAMETERS: node - Parent Node + * object - Object to be attached + * type - Type of object, or ACPI_TYPE_ANY if not + * known + * + * RETURN: Status + * + * DESCRIPTION: Record the given object as the value associated with the + * name whose acpi_handle is passed. If Object is NULL + * and Type is ACPI_TYPE_ANY, set the name as having no value. + * Note: Future may require that the Node->Flags field be passed + * as a parameter. + * + * MUTEX: Assumes namespace is locked + * + ******************************************************************************/ +acpi_status +acpi_ns_attach_object(struct acpi_namespace_node *node, + union acpi_operand_object *object, acpi_object_type type) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *last_obj_desc; + acpi_object_type object_type = ACPI_TYPE_ANY; + + ACPI_FUNCTION_TRACE(ns_attach_object); + + /* + * Parameter validation + */ + if (!node) { + + /* Invalid handle */ + + ACPI_ERROR((AE_INFO, "Null NamedObj handle")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (!object && (ACPI_TYPE_ANY != type)) { + + /* Null object */ + + ACPI_ERROR((AE_INFO, + "Null object, but type not ACPI_TYPE_ANY")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { + + /* Not a name handle */ + + ACPI_ERROR((AE_INFO, "Invalid handle %p [%s]", + node, acpi_ut_get_descriptor_name(node))); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Check if this object is already attached */ + + if (node->object == object) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Obj %p already installed in NameObj %p\n", + object, node)); + + return_ACPI_STATUS(AE_OK); + } + + /* If null object, we will just install it */ + + if (!object) { + obj_desc = NULL; + object_type = ACPI_TYPE_ANY; + } + + /* + * If the source object is a namespace Node with an attached object, + * we will use that (attached) object + */ + else if ((ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED) && + ((struct acpi_namespace_node *)object)->object) { + /* + * Value passed is a name handle and that name has a + * non-null value. Use that name's value and type. + */ + obj_desc = ((struct acpi_namespace_node *)object)->object; + object_type = ((struct acpi_namespace_node *)object)->type; + } + + /* + * Otherwise, we will use the parameter object, but we must type + * it first + */ + else { + obj_desc = (union acpi_operand_object *)object; + + /* Use the given type */ + + object_type = type; + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Installing %p into Node %p [%4.4s]\n", + obj_desc, node, acpi_ut_get_node_name(node))); + + /* Detach an existing attached object if present */ + + if (node->object) { + acpi_ns_detach_object(node); + } + + if (obj_desc) { + /* + * Must increment the new value's reference count + * (if it is an internal object) + */ + acpi_ut_add_reference(obj_desc); + + /* + * Handle objects with multiple descriptors - walk + * to the end of the descriptor list + */ + last_obj_desc = obj_desc; + while (last_obj_desc->common.next_object) { + last_obj_desc = last_obj_desc->common.next_object; + } + + /* Install the object at the front of the object list */ + + last_obj_desc->common.next_object = node->object; + } + + node->type = (u8) object_type; + node->object = obj_desc; + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_detach_object + * + * PARAMETERS: node - A Namespace node whose object will be detached + * + * RETURN: None. + * + * DESCRIPTION: Detach/delete an object associated with a namespace node. + * if the object is an allocated object, it is freed. + * Otherwise, the field is simply cleared. + * + ******************************************************************************/ + +void acpi_ns_detach_object(struct acpi_namespace_node *node) +{ + union acpi_operand_object *obj_desc; + + ACPI_FUNCTION_TRACE(ns_detach_object); + + obj_desc = node->object; + + if (!obj_desc || (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA)) { + return_VOID; + } + + if (node->flags & ANOBJ_ALLOCATED_BUFFER) { + + /* Free the dynamic aml buffer */ + + if (obj_desc->common.type == ACPI_TYPE_METHOD) { + ACPI_FREE(obj_desc->method.aml_start); + } + } + + /* Clear the Node entry in all cases */ + + node->object = NULL; + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_OPERAND) { + + /* Unlink object from front of possible object list */ + + node->object = obj_desc->common.next_object; + + /* Handle possible 2-descriptor object */ + + if (node->object && + (node->object->common.type != ACPI_TYPE_LOCAL_DATA)) { + node->object = node->object->common.next_object; + } + + /* + * Detach the object from any data objects (which are still held by + * the namespace node) + */ + if (obj_desc->common.next_object && + ((obj_desc->common.next_object)->common.type == + ACPI_TYPE_LOCAL_DATA)) { + obj_desc->common.next_object = NULL; + } + } + + /* Reset the node type to untyped */ + + node->type = ACPI_TYPE_ANY; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "Node %p [%4.4s] Object %p\n", + node, acpi_ut_get_node_name(node), obj_desc)); + + /* Remove one reference on the object (and all subobjects) */ + + acpi_ut_remove_reference(obj_desc); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_attached_object + * + * PARAMETERS: node - Namespace node + * + * RETURN: Current value of the object field from the Node whose + * handle is passed + * + * DESCRIPTION: Obtain the object attached to a namespace node. + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ns_get_attached_object(struct + acpi_namespace_node + *node) +{ + ACPI_FUNCTION_TRACE_PTR(ns_get_attached_object, node); + + if (!node) { + ACPI_WARNING((AE_INFO, "Null Node ptr")); + return_PTR(NULL); + } + + if (!node->object || + ((ACPI_GET_DESCRIPTOR_TYPE(node->object) != ACPI_DESC_TYPE_OPERAND) + && (ACPI_GET_DESCRIPTOR_TYPE(node->object) != + ACPI_DESC_TYPE_NAMED)) + || ((node->object)->common.type == ACPI_TYPE_LOCAL_DATA)) { + return_PTR(NULL); + } + + return_PTR(node->object); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_secondary_object + * + * PARAMETERS: node - Namespace node + * + * RETURN: Current value of the object field from the Node whose + * handle is passed. + * + * DESCRIPTION: Obtain a secondary object associated with a namespace node. + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ns_get_secondary_object(union + acpi_operand_object + *obj_desc) +{ + ACPI_FUNCTION_TRACE_PTR(ns_get_secondary_object, obj_desc); + + if ((!obj_desc) || + (obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) || + (!obj_desc->common.next_object) || + ((obj_desc->common.next_object)->common.type == + ACPI_TYPE_LOCAL_DATA)) { + return_PTR(NULL); + } + + return_PTR(obj_desc->common.next_object); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_attach_data + * + * PARAMETERS: node - Namespace node + * handler - Handler to be associated with the data + * data - Data to be attached + * + * RETURN: Status + * + * DESCRIPTION: Low-level attach data. Create and attach a Data object. + * + ******************************************************************************/ + +acpi_status +acpi_ns_attach_data(struct acpi_namespace_node *node, + acpi_object_handler handler, void *data) +{ + union acpi_operand_object *prev_obj_desc; + union acpi_operand_object *obj_desc; + union acpi_operand_object *data_desc; + + /* We only allow one attachment per handler */ + + prev_obj_desc = NULL; + obj_desc = node->object; + while (obj_desc) { + if ((obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) && + (obj_desc->data.handler == handler)) { + return (AE_ALREADY_EXISTS); + } + + prev_obj_desc = obj_desc; + obj_desc = obj_desc->common.next_object; + } + + /* Create an internal object for the data */ + + data_desc = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_DATA); + if (!data_desc) { + return (AE_NO_MEMORY); + } + + data_desc->data.handler = handler; + data_desc->data.pointer = data; + + /* Install the data object */ + + if (prev_obj_desc) { + prev_obj_desc->common.next_object = data_desc; + } else { + node->object = data_desc; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_detach_data + * + * PARAMETERS: node - Namespace node + * handler - Handler associated with the data + * + * RETURN: Status + * + * DESCRIPTION: Low-level detach data. Delete the data node, but the caller + * is responsible for the actual data. + * + ******************************************************************************/ + +acpi_status +acpi_ns_detach_data(struct acpi_namespace_node * node, + acpi_object_handler handler) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *prev_obj_desc; + + prev_obj_desc = NULL; + obj_desc = node->object; + while (obj_desc) { + if ((obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) && + (obj_desc->data.handler == handler)) { + if (prev_obj_desc) { + prev_obj_desc->common.next_object = + obj_desc->common.next_object; + } else { + node->object = obj_desc->common.next_object; + } + + acpi_ut_remove_reference(obj_desc); + return (AE_OK); + } + + prev_obj_desc = obj_desc; + obj_desc = obj_desc->common.next_object; + } + + return (AE_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_attached_data + * + * PARAMETERS: node - Namespace node + * handler - Handler associated with the data + * data - Where the data is returned + * + * RETURN: Status + * + * DESCRIPTION: Low level interface to obtain data previously associated with + * a namespace node. + * + ******************************************************************************/ + +acpi_status +acpi_ns_get_attached_data(struct acpi_namespace_node * node, + acpi_object_handler handler, void **data) +{ + union acpi_operand_object *obj_desc; + + obj_desc = node->object; + while (obj_desc) { + if ((obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) && + (obj_desc->data.handler == handler)) { + *data = obj_desc->data.pointer; + return (AE_OK); + } + + obj_desc = obj_desc->common.next_object; + } + + return (AE_NOT_FOUND); +} diff --git a/kernel/drivers/acpi/acpica/nsparse.c b/kernel/drivers/acpi/acpica/nsparse.c new file mode 100644 index 000000000..c95a11976 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsparse.c @@ -0,0 +1,202 @@ +/****************************************************************************** + * + * Module Name: nsparse - namespace interface to AML parser + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acparser.h" +#include "acdispat.h" +#include "actables.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsparse") + +/******************************************************************************* + * + * FUNCTION: ns_one_complete_parse + * + * PARAMETERS: pass_number - 1 or 2 + * table_desc - The table to be parsed. + * + * RETURN: Status + * + * DESCRIPTION: Perform one complete parse of an ACPI/AML table. + * + ******************************************************************************/ +acpi_status +acpi_ns_one_complete_parse(u32 pass_number, + u32 table_index, + struct acpi_namespace_node *start_node) +{ + union acpi_parse_object *parse_root; + acpi_status status; + u32 aml_length; + u8 *aml_start; + struct acpi_walk_state *walk_state; + struct acpi_table_header *table; + acpi_owner_id owner_id; + + ACPI_FUNCTION_TRACE(ns_one_complete_parse); + + status = acpi_tb_get_owner_id(table_index, &owner_id); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Create and init a Root Node */ + + parse_root = acpi_ps_create_scope_op(); + if (!parse_root) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Create and initialize a new walk state */ + + walk_state = acpi_ds_create_walk_state(owner_id, NULL, NULL, NULL); + if (!walk_state) { + acpi_ps_free_op(parse_root); + return_ACPI_STATUS(AE_NO_MEMORY); + } + + status = acpi_get_table_by_index(table_index, &table); + if (ACPI_FAILURE(status)) { + acpi_ds_delete_walk_state(walk_state); + acpi_ps_free_op(parse_root); + return_ACPI_STATUS(status); + } + + /* Table must consist of at least a complete header */ + + if (table->length < sizeof(struct acpi_table_header)) { + status = AE_BAD_HEADER; + } else { + aml_start = (u8 *) table + sizeof(struct acpi_table_header); + aml_length = table->length - sizeof(struct acpi_table_header); + status = acpi_ds_init_aml_walk(walk_state, parse_root, NULL, + aml_start, aml_length, NULL, + (u8) pass_number); + } + + if (ACPI_FAILURE(status)) { + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + + /* start_node is the default location to load the table */ + + if (start_node && start_node != acpi_gbl_root_node) { + status = + acpi_ds_scope_stack_push(start_node, ACPI_TYPE_METHOD, + walk_state); + if (ACPI_FAILURE(status)) { + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + } + + /* Parse the AML */ + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "*PARSE* pass %u parse\n", + pass_number)); + status = acpi_ps_parse_aml(walk_state); + +cleanup: + acpi_ps_delete_parse_tree(parse_root); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_parse_table + * + * PARAMETERS: table_desc - An ACPI table descriptor for table to parse + * start_node - Where to enter the table into the namespace + * + * RETURN: Status + * + * DESCRIPTION: Parse AML within an ACPI table and return a tree of ops + * + ******************************************************************************/ + +acpi_status +acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_parse_table); + + /* + * AML Parse, pass 1 + * + * In this pass, we load most of the namespace. Control methods + * are not parsed until later. A parse tree is not created. Instead, + * each Parser Op subtree is deleted when it is finished. This saves + * a great deal of memory, and allows a small cache of parse objects + * to service the entire parse. The second pass of the parse then + * performs another complete parse of the AML. + */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n")); + status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1, + table_index, start_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * AML Parse, pass 2 + * + * In this pass, we resolve forward references and other things + * that could not be completed during the first pass. + * Another complete parse of the AML is performed, but the + * overhead of this is compensated for by the fact that the + * parse objects are all cached. + */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 2\n")); + status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2, + table_index, start_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/nspredef.c b/kernel/drivers/acpi/acpica/nspredef.c new file mode 100644 index 000000000..0eb54315b --- /dev/null +++ b/kernel/drivers/acpi/acpica/nspredef.c @@ -0,0 +1,400 @@ +/****************************************************************************** + * + * Module Name: nspredef - Validation of ACPI predefined methods and objects + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define ACPI_CREATE_PREDEFINED_TABLE + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nspredef") + +/******************************************************************************* + * + * This module validates predefined ACPI objects that appear in the namespace, + * at the time they are evaluated (via acpi_evaluate_object). The purpose of this + * validation is to detect problems with BIOS-exposed predefined ACPI objects + * before the results are returned to the ACPI-related drivers. + * + * There are several areas that are validated: + * + * 1) The number of input arguments as defined by the method/object in the + * ASL is validated against the ACPI specification. + * 2) The type of the return object (if any) is validated against the ACPI + * specification. + * 3) For returned package objects, the count of package elements is + * validated, as well as the type of each package element. Nested + * packages are supported. + * + * For any problems found, a warning message is issued. + * + ******************************************************************************/ +/* Local prototypes */ +static acpi_status +acpi_ns_check_reference(struct acpi_evaluate_info *info, + union acpi_operand_object *return_object); + +static u32 acpi_ns_get_bitmapped_type(union acpi_operand_object *return_object); + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_return_value + * + * PARAMETERS: node - Namespace node for the method/object + * info - Method execution information block + * user_param_count - Number of parameters actually passed + * return_status - Status from the object evaluation + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status + * + * DESCRIPTION: Check the value returned from a predefined name. + * + ******************************************************************************/ + +acpi_status +acpi_ns_check_return_value(struct acpi_namespace_node *node, + struct acpi_evaluate_info *info, + u32 user_param_count, + acpi_status return_status, + union acpi_operand_object **return_object_ptr) +{ + acpi_status status; + const union acpi_predefined_info *predefined; + + /* If not a predefined name, we cannot validate the return object */ + + predefined = info->predefined; + if (!predefined) { + return (AE_OK); + } + + /* + * If the method failed or did not actually return an object, we cannot + * validate the return object + */ + if ((return_status != AE_OK) && (return_status != AE_CTRL_RETURN_VALUE)) { + return (AE_OK); + } + + /* + * Return value validation and possible repair. + * + * 1) Don't perform return value validation/repair if this feature + * has been disabled via a global option. + * + * 2) We have a return value, but if one wasn't expected, just exit, + * this is not a problem. For example, if the "Implicit Return" + * feature is enabled, methods will always return a value. + * + * 3) If the return value can be of any type, then we cannot perform + * any validation, just exit. + */ + if (acpi_gbl_disable_auto_repair || + (!predefined->info.expected_btypes) || + (predefined->info.expected_btypes == ACPI_RTYPE_ALL)) { + return (AE_OK); + } + + /* + * Check that the type of the main return object is what is expected + * for this predefined name + */ + status = acpi_ns_check_object_type(info, return_object_ptr, + predefined->info.expected_btypes, + ACPI_NOT_PACKAGE_ELEMENT); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* + * + * 4) If there is no return value and it is optional, just return + * AE_OK (_WAK). + */ + if (!(*return_object_ptr)) { + goto exit; + } + + /* + * For returned Package objects, check the type of all sub-objects. + * Note: Package may have been newly created by call above. + */ + if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) { + info->parent_package = *return_object_ptr; + status = acpi_ns_check_package(info, return_object_ptr); + if (ACPI_FAILURE(status)) { + + /* We might be able to fix some errors */ + + if ((status != AE_AML_OPERAND_TYPE) && + (status != AE_AML_OPERAND_VALUE)) { + goto exit; + } + } + } + + /* + * The return object was OK, or it was successfully repaired above. + * Now make some additional checks such as verifying that package + * objects are sorted correctly (if required) or buffer objects have + * the correct data width (bytes vs. dwords). These repairs are + * performed on a per-name basis, i.e., the code is specific to + * particular predefined names. + */ + status = acpi_ns_complex_repairs(info, node, status, return_object_ptr); + +exit: + /* + * If the object validation failed or if we successfully repaired one + * or more objects, mark the parent node to suppress further warning + * messages during the next evaluation of the same method/object. + */ + if (ACPI_FAILURE(status) || (info->return_flags & ACPI_OBJECT_REPAIRED)) { + node->flags |= ANOBJ_EVALUATED; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_object_type + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * expected_btypes - Bitmap of expected return type(s) + * package_index - Index of object within parent package (if + * applicable - ACPI_NOT_PACKAGE_ELEMENT + * otherwise) + * + * RETURN: Status + * + * DESCRIPTION: Check the type of the return object against the expected object + * type(s). Use of Btype allows multiple expected object types. + * + ******************************************************************************/ + +acpi_status +acpi_ns_check_object_type(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr, + u32 expected_btypes, u32 package_index) +{ + union acpi_operand_object *return_object = *return_object_ptr; + acpi_status status = AE_OK; + char type_buffer[48]; /* Room for 5 types */ + + /* A Namespace node should not get here, but make sure */ + + if (return_object && + ACPI_GET_DESCRIPTOR_TYPE(return_object) == ACPI_DESC_TYPE_NAMED) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Invalid return type - Found a Namespace node [%4.4s] type %s", + return_object->node.name.ascii, + acpi_ut_get_type_name(return_object->node. + type))); + return (AE_AML_OPERAND_TYPE); + } + + /* + * Convert the object type (ACPI_TYPE_xxx) to a bitmapped object type. + * The bitmapped type allows multiple possible return types. + * + * Note, the cases below must handle all of the possible types returned + * from all of the predefined names (including elements of returned + * packages) + */ + info->return_btype = acpi_ns_get_bitmapped_type(return_object); + if (info->return_btype == ACPI_RTYPE_ANY) { + + /* Not one of the supported objects, must be incorrect */ + goto type_error_exit; + } + + /* For reference objects, check that the reference type is correct */ + + if ((info->return_btype & expected_btypes) == ACPI_RTYPE_REFERENCE) { + status = acpi_ns_check_reference(info, return_object); + return (status); + } + + /* Attempt simple repair of the returned object if necessary */ + + status = acpi_ns_simple_repair(info, expected_btypes, + package_index, return_object_ptr); + if (ACPI_SUCCESS(status)) { + return (AE_OK); /* Successful repair */ + } + +type_error_exit: + + /* Create a string with all expected types for this predefined object */ + + acpi_ut_get_expected_return_types(type_buffer, expected_btypes); + + if (!return_object) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Expected return object of type %s", + type_buffer)); + } else if (package_index == ACPI_NOT_PACKAGE_ELEMENT) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Return type mismatch - found %s, expected %s", + acpi_ut_get_object_type_name + (return_object), type_buffer)); + } else { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Return Package type mismatch at index %u - " + "found %s, expected %s", package_index, + acpi_ut_get_object_type_name + (return_object), type_buffer)); + } + + return (AE_AML_OPERAND_TYPE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_reference + * + * PARAMETERS: info - Method execution information block + * return_object - Object returned from the evaluation of a + * method or object + * + * RETURN: Status + * + * DESCRIPTION: Check a returned reference object for the correct reference + * type. The only reference type that can be returned from a + * predefined method is a named reference. All others are invalid. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_check_reference(struct acpi_evaluate_info *info, + union acpi_operand_object *return_object) +{ + + /* + * Check the reference object for the correct reference type (opcode). + * The only type of reference that can be converted to an union acpi_object is + * a reference to a named object (reference class: NAME) + */ + if (return_object->reference.class == ACPI_REFCLASS_NAME) { + return (AE_OK); + } + + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, + "Return type mismatch - unexpected reference object type [%s] %2.2X", + acpi_ut_get_reference_name(return_object), + return_object->reference.class)); + + return (AE_AML_OPERAND_TYPE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_bitmapped_type + * + * PARAMETERS: return_object - Object returned from method/obj evaluation + * + * RETURN: Object return type. ACPI_RTYPE_ANY indicates that the object + * type is not supported. ACPI_RTYPE_NONE indicates that no + * object was returned (return_object is NULL). + * + * DESCRIPTION: Convert object type into a bitmapped object return type. + * + ******************************************************************************/ + +static u32 acpi_ns_get_bitmapped_type(union acpi_operand_object *return_object) +{ + u32 return_btype; + + if (!return_object) { + return (ACPI_RTYPE_NONE); + } + + /* Map acpi_object_type to internal bitmapped type */ + + switch (return_object->common.type) { + case ACPI_TYPE_INTEGER: + + return_btype = ACPI_RTYPE_INTEGER; + break; + + case ACPI_TYPE_BUFFER: + + return_btype = ACPI_RTYPE_BUFFER; + break; + + case ACPI_TYPE_STRING: + + return_btype = ACPI_RTYPE_STRING; + break; + + case ACPI_TYPE_PACKAGE: + + return_btype = ACPI_RTYPE_PACKAGE; + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + return_btype = ACPI_RTYPE_REFERENCE; + break; + + default: + + /* Not one of the supported objects, must be incorrect */ + + return_btype = ACPI_RTYPE_ANY; + break; + } + + return (return_btype); +} diff --git a/kernel/drivers/acpi/acpica/nsprepkg.c b/kernel/drivers/acpi/acpica/nsprepkg.c new file mode 100644 index 000000000..8b79958b7 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsprepkg.c @@ -0,0 +1,663 @@ +/****************************************************************************** + * + * Module Name: nsprepkg - Validation of package objects for predefined names + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsprepkg") + +/* Local prototypes */ +static acpi_status +acpi_ns_check_package_list(struct acpi_evaluate_info *info, + const union acpi_predefined_info *package, + union acpi_operand_object **elements, u32 count); + +static acpi_status +acpi_ns_check_package_elements(struct acpi_evaluate_info *info, + union acpi_operand_object **elements, + u8 type1, + u32 count1, + u8 type2, u32 count2, u32 start_index); + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_package + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status + * + * DESCRIPTION: Check a returned package object for the correct count and + * correct type of all sub-objects. + * + ******************************************************************************/ + +acpi_status +acpi_ns_check_package(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + const union acpi_predefined_info *package; + union acpi_operand_object **elements; + acpi_status status = AE_OK; + u32 expected_count; + u32 count; + u32 i; + + ACPI_FUNCTION_NAME(ns_check_package); + + /* The package info for this name is in the next table entry */ + + package = info->predefined + 1; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "%s Validating return Package of Type %X, Count %X\n", + info->full_pathname, package->ret_info.type, + return_object->package.count)); + + /* + * For variable-length Packages, we can safely remove all embedded + * and trailing NULL package elements + */ + acpi_ns_remove_null_elements(info, package->ret_info.type, + return_object); + + /* Extract package count and elements array */ + + elements = return_object->package.elements; + count = return_object->package.count; + + /* + * Most packages must have at least one element. The only exception + * is the variable-length package (ACPI_PTYPE1_VAR). + */ + if (!count) { + if (package->ret_info.type == ACPI_PTYPE1_VAR) { + return (AE_OK); + } + + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Return Package has no elements (empty)")); + + return (AE_AML_OPERAND_VALUE); + } + + /* + * Decode the type of the expected package contents + * + * PTYPE1 packages contain no subpackages + * PTYPE2 packages contain subpackages + */ + switch (package->ret_info.type) { + case ACPI_PTYPE1_FIXED: + /* + * The package count is fixed and there are no subpackages + * + * If package is too small, exit. + * If package is larger than expected, issue warning but continue + */ + expected_count = + package->ret_info.count1 + package->ret_info.count2; + if (count < expected_count) { + goto package_too_small; + } else if (count > expected_count) { + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Return Package is larger than needed - " + "found %u, expected %u\n", + info->full_pathname, count, + expected_count)); + } + + /* Validate all elements of the returned package */ + + status = acpi_ns_check_package_elements(info, elements, + package->ret_info. + object_type1, + package->ret_info. + count1, + package->ret_info. + object_type2, + package->ret_info. + count2, 0); + break; + + case ACPI_PTYPE1_VAR: + /* + * The package count is variable, there are no subpackages, and all + * elements must be of the same type + */ + for (i = 0; i < count; i++) { + status = acpi_ns_check_object_type(info, elements, + package->ret_info. + object_type1, i); + if (ACPI_FAILURE(status)) { + return (status); + } + elements++; + } + break; + + case ACPI_PTYPE1_OPTION: + /* + * The package count is variable, there are no subpackages. There are + * a fixed number of required elements, and a variable number of + * optional elements. + * + * Check if package is at least as large as the minimum required + */ + expected_count = package->ret_info3.count; + if (count < expected_count) { + goto package_too_small; + } + + /* Variable number of sub-objects */ + + for (i = 0; i < count; i++) { + if (i < package->ret_info3.count) { + + /* These are the required package elements (0, 1, or 2) */ + + status = + acpi_ns_check_object_type(info, elements, + package-> + ret_info3. + object_type[i], + i); + if (ACPI_FAILURE(status)) { + return (status); + } + } else { + /* These are the optional package elements */ + + status = + acpi_ns_check_object_type(info, elements, + package-> + ret_info3. + tail_object_type, + i); + if (ACPI_FAILURE(status)) { + return (status); + } + } + elements++; + } + break; + + case ACPI_PTYPE2_REV_FIXED: + + /* First element is the (Integer) revision */ + + status = acpi_ns_check_object_type(info, elements, + ACPI_RTYPE_INTEGER, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + + elements++; + count--; + + /* Examine the subpackages */ + + status = + acpi_ns_check_package_list(info, package, elements, count); + break; + + case ACPI_PTYPE2_PKG_COUNT: + + /* First element is the (Integer) count of subpackages to follow */ + + status = acpi_ns_check_object_type(info, elements, + ACPI_RTYPE_INTEGER, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Count cannot be larger than the parent package length, but allow it + * to be smaller. The >= accounts for the Integer above. + */ + expected_count = (u32)(*elements)->integer.value; + if (expected_count >= count) { + goto package_too_small; + } + + count = expected_count; + elements++; + + /* Examine the subpackages */ + + status = + acpi_ns_check_package_list(info, package, elements, count); + break; + + case ACPI_PTYPE2: + case ACPI_PTYPE2_FIXED: + case ACPI_PTYPE2_MIN: + case ACPI_PTYPE2_COUNT: + case ACPI_PTYPE2_FIX_VAR: + /* + * These types all return a single Package that consists of a + * variable number of subpackages. + * + * First, ensure that the first element is a subpackage. If not, + * the BIOS may have incorrectly returned the object as a single + * package instead of a Package of Packages (a common error if + * there is only one entry). We may be able to repair this by + * wrapping the returned Package with a new outer Package. + */ + if (*elements + && ((*elements)->common.type != ACPI_TYPE_PACKAGE)) { + + /* Create the new outer package and populate it */ + + status = + acpi_ns_wrap_with_package(info, return_object, + return_object_ptr); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Update locals to point to the new package (of 1 element) */ + + return_object = *return_object_ptr; + elements = return_object->package.elements; + count = 1; + } + + /* Examine the subpackages */ + + status = + acpi_ns_check_package_list(info, package, elements, count); + break; + + case ACPI_PTYPE2_UUID_PAIR: + + /* The package must contain pairs of (UUID + type) */ + + if (count & 1) { + expected_count = count + 1; + goto package_too_small; + } + + while (count > 0) { + status = acpi_ns_check_object_type(info, elements, + package->ret_info. + object_type1, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Validate length of the UUID buffer */ + + if ((*elements)->buffer.length != 16) { + ACPI_WARN_PREDEFINED((AE_INFO, + info->full_pathname, + info->node_flags, + "Invalid length for UUID Buffer")); + return (AE_AML_OPERAND_VALUE); + } + + status = acpi_ns_check_object_type(info, elements + 1, + package->ret_info. + object_type2, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + + elements += 2; + count -= 2; + } + break; + + default: + + /* Should not get here if predefined info table is correct */ + + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Invalid internal return type in table entry: %X", + package->ret_info.type)); + + return (AE_AML_INTERNAL); + } + + return (status); + +package_too_small: + + /* Error exit for the case with an incorrect package count */ + + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, + "Return Package is too small - found %u elements, expected %u", + count, expected_count)); + + return (AE_AML_OPERAND_VALUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_package_list + * + * PARAMETERS: info - Method execution information block + * package - Pointer to package-specific info for method + * elements - Element list of parent package. All elements + * of this list should be of type Package. + * count - Count of subpackages + * + * RETURN: Status + * + * DESCRIPTION: Examine a list of subpackages + * + ******************************************************************************/ + +static acpi_status +acpi_ns_check_package_list(struct acpi_evaluate_info *info, + const union acpi_predefined_info *package, + union acpi_operand_object **elements, u32 count) +{ + union acpi_operand_object *sub_package; + union acpi_operand_object **sub_elements; + acpi_status status; + u32 expected_count; + u32 i; + u32 j; + + /* + * Validate each subpackage in the parent Package + * + * NOTE: assumes list of subpackages contains no NULL elements. + * Any NULL elements should have been removed by earlier call + * to acpi_ns_remove_null_elements. + */ + for (i = 0; i < count; i++) { + sub_package = *elements; + sub_elements = sub_package->package.elements; + info->parent_package = sub_package; + + /* Each sub-object must be of type Package */ + + status = acpi_ns_check_object_type(info, &sub_package, + ACPI_RTYPE_PACKAGE, i); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Examine the different types of expected subpackages */ + + info->parent_package = sub_package; + switch (package->ret_info.type) { + case ACPI_PTYPE2: + case ACPI_PTYPE2_PKG_COUNT: + case ACPI_PTYPE2_REV_FIXED: + + /* Each subpackage has a fixed number of elements */ + + expected_count = + package->ret_info.count1 + package->ret_info.count2; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + + status = + acpi_ns_check_package_elements(info, sub_elements, + package->ret_info. + object_type1, + package->ret_info. + count1, + package->ret_info. + object_type2, + package->ret_info. + count2, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + case ACPI_PTYPE2_FIX_VAR: + /* + * Each subpackage has a fixed number of elements and an + * optional element + */ + expected_count = + package->ret_info.count1 + package->ret_info.count2; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + + status = + acpi_ns_check_package_elements(info, sub_elements, + package->ret_info. + object_type1, + package->ret_info. + count1, + package->ret_info. + object_type2, + sub_package->package. + count - + package->ret_info. + count1, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + case ACPI_PTYPE2_FIXED: + + /* Each subpackage has a fixed length */ + + expected_count = package->ret_info2.count; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + + /* Check the type of each subpackage element */ + + for (j = 0; j < expected_count; j++) { + status = + acpi_ns_check_object_type(info, + &sub_elements[j], + package-> + ret_info2. + object_type[j], + j); + if (ACPI_FAILURE(status)) { + return (status); + } + } + break; + + case ACPI_PTYPE2_MIN: + + /* Each subpackage has a variable but minimum length */ + + expected_count = package->ret_info.count1; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + + /* Check the type of each subpackage element */ + + status = + acpi_ns_check_package_elements(info, sub_elements, + package->ret_info. + object_type1, + sub_package->package. + count, 0, 0, 0); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + case ACPI_PTYPE2_COUNT: + /* + * First element is the (Integer) count of elements, including + * the count field (the ACPI name is num_elements) + */ + status = acpi_ns_check_object_type(info, sub_elements, + ACPI_RTYPE_INTEGER, + 0); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * Make sure package is large enough for the Count and is + * is as large as the minimum size + */ + expected_count = (u32)(*sub_elements)->integer.value; + if (sub_package->package.count < expected_count) { + goto package_too_small; + } + if (sub_package->package.count < + package->ret_info.count1) { + expected_count = package->ret_info.count1; + goto package_too_small; + } + if (expected_count == 0) { + /* + * Either the num_entries element was originally zero or it was + * a NULL element and repaired to an Integer of value zero. + * In either case, repair it by setting num_entries to be the + * actual size of the subpackage. + */ + expected_count = sub_package->package.count; + (*sub_elements)->integer.value = expected_count; + } + + /* Check the type of each subpackage element */ + + status = + acpi_ns_check_package_elements(info, + (sub_elements + 1), + package->ret_info. + object_type1, + (expected_count - 1), + 0, 0, 1); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + default: /* Should not get here, type was validated by caller */ + + return (AE_AML_INTERNAL); + } + + elements++; + } + + return (AE_OK); + +package_too_small: + + /* The subpackage count was smaller than required */ + + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, + "Return SubPackage[%u] is too small - found %u elements, expected %u", + i, sub_package->package.count, expected_count)); + + return (AE_AML_OPERAND_VALUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_package_elements + * + * PARAMETERS: info - Method execution information block + * elements - Pointer to the package elements array + * type1 - Object type for first group + * count1 - Count for first group + * type2 - Object type for second group + * count2 - Count for second group + * start_index - Start of the first group of elements + * + * RETURN: Status + * + * DESCRIPTION: Check that all elements of a package are of the correct object + * type. Supports up to two groups of different object types. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_check_package_elements(struct acpi_evaluate_info *info, + union acpi_operand_object **elements, + u8 type1, + u32 count1, + u8 type2, u32 count2, u32 start_index) +{ + union acpi_operand_object **this_element = elements; + acpi_status status; + u32 i; + + /* + * Up to two groups of package elements are supported by the data + * structure. All elements in each group must be of the same type. + * The second group can have a count of zero. + */ + for (i = 0; i < count1; i++) { + status = acpi_ns_check_object_type(info, this_element, + type1, i + start_index); + if (ACPI_FAILURE(status)) { + return (status); + } + this_element++; + } + + for (i = 0; i < count2; i++) { + status = acpi_ns_check_object_type(info, this_element, + type2, + (i + count1 + start_index)); + if (ACPI_FAILURE(status)) { + return (status); + } + this_element++; + } + + return (AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/nsrepair.c b/kernel/drivers/acpi/acpica/nsrepair.c new file mode 100644 index 000000000..151fcd95b --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsrepair.c @@ -0,0 +1,595 @@ +/****************************************************************************** + * + * Module Name: nsrepair - Repair for objects returned by predefined methods + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acinterp.h" +#include "acpredef.h" +#include "amlresrc.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsrepair") + +/******************************************************************************* + * + * This module attempts to repair or convert objects returned by the + * predefined methods to an object type that is expected, as per the ACPI + * specification. The need for this code is dictated by the many machines that + * return incorrect types for the standard predefined methods. Performing these + * conversions here, in one place, eliminates the need for individual ACPI + * device drivers to do the same. Note: Most of these conversions are different + * than the internal object conversion routines used for implicit object + * conversion. + * + * The following conversions can be performed as necessary: + * + * Integer -> String + * Integer -> Buffer + * String -> Integer + * String -> Buffer + * Buffer -> Integer + * Buffer -> String + * Buffer -> Package of Integers + * Package -> Package of one Package + * + * Additional conversions that are available: + * Convert a null return or zero return value to an end_tag descriptor + * Convert an ASCII string to a Unicode buffer + * + * An incorrect standalone object is wrapped with required outer package + * + * Additional possible repairs: + * Required package elements that are NULL replaced by Integer/String/Buffer + * + ******************************************************************************/ +/* Local prototypes */ +static const struct acpi_simple_repair_info *acpi_ns_match_simple_repair(struct + acpi_namespace_node + *node, + u32 + return_btype, + u32 + package_index); + +/* + * Special but simple repairs for some names. + * + * 2nd argument: Unexpected types that can be repaired + */ +static const struct acpi_simple_repair_info acpi_object_repair_info[] = { + /* Resource descriptor conversions */ + + {"_CRS", + ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER | + ACPI_RTYPE_NONE, + ACPI_NOT_PACKAGE_ELEMENT, + acpi_ns_convert_to_resource}, + {"_DMA", + ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER | + ACPI_RTYPE_NONE, + ACPI_NOT_PACKAGE_ELEMENT, + acpi_ns_convert_to_resource}, + {"_PRS", + ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER | + ACPI_RTYPE_NONE, + ACPI_NOT_PACKAGE_ELEMENT, + acpi_ns_convert_to_resource}, + + /* Unicode conversions */ + + {"_MLS", ACPI_RTYPE_STRING, 1, + acpi_ns_convert_to_unicode}, + {"_STR", ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER, + ACPI_NOT_PACKAGE_ELEMENT, + acpi_ns_convert_to_unicode}, + {{0, 0, 0, 0}, 0, 0, NULL} /* Table terminator */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ns_simple_repair + * + * PARAMETERS: info - Method execution information block + * expected_btypes - Object types expected + * package_index - Index of object within parent package (if + * applicable - ACPI_NOT_PACKAGE_ELEMENT + * otherwise) + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if repair was successful. + * + * DESCRIPTION: Attempt to repair/convert a return object of a type that was + * not expected. + * + ******************************************************************************/ + +acpi_status +acpi_ns_simple_repair(struct acpi_evaluate_info *info, + u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *new_object = NULL; + acpi_status status; + const struct acpi_simple_repair_info *predefined; + + ACPI_FUNCTION_NAME(ns_simple_repair); + + /* + * Special repairs for certain names that are in the repair table. + * Check if this name is in the list of repairable names. + */ + predefined = acpi_ns_match_simple_repair(info->node, + info->return_btype, + package_index); + if (predefined) { + if (!return_object) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + ACPI_WARN_ALWAYS, + "Missing expected return value")); + } + + status = + predefined->object_converter(return_object, &new_object); + if (ACPI_FAILURE(status)) { + + /* A fatal error occurred during a conversion */ + + ACPI_EXCEPTION((AE_INFO, status, + "During return object analysis")); + return (status); + } + if (new_object) { + goto object_repaired; + } + } + + /* + * Do not perform simple object repair unless the return type is not + * expected. + */ + if (info->return_btype & expected_btypes) { + return (AE_OK); + } + + /* + * At this point, we know that the type of the returned object was not + * one of the expected types for this predefined name. Attempt to + * repair the object by converting it to one of the expected object + * types for this predefined name. + */ + + /* + * If there is no return value, check if we require a return value for + * this predefined name. Either one return value is expected, or none, + * for both methods and other objects. + * + * Try to fix if there was no return object. Warning if failed to fix. + */ + if (!return_object) { + if (expected_btypes && (!(expected_btypes & ACPI_RTYPE_NONE))) { + if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { + ACPI_WARN_PREDEFINED((AE_INFO, + info->full_pathname, + ACPI_WARN_ALWAYS, + "Found unexpected NULL package element")); + + status = + acpi_ns_repair_null_element(info, + expected_btypes, + package_index, + return_object_ptr); + if (ACPI_SUCCESS(status)) { + return (AE_OK); /* Repair was successful */ + } + } else { + ACPI_WARN_PREDEFINED((AE_INFO, + info->full_pathname, + ACPI_WARN_ALWAYS, + "Missing expected return value")); + } + + return (AE_AML_NO_RETURN_VALUE); + } + } + + if (expected_btypes & ACPI_RTYPE_INTEGER) { + status = acpi_ns_convert_to_integer(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; + } + } + if (expected_btypes & ACPI_RTYPE_STRING) { + status = acpi_ns_convert_to_string(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; + } + } + if (expected_btypes & ACPI_RTYPE_BUFFER) { + status = acpi_ns_convert_to_buffer(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; + } + } + if (expected_btypes & ACPI_RTYPE_PACKAGE) { + /* + * A package is expected. We will wrap the existing object with a + * new package object. It is often the case that if a variable-length + * package is required, but there is only a single object needed, the + * BIOS will return that object instead of wrapping it with a Package + * object. Note: after the wrapping, the package will be validated + * for correct contents (expected object type or types). + */ + status = + acpi_ns_wrap_with_package(info, return_object, &new_object); + if (ACPI_SUCCESS(status)) { + /* + * The original object just had its reference count + * incremented for being inserted into the new package. + */ + *return_object_ptr = new_object; /* New Package object */ + info->return_flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); + } + } + + /* We cannot repair this object */ + + return (AE_AML_OPERAND_TYPE); + +object_repaired: + + /* Object was successfully repaired */ + + if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { + /* + * The original object is a package element. We need to + * decrement the reference count of the original object, + * for removing it from the package. + * + * However, if the original object was just wrapped with a + * package object as part of the repair, we don't need to + * change the reference count. + */ + if (!(info->return_flags & ACPI_OBJECT_WRAPPED)) { + new_object->common.reference_count = + return_object->common.reference_count; + + if (return_object->common.reference_count > 1) { + return_object->common.reference_count--; + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Converted %s to expected %s at Package index %u\n", + info->full_pathname, + acpi_ut_get_object_type_name(return_object), + acpi_ut_get_object_type_name(new_object), + package_index)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Converted %s to expected %s\n", + info->full_pathname, + acpi_ut_get_object_type_name(return_object), + acpi_ut_get_object_type_name(new_object))); + } + + /* Delete old object, install the new return object */ + + acpi_ut_remove_reference(return_object); + *return_object_ptr = new_object; + info->return_flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_match_simple_repair + * + * PARAMETERS: node - Namespace node for the method/object + * return_btype - Object type that was returned + * package_index - Index of object within parent package (if + * applicable - ACPI_NOT_PACKAGE_ELEMENT + * otherwise) + * + * RETURN: Pointer to entry in repair table. NULL indicates not found. + * + * DESCRIPTION: Check an object name against the repairable object list. + * + *****************************************************************************/ + +static const struct acpi_simple_repair_info *acpi_ns_match_simple_repair(struct + acpi_namespace_node + *node, + u32 + return_btype, + u32 + package_index) +{ + const struct acpi_simple_repair_info *this_name; + + /* Search info table for a repairable predefined method/object name */ + + this_name = acpi_object_repair_info; + while (this_name->object_converter) { + if (ACPI_COMPARE_NAME(node->name.ascii, this_name->name)) { + + /* Check if we can actually repair this name/type combination */ + + if ((return_btype & this_name->unexpected_btypes) && + (package_index == this_name->package_index)) { + return (this_name); + } + + return (NULL); + } + this_name++; + } + + return (NULL); /* Name was not found in the repair table */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_repair_null_element + * + * PARAMETERS: info - Method execution information block + * expected_btypes - Object types expected + * package_index - Index of object within parent package (if + * applicable - ACPI_NOT_PACKAGE_ELEMENT + * otherwise) + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if repair was successful. + * + * DESCRIPTION: Attempt to repair a NULL element of a returned Package object. + * + ******************************************************************************/ + +acpi_status +acpi_ns_repair_null_element(struct acpi_evaluate_info * info, + u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *new_object; + + ACPI_FUNCTION_NAME(ns_repair_null_element); + + /* No repair needed if return object is non-NULL */ + + if (return_object) { + return (AE_OK); + } + + /* + * Attempt to repair a NULL element of a Package object. This applies to + * predefined names that return a fixed-length package and each element + * is required. It does not apply to variable-length packages where NULL + * elements are allowed, especially at the end of the package. + */ + if (expected_btypes & ACPI_RTYPE_INTEGER) { + + /* Need an integer - create a zero-value integer */ + + new_object = acpi_ut_create_integer_object((u64)0); + } else if (expected_btypes & ACPI_RTYPE_STRING) { + + /* Need a string - create a NULL string */ + + new_object = acpi_ut_create_string_object(0); + } else if (expected_btypes & ACPI_RTYPE_BUFFER) { + + /* Need a buffer - create a zero-length buffer */ + + new_object = acpi_ut_create_buffer_object(0); + } else { + /* Error for all other expected types */ + + return (AE_AML_OPERAND_TYPE); + } + + if (!new_object) { + return (AE_NO_MEMORY); + } + + /* Set the reference count according to the parent Package object */ + + new_object->common.reference_count = + info->parent_package->common.reference_count; + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Converted NULL package element to expected %s at index %u\n", + info->full_pathname, + acpi_ut_get_object_type_name(new_object), + package_index)); + + *return_object_ptr = new_object; + info->return_flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_remove_null_elements + * + * PARAMETERS: info - Method execution information block + * package_type - An acpi_return_package_types value + * obj_desc - A Package object + * + * RETURN: None. + * + * DESCRIPTION: Remove all NULL package elements from packages that contain + * a variable number of subpackages. For these types of + * packages, NULL elements can be safely removed. + * + *****************************************************************************/ + +void +acpi_ns_remove_null_elements(struct acpi_evaluate_info *info, + u8 package_type, + union acpi_operand_object *obj_desc) +{ + union acpi_operand_object **source; + union acpi_operand_object **dest; + u32 count; + u32 new_count; + u32 i; + + ACPI_FUNCTION_NAME(ns_remove_null_elements); + + /* + * We can safely remove all NULL elements from these package types: + * PTYPE1_VAR packages contain a variable number of simple data types. + * PTYPE2 packages contain a variable number of subpackages. + */ + switch (package_type) { + case ACPI_PTYPE1_VAR: + case ACPI_PTYPE2: + case ACPI_PTYPE2_COUNT: + case ACPI_PTYPE2_PKG_COUNT: + case ACPI_PTYPE2_FIXED: + case ACPI_PTYPE2_MIN: + case ACPI_PTYPE2_REV_FIXED: + case ACPI_PTYPE2_FIX_VAR: + + break; + + default: + case ACPI_PTYPE1_FIXED: + case ACPI_PTYPE1_OPTION: + return; + } + + count = obj_desc->package.count; + new_count = count; + + source = obj_desc->package.elements; + dest = source; + + /* Examine all elements of the package object, remove nulls */ + + for (i = 0; i < count; i++) { + if (!*source) { + new_count--; + } else { + *dest = *source; + dest++; + } + source++; + } + + /* Update parent package if any null elements were removed */ + + if (new_count < count) { + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Found and removed %u NULL elements\n", + info->full_pathname, (count - new_count))); + + /* NULL terminate list and update the package count */ + + *dest = NULL; + obj_desc->package.count = new_count; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_wrap_with_package + * + * PARAMETERS: info - Method execution information block + * original_object - Pointer to the object to repair. + * obj_desc_ptr - The new package object is returned here + * + * RETURN: Status, new object in *obj_desc_ptr + * + * DESCRIPTION: Repair a common problem with objects that are defined to + * return a variable-length Package of sub-objects. If there is + * only one sub-object, some BIOS code mistakenly simply declares + * the single object instead of a Package with one sub-object. + * This function attempts to repair this error by wrapping a + * Package object around the original object, creating the + * correct and expected Package with one sub-object. + * + * Names that can be repaired in this manner include: + * _ALR, _CSD, _HPX, _MLS, _PLD, _PRT, _PSS, _TRT, _TSS, + * _BCL, _DOD, _FIX, _Sx + * + ******************************************************************************/ + +acpi_status +acpi_ns_wrap_with_package(struct acpi_evaluate_info *info, + union acpi_operand_object *original_object, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_operand_object *pkg_obj_desc; + + ACPI_FUNCTION_NAME(ns_wrap_with_package); + + /* + * Create the new outer package and populate it. The new package will + * have a single element, the lone sub-object. + */ + pkg_obj_desc = acpi_ut_create_package_object(1); + if (!pkg_obj_desc) { + return (AE_NO_MEMORY); + } + + pkg_obj_desc->package.elements[0] = original_object; + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Wrapped %s with expected Package object\n", + info->full_pathname, + acpi_ut_get_object_type_name(original_object))); + + /* Return the new object in the object pointer */ + + *obj_desc_ptr = pkg_obj_desc; + info->return_flags |= ACPI_OBJECT_REPAIRED | ACPI_OBJECT_WRAPPED; + return (AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/nsrepair2.c b/kernel/drivers/acpi/acpica/nsrepair2.c new file mode 100644 index 000000000..c30672d23 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsrepair2.c @@ -0,0 +1,979 @@ +/****************************************************************************** + * + * Module Name: nsrepair2 - Repair for objects returned by specific + * predefined methods + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsrepair2") + +/* + * Information structure and handler for ACPI predefined names that can + * be repaired on a per-name basis. + */ +typedef +acpi_status(*acpi_repair_function) (struct acpi_evaluate_info * info, + union acpi_operand_object + **return_object_ptr); + +typedef struct acpi_repair_info { + char name[ACPI_NAME_SIZE]; + acpi_repair_function repair_function; + +} acpi_repair_info; + +/* Local prototypes */ + +static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct + acpi_namespace_node + *node); + +static acpi_status +acpi_ns_repair_ALR(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_CID(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_CST(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_FDE(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_HID(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_PRT(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_PSS(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_TSS(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, + union acpi_operand_object *return_object, + u32 start_index, + u32 expected_count, + u32 sort_index, + u8 sort_direction, char *sort_key_name); + +/* Values for sort_direction above */ + +#define ACPI_SORT_ASCENDING 0 +#define ACPI_SORT_DESCENDING 1 + +static void +acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index); + +static void +acpi_ns_sort_list(union acpi_operand_object **elements, + u32 count, u32 index, u8 sort_direction); + +/* + * This table contains the names of the predefined methods for which we can + * perform more complex repairs. + * + * As necessary: + * + * _ALR: Sort the list ascending by ambient_illuminance + * _CID: Strings: uppercase all, remove any leading asterisk + * _CST: Sort the list ascending by C state type + * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs + * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs + * _HID: Strings: uppercase all, remove any leading asterisk + * _PRT: Fix reversed source_name and source_index + * _PSS: Sort the list descending by Power + * _TSS: Sort the list descending by Power + * + * Names that must be packages, but cannot be sorted: + * + * _BCL: Values are tied to the Package index where they appear, and cannot + * be moved or sorted. These index values are used for _BQC and _BCM. + * However, we can fix the case where a buffer is returned, by converting + * it to a Package of integers. + */ +static const struct acpi_repair_info acpi_ns_repairable_names[] = { + {"_ALR", acpi_ns_repair_ALR}, + {"_CID", acpi_ns_repair_CID}, + {"_CST", acpi_ns_repair_CST}, + {"_FDE", acpi_ns_repair_FDE}, + {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */ + {"_HID", acpi_ns_repair_HID}, + {"_PRT", acpi_ns_repair_PRT}, + {"_PSS", acpi_ns_repair_PSS}, + {"_TSS", acpi_ns_repair_TSS}, + {{0, 0, 0, 0}, NULL} /* Table terminator */ +}; + +#define ACPI_FDE_FIELD_COUNT 5 +#define ACPI_FDE_BYTE_BUFFER_SIZE 5 +#define ACPI_FDE_DWORD_BUFFER_SIZE (ACPI_FDE_FIELD_COUNT * sizeof (u32)) + +/****************************************************************************** + * + * FUNCTION: acpi_ns_complex_repairs + * + * PARAMETERS: info - Method execution information block + * node - Namespace node for the method/object + * validate_status - Original status of earlier validation + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if repair was successful. If name is not + * matched, validate_status is returned. + * + * DESCRIPTION: Attempt to repair/convert a return object of a type that was + * not expected. + * + *****************************************************************************/ + +acpi_status +acpi_ns_complex_repairs(struct acpi_evaluate_info *info, + struct acpi_namespace_node *node, + acpi_status validate_status, + union acpi_operand_object **return_object_ptr) +{ + const struct acpi_repair_info *predefined; + acpi_status status; + + /* Check if this name is in the list of repairable names */ + + predefined = acpi_ns_match_complex_repair(node); + if (!predefined) { + return (validate_status); + } + + status = predefined->repair_function(info, return_object_ptr); + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_match_complex_repair + * + * PARAMETERS: node - Namespace node for the method/object + * + * RETURN: Pointer to entry in repair table. NULL indicates not found. + * + * DESCRIPTION: Check an object name against the repairable object list. + * + *****************************************************************************/ + +static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct + acpi_namespace_node + *node) +{ + const struct acpi_repair_info *this_name; + + /* Search info table for a repairable predefined method/object name */ + + this_name = acpi_ns_repairable_names; + while (this_name->repair_function) { + if (ACPI_COMPARE_NAME(node->name.ascii, this_name->name)) { + return (this_name); + } + this_name++; + } + + return (NULL); /* Not found */ +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_ALR + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _ALR object. If necessary, sort the object list + * ascending by the ambient illuminance values. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_ALR(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + acpi_status status; + + status = acpi_ns_check_sorted_list(info, return_object, 0, 2, 1, + ACPI_SORT_ASCENDING, + "AmbientIlluminance"); + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_FDE + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _FDE and _GTM objects. The expected return + * value is a Buffer of 5 DWORDs. This function repairs a common + * problem where the return value is a Buffer of BYTEs, not + * DWORDs. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_FDE(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *buffer_object; + u8 *byte_buffer; + u32 *dword_buffer; + u32 i; + + ACPI_FUNCTION_NAME(ns_repair_FDE); + + switch (return_object->common.type) { + case ACPI_TYPE_BUFFER: + + /* This is the expected type. Length should be (at least) 5 DWORDs */ + + if (return_object->buffer.length >= ACPI_FDE_DWORD_BUFFER_SIZE) { + return (AE_OK); + } + + /* We can only repair if we have exactly 5 BYTEs */ + + if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Incorrect return buffer length %u, expected %u", + return_object->buffer.length, + ACPI_FDE_DWORD_BUFFER_SIZE)); + + return (AE_AML_OPERAND_TYPE); + } + + /* Create the new (larger) buffer object */ + + buffer_object = + acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE); + if (!buffer_object) { + return (AE_NO_MEMORY); + } + + /* Expand each byte to a DWORD */ + + byte_buffer = return_object->buffer.pointer; + dword_buffer = + ACPI_CAST_PTR(u32, buffer_object->buffer.pointer); + + for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++) { + *dword_buffer = (u32) *byte_buffer; + dword_buffer++; + byte_buffer++; + } + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s Expanded Byte Buffer to expected DWord Buffer\n", + info->full_pathname)); + break; + + default: + + return (AE_AML_OPERAND_TYPE); + } + + /* Delete the original return object, return the new buffer object */ + + acpi_ut_remove_reference(return_object); + *return_object_ptr = buffer_object; + + info->return_flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_CID + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _CID object. If a string, ensure that all + * letters are uppercase and that there is no leading asterisk. + * If a Package, ensure same for all string elements. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_CID(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + acpi_status status; + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object **element_ptr; + union acpi_operand_object *original_element; + u16 original_ref_count; + u32 i; + + /* Check for _CID as a simple string */ + + if (return_object->common.type == ACPI_TYPE_STRING) { + status = acpi_ns_repair_HID(info, return_object_ptr); + return (status); + } + + /* Exit if not a Package */ + + if (return_object->common.type != ACPI_TYPE_PACKAGE) { + return (AE_OK); + } + + /* Examine each element of the _CID package */ + + element_ptr = return_object->package.elements; + for (i = 0; i < return_object->package.count; i++) { + original_element = *element_ptr; + original_ref_count = original_element->common.reference_count; + + status = acpi_ns_repair_HID(info, element_ptr); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Take care with reference counts */ + + if (original_element != *element_ptr) { + + /* Element was replaced */ + + (*element_ptr)->common.reference_count = + original_ref_count; + + acpi_ut_remove_reference(original_element); + } + + element_ptr++; + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_CST + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _CST object: + * 1. Sort the list ascending by C state type + * 2. Ensure type cannot be zero + * 3. A subpackage count of zero means _CST is meaningless + * 4. Count must match the number of C state subpackages + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_CST(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object **outer_elements; + u32 outer_element_count; + union acpi_operand_object *obj_desc; + acpi_status status; + u8 removing; + u32 i; + + ACPI_FUNCTION_NAME(ns_repair_CST); + + /* + * Check if the C-state type values are proportional. + */ + outer_element_count = return_object->package.count - 1; + i = 0; + while (i < outer_element_count) { + outer_elements = &return_object->package.elements[i + 1]; + removing = FALSE; + + if ((*outer_elements)->package.count == 0) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "SubPackage[%u] - removing entry due to zero count", + i)); + removing = TRUE; + goto remove_element; + } + + obj_desc = (*outer_elements)->package.elements[1]; /* Index1 = Type */ + if ((u32)obj_desc->integer.value == 0) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "SubPackage[%u] - removing entry due to invalid Type(0)", + i)); + removing = TRUE; + } + +remove_element: + if (removing) { + acpi_ns_remove_element(return_object, i + 1); + outer_element_count--; + } else { + i++; + } + } + + /* Update top-level package count, Type "Integer" checked elsewhere */ + + obj_desc = return_object->package.elements[0]; + obj_desc->integer.value = outer_element_count; + + /* + * Entries (subpackages) in the _CST Package must be sorted by the + * C-state type, in ascending order. + */ + status = acpi_ns_check_sorted_list(info, return_object, 1, 4, 1, + ACPI_SORT_ASCENDING, "C-State Type"); + if (ACPI_FAILURE(status)) { + return (status); + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_HID + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _HID object. If a string, ensure that all + * letters are uppercase and that there is no leading asterisk. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_HID(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *new_string; + char *source; + char *dest; + + ACPI_FUNCTION_NAME(ns_repair_HID); + + /* We only care about string _HID objects (not integers) */ + + if (return_object->common.type != ACPI_TYPE_STRING) { + return (AE_OK); + } + + if (return_object->string.length == 0) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "Invalid zero-length _HID or _CID string")); + + /* Return AE_OK anyway, let driver handle it */ + + info->return_flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); + } + + /* It is simplest to always create a new string object */ + + new_string = acpi_ut_create_string_object(return_object->string.length); + if (!new_string) { + return (AE_NO_MEMORY); + } + + /* + * Remove a leading asterisk if present. For some unknown reason, there + * are many machines in the field that contains IDs like this. + * + * Examples: "*PNP0C03", "*ACPI0003" + */ + source = return_object->string.pointer; + if (*source == '*') { + source++; + new_string->string.length--; + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Removed invalid leading asterisk\n", + info->full_pathname)); + } + + /* + * Copy and uppercase the string. From the ACPI 5.0 specification: + * + * A valid PNP ID must be of the form "AAA####" where A is an uppercase + * letter and # is a hex digit. A valid ACPI ID must be of the form + * "NNNN####" where N is an uppercase letter or decimal digit, and + * # is a hex digit. + */ + for (dest = new_string->string.pointer; *source; dest++, source++) { + *dest = (char)ACPI_TOUPPER(*source); + } + + acpi_ut_remove_reference(return_object); + *return_object_ptr = new_string; + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_PRT + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _PRT object. If necessary, fix reversed + * source_name and source_index field, a common BIOS bug. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_PRT(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *package_object = *return_object_ptr; + union acpi_operand_object **top_object_list; + union acpi_operand_object **sub_object_list; + union acpi_operand_object *obj_desc; + union acpi_operand_object *sub_package; + u32 element_count; + u32 index; + + /* Each element in the _PRT package is a subpackage */ + + top_object_list = package_object->package.elements; + element_count = package_object->package.count; + + /* Examine each subpackage */ + + for (index = 0; index < element_count; index++, top_object_list++) { + sub_package = *top_object_list; + sub_object_list = sub_package->package.elements; + + /* Check for minimum required element count */ + + if (sub_package->package.count < 4) { + continue; + } + + /* + * If the BIOS has erroneously reversed the _PRT source_name (index 2) + * and the source_index (index 3), fix it. _PRT is important enough to + * workaround this BIOS error. This also provides compatibility with + * other ACPI implementations. + */ + obj_desc = sub_object_list[3]; + if (!obj_desc || (obj_desc->common.type != ACPI_TYPE_INTEGER)) { + sub_object_list[3] = sub_object_list[2]; + sub_object_list[2] = obj_desc; + info->return_flags |= ACPI_OBJECT_REPAIRED; + + ACPI_WARN_PREDEFINED((AE_INFO, + info->full_pathname, + info->node_flags, + "PRT[%X]: Fixed reversed SourceName and SourceIndex", + index)); + } + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_PSS + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _PSS object. If necessary, sort the object list + * by the CPU frequencies. Check that the power dissipation values + * are all proportional to CPU frequency (i.e., sorting by + * frequency should be the same as sorting by power.) + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_PSS(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object **outer_elements; + u32 outer_element_count; + union acpi_operand_object **elements; + union acpi_operand_object *obj_desc; + u32 previous_value; + acpi_status status; + u32 i; + + /* + * Entries (subpackages) in the _PSS Package must be sorted by power + * dissipation, in descending order. If it appears that the list is + * incorrectly sorted, sort it. We sort by cpu_frequency, since this + * should be proportional to the power. + */ + status = acpi_ns_check_sorted_list(info, return_object, 0, 6, 0, + ACPI_SORT_DESCENDING, + "CpuFrequency"); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* + * We now know the list is correctly sorted by CPU frequency. Check if + * the power dissipation values are proportional. + */ + previous_value = ACPI_UINT32_MAX; + outer_elements = return_object->package.elements; + outer_element_count = return_object->package.count; + + for (i = 0; i < outer_element_count; i++) { + elements = (*outer_elements)->package.elements; + obj_desc = elements[1]; /* Index1 = power_dissipation */ + + if ((u32) obj_desc->integer.value > previous_value) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "SubPackage[%u,%u] - suspicious power dissipation values", + i - 1, i)); + } + + previous_value = (u32) obj_desc->integer.value; + outer_elements++; + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_TSS + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list + * descending by the power dissipation values. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_TSS(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + acpi_status status; + struct acpi_namespace_node *node; + + /* + * We can only sort the _TSS return package if there is no _PSS in the + * same scope. This is because if _PSS is present, the ACPI specification + * dictates that the _TSS Power Dissipation field is to be ignored, and + * therefore some BIOSs leave garbage values in the _TSS Power field(s). + * In this case, it is best to just return the _TSS package as-is. + * (May, 2011) + */ + status = acpi_ns_get_node(info->node, "^_PSS", + ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_SUCCESS(status)) { + return (AE_OK); + } + + status = acpi_ns_check_sorted_list(info, return_object, 0, 5, 1, + ACPI_SORT_DESCENDING, + "PowerDissipation"); + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_check_sorted_list + * + * PARAMETERS: info - Method execution information block + * return_object - Pointer to the top-level returned object + * start_index - Index of the first subpackage + * expected_count - Minimum length of each subpackage + * sort_index - Subpackage entry to sort on + * sort_direction - Ascending or descending + * sort_key_name - Name of the sort_index field + * + * RETURN: Status. AE_OK if the list is valid and is sorted correctly or + * has been repaired by sorting the list. + * + * DESCRIPTION: Check if the package list is valid and sorted correctly by the + * sort_index. If not, then sort the list. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, + union acpi_operand_object *return_object, + u32 start_index, + u32 expected_count, + u32 sort_index, + u8 sort_direction, char *sort_key_name) +{ + u32 outer_element_count; + union acpi_operand_object **outer_elements; + union acpi_operand_object **elements; + union acpi_operand_object *obj_desc; + u32 i; + u32 previous_value; + + ACPI_FUNCTION_NAME(ns_check_sorted_list); + + /* The top-level object must be a package */ + + if (return_object->common.type != ACPI_TYPE_PACKAGE) { + return (AE_AML_OPERAND_TYPE); + } + + /* + * NOTE: assumes list of subpackages contains no NULL elements. + * Any NULL elements should have been removed by earlier call + * to acpi_ns_remove_null_elements. + */ + outer_element_count = return_object->package.count; + if (!outer_element_count || start_index >= outer_element_count) { + return (AE_AML_PACKAGE_LIMIT); + } + + outer_elements = &return_object->package.elements[start_index]; + outer_element_count -= start_index; + + previous_value = 0; + if (sort_direction == ACPI_SORT_DESCENDING) { + previous_value = ACPI_UINT32_MAX; + } + + /* Examine each subpackage */ + + for (i = 0; i < outer_element_count; i++) { + + /* Each element of the top-level package must also be a package */ + + if ((*outer_elements)->common.type != ACPI_TYPE_PACKAGE) { + return (AE_AML_OPERAND_TYPE); + } + + /* Each subpackage must have the minimum length */ + + if ((*outer_elements)->package.count < expected_count) { + return (AE_AML_PACKAGE_LIMIT); + } + + elements = (*outer_elements)->package.elements; + obj_desc = elements[sort_index]; + + if (obj_desc->common.type != ACPI_TYPE_INTEGER) { + return (AE_AML_OPERAND_TYPE); + } + + /* + * The list must be sorted in the specified order. If we detect a + * discrepancy, sort the entire list. + */ + if (((sort_direction == ACPI_SORT_ASCENDING) && + (obj_desc->integer.value < previous_value)) || + ((sort_direction == ACPI_SORT_DESCENDING) && + (obj_desc->integer.value > previous_value))) { + acpi_ns_sort_list(&return_object->package. + elements[start_index], + outer_element_count, sort_index, + sort_direction); + + info->return_flags |= ACPI_OBJECT_REPAIRED; + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Repaired unsorted list - now sorted by %s\n", + info->full_pathname, sort_key_name)); + return (AE_OK); + } + + previous_value = (u32) obj_desc->integer.value; + outer_elements++; + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_sort_list + * + * PARAMETERS: elements - Package object element list + * count - Element count for above + * index - Sort by which package element + * sort_direction - Ascending or Descending sort + * + * RETURN: None + * + * DESCRIPTION: Sort the objects that are in a package element list. + * + * NOTE: Assumes that all NULL elements have been removed from the package, + * and that all elements have been verified to be of type Integer. + * + *****************************************************************************/ + +static void +acpi_ns_sort_list(union acpi_operand_object **elements, + u32 count, u32 index, u8 sort_direction) +{ + union acpi_operand_object *obj_desc1; + union acpi_operand_object *obj_desc2; + union acpi_operand_object *temp_obj; + u32 i; + u32 j; + + /* Simple bubble sort */ + + for (i = 1; i < count; i++) { + for (j = (count - 1); j >= i; j--) { + obj_desc1 = elements[j - 1]->package.elements[index]; + obj_desc2 = elements[j]->package.elements[index]; + + if (((sort_direction == ACPI_SORT_ASCENDING) && + (obj_desc1->integer.value > + obj_desc2->integer.value)) + || ((sort_direction == ACPI_SORT_DESCENDING) + && (obj_desc1->integer.value < + obj_desc2->integer.value))) { + temp_obj = elements[j - 1]; + elements[j - 1] = elements[j]; + elements[j] = temp_obj; + } + } + } +} + +/****************************************************************************** + * + * FUNCTION: acpi_ns_remove_element + * + * PARAMETERS: obj_desc - Package object element list + * index - Index of element to remove + * + * RETURN: None + * + * DESCRIPTION: Remove the requested element of a package and delete it. + * + *****************************************************************************/ + +static void +acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index) +{ + union acpi_operand_object **source; + union acpi_operand_object **dest; + u32 count; + u32 new_count; + u32 i; + + ACPI_FUNCTION_NAME(ns_remove_element); + + count = obj_desc->package.count; + new_count = count - 1; + + source = obj_desc->package.elements; + dest = source; + + /* Examine all elements of the package object, remove matched index */ + + for (i = 0; i < count; i++) { + if (i == index) { + acpi_ut_remove_reference(*source); /* Remove one ref for being in pkg */ + acpi_ut_remove_reference(*source); + } else { + *dest = *source; + dest++; + } + source++; + } + + /* NULL terminate list and update the package count */ + + *dest = NULL; + obj_desc->package.count = new_count; +} diff --git a/kernel/drivers/acpi/acpica/nssearch.c b/kernel/drivers/acpi/acpica/nssearch.c new file mode 100644 index 000000000..4a9d4a660 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nssearch.c @@ -0,0 +1,399 @@ +/******************************************************************************* + * + * Module Name: nssearch - Namespace search + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#ifdef ACPI_ASL_COMPILER +#include "amlcode.h" +#endif + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nssearch") + +/* Local prototypes */ +static acpi_status +acpi_ns_search_parent_tree(u32 target_name, + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_namespace_node **return_node); + +/******************************************************************************* + * + * FUNCTION: acpi_ns_search_one_scope + * + * PARAMETERS: target_name - Ascii ACPI name to search for + * parent_node - Starting node where search will begin + * type - Object type to match + * return_node - Where the matched Named obj is returned + * + * RETURN: Status + * + * DESCRIPTION: Search a single level of the namespace. Performs a + * simple search of the specified level, and does not add + * entries or search parents. + * + * + * Named object lists are built (and subsequently dumped) in the + * order in which the names are encountered during the namespace load; + * + * All namespace searching is linear in this implementation, but + * could be easily modified to support any improved search + * algorithm. However, the linear search was chosen for simplicity + * and because the trees are small and the other interpreter + * execution overhead is relatively high. + * + * Note: CPU execution analysis has shown that the AML interpreter spends + * a very small percentage of its time searching the namespace. Therefore, + * the linear search seems to be sufficient, as there would seem to be + * little value in improving the search. + * + ******************************************************************************/ + +acpi_status +acpi_ns_search_one_scope(u32 target_name, + struct acpi_namespace_node *parent_node, + acpi_object_type type, + struct acpi_namespace_node **return_node) +{ + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(ns_search_one_scope); + +#ifdef ACPI_DEBUG_OUTPUT + if (ACPI_LV_NAMES & acpi_dbg_level) { + char *scope_name; + + scope_name = acpi_ns_get_external_pathname(parent_node); + if (scope_name) { + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Searching %s (%p) For [%4.4s] (%s)\n", + scope_name, parent_node, + ACPI_CAST_PTR(char, &target_name), + acpi_ut_get_type_name(type))); + + ACPI_FREE(scope_name); + } + } +#endif + + /* + * Search for name at this namespace level, which is to say that we + * must search for the name among the children of this object + */ + node = parent_node->child; + while (node) { + + /* Check for match against the name */ + + if (node->name.integer == target_name) { + + /* Resolve a control method alias if any */ + + if (acpi_ns_get_type(node) == + ACPI_TYPE_LOCAL_METHOD_ALIAS) { + node = + ACPI_CAST_PTR(struct acpi_namespace_node, + node->object); + } + + /* Found matching entry */ + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Name [%4.4s] (%s) %p found in scope [%4.4s] %p\n", + ACPI_CAST_PTR(char, &target_name), + acpi_ut_get_type_name(node->type), + node, + acpi_ut_get_node_name(parent_node), + parent_node)); + + *return_node = node; + return_ACPI_STATUS(AE_OK); + } + + /* Didn't match name, move on to the next peer object */ + + node = node->peer; + } + + /* Searched entire namespace level, not found */ + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Name [%4.4s] (%s) not found in search in scope [%4.4s] " + "%p first child %p\n", + ACPI_CAST_PTR(char, &target_name), + acpi_ut_get_type_name(type), + acpi_ut_get_node_name(parent_node), parent_node, + parent_node->child)); + + return_ACPI_STATUS(AE_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_search_parent_tree + * + * PARAMETERS: target_name - Ascii ACPI name to search for + * node - Starting node where search will begin + * type - Object type to match + * return_node - Where the matched Node is returned + * + * RETURN: Status + * + * DESCRIPTION: Called when a name has not been found in the current namespace + * level. Before adding it or giving up, ACPI scope rules require + * searching enclosing scopes in cases identified by acpi_ns_local(). + * + * "A name is located by finding the matching name in the current + * name space, and then in the parent name space. If the parent + * name space does not contain the name, the search continues + * recursively until either the name is found or the name space + * does not have a parent (the root of the name space). This + * indicates that the name is not found" (From ACPI Specification, + * section 5.3) + * + ******************************************************************************/ + +static acpi_status +acpi_ns_search_parent_tree(u32 target_name, + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_namespace_node **return_node) +{ + acpi_status status; + struct acpi_namespace_node *parent_node; + + ACPI_FUNCTION_TRACE(ns_search_parent_tree); + + parent_node = node->parent; + + /* + * If there is no parent (i.e., we are at the root) or type is "local", + * we won't be searching the parent tree. + */ + if (!parent_node) { + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "[%4.4s] has no parent\n", + ACPI_CAST_PTR(char, &target_name))); + return_ACPI_STATUS(AE_NOT_FOUND); + } + + if (acpi_ns_local(type)) { + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "[%4.4s] type [%s] must be local to this scope (no parent search)\n", + ACPI_CAST_PTR(char, &target_name), + acpi_ut_get_type_name(type))); + return_ACPI_STATUS(AE_NOT_FOUND); + } + + /* Search the parent tree */ + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "Searching parent [%4.4s] for [%4.4s]\n", + acpi_ut_get_node_name(parent_node), + ACPI_CAST_PTR(char, &target_name))); + + /* Search parents until target is found or we have backed up to the root */ + + while (parent_node) { + /* + * Search parent scope. Use TYPE_ANY because we don't care about the + * object type at this point, we only care about the existence of + * the actual name we are searching for. Typechecking comes later. + */ + status = + acpi_ns_search_one_scope(target_name, parent_node, + ACPI_TYPE_ANY, return_node); + if (ACPI_SUCCESS(status)) { + return_ACPI_STATUS(status); + } + + /* Not found here, go up another level (until we reach the root) */ + + parent_node = parent_node->parent; + } + + /* Not found in parent tree */ + + return_ACPI_STATUS(AE_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_search_and_enter + * + * PARAMETERS: target_name - Ascii ACPI name to search for (4 chars) + * walk_state - Current state of the walk + * node - Starting node where search will begin + * interpreter_mode - Add names only in ACPI_MODE_LOAD_PASS_x. + * Otherwise,search only. + * type - Object type to match + * flags - Flags describing the search restrictions + * return_node - Where the Node is returned + * + * RETURN: Status + * + * DESCRIPTION: Search for a name segment in a single namespace level, + * optionally adding it if it is not found. If the passed + * Type is not Any and the type previously stored in the + * entry was Any (i.e. unknown), update the stored type. + * + * In ACPI_IMODE_EXECUTE, search only. + * In other modes, search and add if not found. + * + ******************************************************************************/ + +acpi_status +acpi_ns_search_and_enter(u32 target_name, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + acpi_interpreter_mode interpreter_mode, + acpi_object_type type, + u32 flags, struct acpi_namespace_node **return_node) +{ + acpi_status status; + struct acpi_namespace_node *new_node; + + ACPI_FUNCTION_TRACE(ns_search_and_enter); + + /* Parameter validation */ + + if (!node || !target_name || !return_node) { + ACPI_ERROR((AE_INFO, + "Null parameter: Node %p Name 0x%X ReturnNode %p", + node, target_name, return_node)); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * Name must consist of valid ACPI characters. We will repair the name if + * necessary because we don't want to abort because of this, but we want + * all namespace names to be printable. A warning message is appropriate. + * + * This issue came up because there are in fact machines that exhibit + * this problem, and we want to be able to enable ACPI support for them, + * even though there are a few bad names. + */ + acpi_ut_repair_name(ACPI_CAST_PTR(char, &target_name)); + + /* Try to find the name in the namespace level specified by the caller */ + + *return_node = ACPI_ENTRY_NOT_FOUND; + status = acpi_ns_search_one_scope(target_name, node, type, return_node); + if (status != AE_NOT_FOUND) { + /* + * If we found it AND the request specifies that a find is an error, + * return the error + */ + if ((status == AE_OK) && (flags & ACPI_NS_ERROR_IF_FOUND)) { + status = AE_ALREADY_EXISTS; + } +#ifdef ACPI_ASL_COMPILER + if (*return_node && (*return_node)->type == ACPI_TYPE_ANY) { + (*return_node)->flags |= ANOBJ_IS_EXTERNAL; + } +#endif + + /* Either found it or there was an error: finished either way */ + + return_ACPI_STATUS(status); + } + + /* + * The name was not found. If we are NOT performing the first pass + * (name entry) of loading the namespace, search the parent tree (all the + * way to the root if necessary.) We don't want to perform the parent + * search when the namespace is actually being loaded. We want to perform + * the search when namespace references are being resolved (load pass 2) + * and during the execution phase. + */ + if ((interpreter_mode != ACPI_IMODE_LOAD_PASS1) && + (flags & ACPI_NS_SEARCH_PARENT)) { + /* + * Not found at this level - search parent tree according to the + * ACPI specification + */ + status = + acpi_ns_search_parent_tree(target_name, node, type, + return_node); + if (ACPI_SUCCESS(status)) { + return_ACPI_STATUS(status); + } + } + + /* In execute mode, just search, never add names. Exit now */ + + if (interpreter_mode == ACPI_IMODE_EXECUTE) { + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "%4.4s Not found in %p [Not adding]\n", + ACPI_CAST_PTR(char, &target_name), node)); + + return_ACPI_STATUS(AE_NOT_FOUND); + } + + /* Create the new named object */ + + new_node = acpi_ns_create_node(target_name); + if (!new_node) { + return_ACPI_STATUS(AE_NO_MEMORY); + } +#ifdef ACPI_ASL_COMPILER + + /* Node is an object defined by an External() statement */ + + if (flags & ACPI_NS_EXTERNAL || + (walk_state && walk_state->opcode == AML_SCOPE_OP)) { + new_node->flags |= ANOBJ_IS_EXTERNAL; + } +#endif + + if (flags & ACPI_NS_TEMPORARY) { + new_node->flags |= ANOBJ_TEMPORARY; + } + + /* Install the new object into the parent's list of children */ + + acpi_ns_install_node(walk_state, node, new_node, type); + *return_node = new_node; + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/nsutils.c b/kernel/drivers/acpi/acpica/nsutils.c new file mode 100644 index 000000000..6ad02008c --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsutils.c @@ -0,0 +1,730 @@ +/****************************************************************************** + * + * Module Name: nsutils - Utilities for accessing ACPI namespace, accessing + * parents and siblings and Scope manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsutils") + +/* Local prototypes */ +#ifdef ACPI_OBSOLETE_FUNCTIONS +acpi_name acpi_ns_find_parent_name(struct acpi_namespace_node *node_to_search); +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ns_print_node_pathname + * + * PARAMETERS: node - Object + * message - Prefix message + * + * DESCRIPTION: Print an object's full namespace pathname + * Manages allocation/freeing of a pathname buffer + * + ******************************************************************************/ + +void +acpi_ns_print_node_pathname(struct acpi_namespace_node *node, + const char *message) +{ + struct acpi_buffer buffer; + acpi_status status; + + if (!node) { + acpi_os_printf("[NULL NAME]"); + return; + } + + /* Convert handle to full pathname and print it (with supplied message) */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + + status = acpi_ns_handle_to_pathname(node, &buffer); + if (ACPI_SUCCESS(status)) { + if (message) { + acpi_os_printf("%s ", message); + } + + acpi_os_printf("[%s] (Node %p)", (char *)buffer.pointer, node); + ACPI_FREE(buffer.pointer); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_type + * + * PARAMETERS: node - Parent Node to be examined + * + * RETURN: Type field from Node whose handle is passed + * + * DESCRIPTION: Return the type of a Namespace node + * + ******************************************************************************/ + +acpi_object_type acpi_ns_get_type(struct acpi_namespace_node * node) +{ + ACPI_FUNCTION_TRACE(ns_get_type); + + if (!node) { + ACPI_WARNING((AE_INFO, "Null Node parameter")); + return_UINT8(ACPI_TYPE_ANY); + } + + return_UINT8(node->type); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_local + * + * PARAMETERS: type - A namespace object type + * + * RETURN: LOCAL if names must be found locally in objects of the + * passed type, 0 if enclosing scopes should be searched + * + * DESCRIPTION: Returns scope rule for the given object type. + * + ******************************************************************************/ + +u32 acpi_ns_local(acpi_object_type type) +{ + ACPI_FUNCTION_TRACE(ns_local); + + if (!acpi_ut_valid_object_type(type)) { + + /* Type code out of range */ + + ACPI_WARNING((AE_INFO, "Invalid Object Type 0x%X", type)); + return_UINT32(ACPI_NS_NORMAL); + } + + return_UINT32(acpi_gbl_ns_properties[type] & ACPI_NS_LOCAL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_internal_name_length + * + * PARAMETERS: info - Info struct initialized with the + * external name pointer. + * + * RETURN: None + * + * DESCRIPTION: Calculate the length of the internal (AML) namestring + * corresponding to the external (ASL) namestring. + * + ******************************************************************************/ + +void acpi_ns_get_internal_name_length(struct acpi_namestring_info *info) +{ + const char *next_external_char; + u32 i; + + ACPI_FUNCTION_ENTRY(); + + next_external_char = info->external_name; + info->num_carats = 0; + info->num_segments = 0; + info->fully_qualified = FALSE; + + /* + * For the internal name, the required length is 4 bytes per segment, plus + * 1 each for root_prefix, multi_name_prefix_op, segment count, trailing null + * (which is not really needed, but no there's harm in putting it there) + * + * strlen() + 1 covers the first name_seg, which has no path separator + */ + if (ACPI_IS_ROOT_PREFIX(*next_external_char)) { + info->fully_qualified = TRUE; + next_external_char++; + + /* Skip redundant root_prefix, like \\_SB.PCI0.SBRG.EC0 */ + + while (ACPI_IS_ROOT_PREFIX(*next_external_char)) { + next_external_char++; + } + } else { + /* Handle Carat prefixes */ + + while (ACPI_IS_PARENT_PREFIX(*next_external_char)) { + info->num_carats++; + next_external_char++; + } + } + + /* + * Determine the number of ACPI name "segments" by counting the number of + * path separators within the string. Start with one segment since the + * segment count is [(# separators) + 1], and zero separators is ok. + */ + if (*next_external_char) { + info->num_segments = 1; + for (i = 0; next_external_char[i]; i++) { + if (ACPI_IS_PATH_SEPARATOR(next_external_char[i])) { + info->num_segments++; + } + } + } + + info->length = (ACPI_NAME_SIZE * info->num_segments) + + 4 + info->num_carats; + + info->next_external_char = next_external_char; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_build_internal_name + * + * PARAMETERS: info - Info struct fully initialized + * + * RETURN: Status + * + * DESCRIPTION: Construct the internal (AML) namestring + * corresponding to the external (ASL) namestring. + * + ******************************************************************************/ + +acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info) +{ + u32 num_segments = info->num_segments; + char *internal_name = info->internal_name; + const char *external_name = info->next_external_char; + char *result = NULL; + u32 i; + + ACPI_FUNCTION_TRACE(ns_build_internal_name); + + /* Setup the correct prefixes, counts, and pointers */ + + if (info->fully_qualified) { + internal_name[0] = AML_ROOT_PREFIX; + + if (num_segments <= 1) { + result = &internal_name[1]; + } else if (num_segments == 2) { + internal_name[1] = AML_DUAL_NAME_PREFIX; + result = &internal_name[2]; + } else { + internal_name[1] = AML_MULTI_NAME_PREFIX_OP; + internal_name[2] = (char)num_segments; + result = &internal_name[3]; + } + } else { + /* + * Not fully qualified. + * Handle Carats first, then append the name segments + */ + i = 0; + if (info->num_carats) { + for (i = 0; i < info->num_carats; i++) { + internal_name[i] = AML_PARENT_PREFIX; + } + } + + if (num_segments <= 1) { + result = &internal_name[i]; + } else if (num_segments == 2) { + internal_name[i] = AML_DUAL_NAME_PREFIX; + result = &internal_name[(acpi_size) i + 1]; + } else { + internal_name[i] = AML_MULTI_NAME_PREFIX_OP; + internal_name[(acpi_size) i + 1] = (char)num_segments; + result = &internal_name[(acpi_size) i + 2]; + } + } + + /* Build the name (minus path separators) */ + + for (; num_segments; num_segments--) { + for (i = 0; i < ACPI_NAME_SIZE; i++) { + if (ACPI_IS_PATH_SEPARATOR(*external_name) || + (*external_name == 0)) { + + /* Pad the segment with underscore(s) if segment is short */ + + result[i] = '_'; + } else { + /* Convert the character to uppercase and save it */ + + result[i] = + (char)ACPI_TOUPPER((int)*external_name); + external_name++; + } + } + + /* Now we must have a path separator, or the pathname is bad */ + + if (!ACPI_IS_PATH_SEPARATOR(*external_name) && + (*external_name != 0)) { + return_ACPI_STATUS(AE_BAD_PATHNAME); + } + + /* Move on the next segment */ + + external_name++; + result += ACPI_NAME_SIZE; + } + + /* Terminate the string */ + + *result = 0; + + if (info->fully_qualified) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Returning [%p] (abs) \"\\%s\"\n", + internal_name, internal_name)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Returning [%p] (rel) \"%s\"\n", + internal_name, internal_name)); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_internalize_name + * + * PARAMETERS: *external_name - External representation of name + * **Converted name - Where to return the resulting + * internal represention of the name + * + * RETURN: Status + * + * DESCRIPTION: Convert an external representation (e.g. "\_PR_.CPU0") + * to internal form (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30) + * + *******************************************************************************/ + +acpi_status +acpi_ns_internalize_name(const char *external_name, char **converted_name) +{ + char *internal_name; + struct acpi_namestring_info info; + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_internalize_name); + + if ((!external_name) || (*external_name == 0) || (!converted_name)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Get the length of the new internal name */ + + info.external_name = external_name; + acpi_ns_get_internal_name_length(&info); + + /* We need a segment to store the internal name */ + + internal_name = ACPI_ALLOCATE_ZEROED(info.length); + if (!internal_name) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Build the name */ + + info.internal_name = internal_name; + status = acpi_ns_build_internal_name(&info); + if (ACPI_FAILURE(status)) { + ACPI_FREE(internal_name); + return_ACPI_STATUS(status); + } + + *converted_name = internal_name; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_externalize_name + * + * PARAMETERS: internal_name_length - Lenth of the internal name below + * internal_name - Internal representation of name + * converted_name_length - Where the length is returned + * converted_name - Where the resulting external name + * is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert internal name (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30) + * to its external (printable) form (e.g. "\_PR_.CPU0") + * + ******************************************************************************/ + +acpi_status +acpi_ns_externalize_name(u32 internal_name_length, + const char *internal_name, + u32 * converted_name_length, char **converted_name) +{ + u32 names_index = 0; + u32 num_segments = 0; + u32 required_length; + u32 prefix_length = 0; + u32 i = 0; + u32 j = 0; + + ACPI_FUNCTION_TRACE(ns_externalize_name); + + if (!internal_name_length || !internal_name || !converted_name) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Check for a prefix (one '\' | one or more '^') */ + + switch (internal_name[0]) { + case AML_ROOT_PREFIX: + + prefix_length = 1; + break; + + case AML_PARENT_PREFIX: + + for (i = 0; i < internal_name_length; i++) { + if (ACPI_IS_PARENT_PREFIX(internal_name[i])) { + prefix_length = i + 1; + } else { + break; + } + } + + if (i == internal_name_length) { + prefix_length = i; + } + + break; + + default: + + break; + } + + /* + * Check for object names. Note that there could be 0-255 of these + * 4-byte elements. + */ + if (prefix_length < internal_name_length) { + switch (internal_name[prefix_length]) { + case AML_MULTI_NAME_PREFIX_OP: + + /* 4-byte names */ + + names_index = prefix_length + 2; + num_segments = (u8) + internal_name[(acpi_size) prefix_length + 1]; + break; + + case AML_DUAL_NAME_PREFIX: + + /* Two 4-byte names */ + + names_index = prefix_length + 1; + num_segments = 2; + break; + + case 0: + + /* null_name */ + + names_index = 0; + num_segments = 0; + break; + + default: + + /* one 4-byte name */ + + names_index = prefix_length; + num_segments = 1; + break; + } + } + + /* + * Calculate the length of converted_name, which equals the length + * of the prefix, length of all object names, length of any required + * punctuation ('.') between object names, plus the NULL terminator. + */ + required_length = prefix_length + (4 * num_segments) + + ((num_segments > 0) ? (num_segments - 1) : 0) + 1; + + /* + * Check to see if we're still in bounds. If not, there's a problem + * with internal_name (invalid format). + */ + if (required_length > internal_name_length) { + ACPI_ERROR((AE_INFO, "Invalid internal name")); + return_ACPI_STATUS(AE_BAD_PATHNAME); + } + + /* Build the converted_name */ + + *converted_name = ACPI_ALLOCATE_ZEROED(required_length); + if (!(*converted_name)) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + j = 0; + + for (i = 0; i < prefix_length; i++) { + (*converted_name)[j++] = internal_name[i]; + } + + if (num_segments > 0) { + for (i = 0; i < num_segments; i++) { + if (i > 0) { + (*converted_name)[j++] = '.'; + } + + /* Copy and validate the 4-char name segment */ + + ACPI_MOVE_NAME(&(*converted_name)[j], + &internal_name[names_index]); + acpi_ut_repair_name(&(*converted_name)[j]); + + j += ACPI_NAME_SIZE; + names_index += ACPI_NAME_SIZE; + } + } + + if (converted_name_length) { + *converted_name_length = (u32) required_length; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_validate_handle + * + * PARAMETERS: handle - Handle to be validated and typecast to a + * namespace node. + * + * RETURN: A pointer to a namespace node + * + * DESCRIPTION: Convert a namespace handle to a namespace node. Handles special + * cases for the root node. + * + * NOTE: Real integer handles would allow for more verification + * and keep all pointers within this subsystem - however this introduces + * more overhead and has not been necessary to this point. Drivers + * holding handles are typically notified before a node becomes invalid + * due to a table unload. + * + ******************************************************************************/ + +struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle) +{ + + ACPI_FUNCTION_ENTRY(); + + /* Parameter validation */ + + if ((!handle) || (handle == ACPI_ROOT_OBJECT)) { + return (acpi_gbl_root_node); + } + + /* We can at least attempt to verify the handle */ + + if (ACPI_GET_DESCRIPTOR_TYPE(handle) != ACPI_DESC_TYPE_NAMED) { + return (NULL); + } + + return (ACPI_CAST_PTR(struct acpi_namespace_node, handle)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: free memory allocated for namespace and ACPI table storage. + * + ******************************************************************************/ + +void acpi_ns_terminate(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ns_terminate); + + /* + * Free the entire namespace -- all nodes and all objects + * attached to the nodes + */ + acpi_ns_delete_namespace_subtree(acpi_gbl_root_node); + + /* Delete any objects attached to the root node */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_VOID; + } + + acpi_ns_delete_node(acpi_gbl_root_node); + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Namespace freed\n")); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_opens_scope + * + * PARAMETERS: type - A valid namespace type + * + * RETURN: NEWSCOPE if the passed type "opens a name scope" according + * to the ACPI specification, else 0 + * + ******************************************************************************/ + +u32 acpi_ns_opens_scope(acpi_object_type type) +{ + ACPI_FUNCTION_ENTRY(); + + if (type > ACPI_TYPE_LOCAL_MAX) { + + /* type code out of range */ + + ACPI_WARNING((AE_INFO, "Invalid Object Type 0x%X", type)); + return (ACPI_NS_NORMAL); + } + + return (((u32)acpi_gbl_ns_properties[type]) & ACPI_NS_NEWSCOPE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_node + * + * PARAMETERS: *pathname - Name to be found, in external (ASL) format. The + * \ (backslash) and ^ (carat) prefixes, and the + * . (period) to separate segments are supported. + * prefix_node - Root of subtree to be searched, or NS_ALL for the + * root of the name space. If Name is fully + * qualified (first s8 is '\'), the passed value + * of Scope will not be accessed. + * flags - Used to indicate whether to perform upsearch or + * not. + * return_node - Where the Node is returned + * + * DESCRIPTION: Look up a name relative to a given scope and return the + * corresponding Node. NOTE: Scope can be null. + * + * MUTEX: Locks namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_get_node(struct acpi_namespace_node *prefix_node, + const char *pathname, + u32 flags, struct acpi_namespace_node **return_node) +{ + union acpi_generic_state scope_info; + acpi_status status; + char *internal_path; + + ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname)); + + /* Simplest case is a null pathname */ + + if (!pathname) { + *return_node = prefix_node; + if (!prefix_node) { + *return_node = acpi_gbl_root_node; + } + return_ACPI_STATUS(AE_OK); + } + + /* Quick check for a reference to the root */ + + if (ACPI_IS_ROOT_PREFIX(pathname[0]) && (!pathname[1])) { + *return_node = acpi_gbl_root_node; + return_ACPI_STATUS(AE_OK); + } + + /* Convert path to internal representation */ + + status = acpi_ns_internalize_name(pathname, &internal_path); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Must lock namespace during lookup */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Setup lookup scope (search starting point) */ + + scope_info.scope.node = prefix_node; + + /* Lookup the name in the namespace */ + + status = acpi_ns_lookup(&scope_info, internal_path, ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, + (flags | ACPI_NS_DONT_OPEN_SCOPE), NULL, + return_node); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s, %s\n", + pathname, acpi_format_exception(status))); + } + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + +cleanup: + ACPI_FREE(internal_path); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/nswalk.c b/kernel/drivers/acpi/acpica/nswalk.c new file mode 100644 index 000000000..c68609a2b --- /dev/null +++ b/kernel/drivers/acpi/acpica/nswalk.c @@ -0,0 +1,358 @@ +/****************************************************************************** + * + * Module Name: nswalk - Functions for walking the ACPI namespace + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nswalk") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_next_node + * + * PARAMETERS: parent_node - Parent node whose children we are + * getting + * child_node - Previous child that was found. + * The NEXT child will be returned + * + * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if + * none is found. + * + * DESCRIPTION: Return the next peer node within the namespace. If Handle + * is valid, Scope is ignored. Otherwise, the first node + * within Scope is returned. + * + ******************************************************************************/ +struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node + *parent_node, + struct acpi_namespace_node + *child_node) +{ + ACPI_FUNCTION_ENTRY(); + + if (!child_node) { + + /* It's really the parent's _scope_ that we want */ + + return (parent_node->child); + } + + /* Otherwise just return the next peer */ + + return (child_node->peer); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_next_node_typed + * + * PARAMETERS: type - Type of node to be searched for + * parent_node - Parent node whose children we are + * getting + * child_node - Previous child that was found. + * The NEXT child will be returned + * + * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if + * none is found. + * + * DESCRIPTION: Return the next peer node within the namespace. If Handle + * is valid, Scope is ignored. Otherwise, the first node + * within Scope is returned. + * + ******************************************************************************/ + +struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type, + struct + acpi_namespace_node + *parent_node, + struct + acpi_namespace_node + *child_node) +{ + struct acpi_namespace_node *next_node = NULL; + + ACPI_FUNCTION_ENTRY(); + + next_node = acpi_ns_get_next_node(parent_node, child_node); + + + /* If any type is OK, we are done */ + + if (type == ACPI_TYPE_ANY) { + + /* next_node is NULL if we are at the end-of-list */ + + return (next_node); + } + + /* Must search for the node -- but within this scope only */ + + while (next_node) { + + /* If type matches, we are done */ + + if (next_node->type == type) { + return (next_node); + } + + /* Otherwise, move on to the next peer node */ + + next_node = next_node->peer; + } + + /* Not found */ + + return (NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_walk_namespace + * + * PARAMETERS: type - acpi_object_type to search for + * start_node - Handle in namespace where search begins + * max_depth - Depth to which search is to reach + * flags - Whether to unlock the NS before invoking + * the callback routine + * descending_callback - Called during tree descent + * when an object of "Type" is found + * ascending_callback - Called during tree ascent + * when an object of "Type" is found + * context - Passed to user function(s) above + * return_value - from the user_function if terminated + * early. Otherwise, returns NULL. + * RETURNS: Status + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the node specified by start_handle. + * The callback function is called whenever a node that matches + * the type parameter is found. If the callback function returns + * a non-zero value, the search is terminated immediately and + * this value is returned to the caller. + * + * The point of this procedure is to provide a generic namespace + * walk routine that can be called from multiple places to + * provide multiple services; the callback function(s) can be + * tailored to each task, whether it is a print function, + * a compare function, etc. + * + ******************************************************************************/ + +acpi_status +acpi_ns_walk_namespace(acpi_object_type type, + acpi_handle start_node, + u32 max_depth, + u32 flags, + acpi_walk_callback descending_callback, + acpi_walk_callback ascending_callback, + void *context, void **return_value) +{ + acpi_status status; + acpi_status mutex_status; + struct acpi_namespace_node *child_node; + struct acpi_namespace_node *parent_node; + acpi_object_type child_type; + u32 level; + u8 node_previously_visited = FALSE; + + ACPI_FUNCTION_TRACE(ns_walk_namespace); + + /* Special case for the namespace Root Node */ + + if (start_node == ACPI_ROOT_OBJECT) { + start_node = acpi_gbl_root_node; + } + + /* Null child means "get first node" */ + + parent_node = start_node; + child_node = acpi_ns_get_next_node(parent_node, NULL); + child_type = ACPI_TYPE_ANY; + level = 1; + + /* + * Traverse the tree of nodes until we bubble back up to where we + * started. When Level is zero, the loop is done because we have + * bubbled up to (and passed) the original parent handle (start_entry) + */ + while (level > 0 && child_node) { + status = AE_OK; + + /* Found next child, get the type if we are not searching for ANY */ + + if (type != ACPI_TYPE_ANY) { + child_type = child_node->type; + } + + /* + * Ignore all temporary namespace nodes (created during control + * method execution) unless told otherwise. These temporary nodes + * can cause a race condition because they can be deleted during + * the execution of the user function (if the namespace is + * unlocked before invocation of the user function.) Only the + * debugger namespace dump will examine the temporary nodes. + */ + if ((child_node->flags & ANOBJ_TEMPORARY) && + !(flags & ACPI_NS_WALK_TEMP_NODES)) { + status = AE_CTRL_DEPTH; + } + + /* Type must match requested type */ + + else if (child_type == type) { + /* + * Found a matching node, invoke the user callback function. + * Unlock the namespace if flag is set. + */ + if (flags & ACPI_NS_WALK_UNLOCK) { + mutex_status = + acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(mutex_status)) { + return_ACPI_STATUS(mutex_status); + } + } + + /* + * Invoke the user function, either descending, ascending, + * or both. + */ + if (!node_previously_visited) { + if (descending_callback) { + status = + descending_callback(child_node, + level, context, + return_value); + } + } else { + if (ascending_callback) { + status = + ascending_callback(child_node, + level, context, + return_value); + } + } + + if (flags & ACPI_NS_WALK_UNLOCK) { + mutex_status = + acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(mutex_status)) { + return_ACPI_STATUS(mutex_status); + } + } + + switch (status) { + case AE_OK: + case AE_CTRL_DEPTH: + + /* Just keep going */ + break; + + case AE_CTRL_TERMINATE: + + /* Exit now, with OK status */ + + return_ACPI_STATUS(AE_OK); + + default: + + /* All others are valid exceptions */ + + return_ACPI_STATUS(status); + } + } + + /* + * Depth first search: Attempt to go down another level in the + * namespace if we are allowed to. Don't go any further if we have + * reached the caller specified maximum depth or if the user + * function has specified that the maximum depth has been reached. + */ + if (!node_previously_visited && + (level < max_depth) && (status != AE_CTRL_DEPTH)) { + if (child_node->child) { + + /* There is at least one child of this node, visit it */ + + level++; + parent_node = child_node; + child_node = + acpi_ns_get_next_node(parent_node, NULL); + continue; + } + } + + /* No more children, re-visit this node */ + + if (!node_previously_visited) { + node_previously_visited = TRUE; + continue; + } + + /* No more children, visit peers */ + + child_node = acpi_ns_get_next_node(parent_node, child_node); + if (child_node) { + node_previously_visited = FALSE; + } + + /* No peers, re-visit parent */ + + else { + /* + * No more children of this node (acpi_ns_get_next_node failed), go + * back upwards in the namespace tree to the node's parent. + */ + level--; + child_node = parent_node; + parent_node = parent_node->parent; + + node_previously_visited = TRUE; + } + } + + /* Complete walk, not terminated by user function */ + + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/nsxfeval.c b/kernel/drivers/acpi/acpica/nsxfeval.c new file mode 100644 index 000000000..b6030a2de --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsxfeval.c @@ -0,0 +1,996 @@ +/******************************************************************************* + * + * Module Name: nsxfeval - Public interfaces to the ACPI subsystem + * ACPI Object evaluation interfaces + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsxfeval") + +/* Local prototypes */ +static void acpi_ns_resolve_references(struct acpi_evaluate_info *info); + +/******************************************************************************* + * + * FUNCTION: acpi_evaluate_object_typed + * + * PARAMETERS: handle - Object handle (optional) + * pathname - Object pathname (optional) + * external_params - List of parameters to pass to method, + * terminated by NULL. May be NULL + * if no parameters are being passed. + * return_buffer - Where to put method's return value (if + * any). If NULL, no value is returned. + * return_type - Expected type of return object + * + * RETURN: Status + * + * DESCRIPTION: Find and evaluate the given object, passing the given + * parameters if necessary. One of "Handle" or "Pathname" must + * be valid (non-null) + * + ******************************************************************************/ + +acpi_status +acpi_evaluate_object_typed(acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *external_params, + struct acpi_buffer *return_buffer, + acpi_object_type return_type) +{ + acpi_status status; + u8 free_buffer_on_error = FALSE; + + ACPI_FUNCTION_TRACE(acpi_evaluate_object_typed); + + /* Return buffer must be valid */ + + if (!return_buffer) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (return_buffer->length == ACPI_ALLOCATE_BUFFER) { + free_buffer_on_error = TRUE; + } + + /* Evaluate the object */ + + status = acpi_evaluate_object(handle, pathname, + external_params, return_buffer); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Type ANY means "don't care" */ + + if (return_type == ACPI_TYPE_ANY) { + return_ACPI_STATUS(AE_OK); + } + + if (return_buffer->length == 0) { + + /* Error because caller specifically asked for a return value */ + + ACPI_ERROR((AE_INFO, "No return value")); + return_ACPI_STATUS(AE_NULL_OBJECT); + } + + /* Examine the object type returned from evaluate_object */ + + if (((union acpi_object *)return_buffer->pointer)->type == return_type) { + return_ACPI_STATUS(AE_OK); + } + + /* Return object type does not match requested type */ + + ACPI_ERROR((AE_INFO, + "Incorrect return type [%s] requested [%s]", + acpi_ut_get_type_name(((union acpi_object *)return_buffer-> + pointer)->type), + acpi_ut_get_type_name(return_type))); + + if (free_buffer_on_error) { + /* + * Free a buffer created via ACPI_ALLOCATE_BUFFER. + * Note: We use acpi_os_free here because acpi_os_allocate was used + * to allocate the buffer. This purposefully bypasses the + * (optionally enabled) allocation tracking mechanism since we + * only want to track internal allocations. + */ + acpi_os_free(return_buffer->pointer); + return_buffer->pointer = NULL; + } + + return_buffer->length = 0; + return_ACPI_STATUS(AE_TYPE); +} + +ACPI_EXPORT_SYMBOL(acpi_evaluate_object_typed) + +/******************************************************************************* + * + * FUNCTION: acpi_evaluate_object + * + * PARAMETERS: handle - Object handle (optional) + * pathname - Object pathname (optional) + * external_params - List of parameters to pass to method, + * terminated by NULL. May be NULL + * if no parameters are being passed. + * return_buffer - Where to put method's return value (if + * any). If NULL, no value is returned. + * + * RETURN: Status + * + * DESCRIPTION: Find and evaluate the given object, passing the given + * parameters if necessary. One of "Handle" or "Pathname" must + * be valid (non-null) + * + ******************************************************************************/ +acpi_status +acpi_evaluate_object(acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *external_params, + struct acpi_buffer *return_buffer) +{ + acpi_status status; + struct acpi_evaluate_info *info; + acpi_size buffer_space_needed; + u32 i; + + ACPI_FUNCTION_TRACE(acpi_evaluate_object); + + /* Allocate and initialize the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Convert and validate the device handle */ + + info->prefix_node = acpi_ns_validate_handle(handle); + if (!info->prefix_node) { + status = AE_BAD_PARAMETER; + goto cleanup; + } + + /* + * Get the actual namespace node for the target object. + * Handles these cases: + * + * 1) Null node, valid pathname from root (absolute path) + * 2) Node and valid pathname (path relative to Node) + * 3) Node, Null pathname + */ + if ((pathname) && (ACPI_IS_ROOT_PREFIX(pathname[0]))) { + + /* The path is fully qualified, just evaluate by name */ + + info->prefix_node = NULL; + } else if (!handle) { + /* + * A handle is optional iff a fully qualified pathname is specified. + * Since we've already handled fully qualified names above, this is + * an error. + */ + if (!pathname) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Both Handle and Pathname are NULL")); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Null Handle with relative pathname [%s]", + pathname)); + } + + status = AE_BAD_PARAMETER; + goto cleanup; + } + + info->relative_pathname = pathname; + + /* + * Convert all external objects passed as arguments to the + * internal version(s). + */ + if (external_params && external_params->count) { + info->param_count = (u16)external_params->count; + + /* Warn on impossible argument count */ + + if (info->param_count > ACPI_METHOD_NUM_ARGS) { + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Excess arguments (%u) - using only %u", + info->param_count, + ACPI_METHOD_NUM_ARGS)); + + info->param_count = ACPI_METHOD_NUM_ARGS; + } + + /* + * Allocate a new parameter block for the internal objects + * Add 1 to count to allow for null terminated internal list + */ + info->parameters = ACPI_ALLOCATE_ZEROED(((acpi_size) info-> + param_count + + 1) * sizeof(void *)); + if (!info->parameters) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Convert each external object in the list to an internal object */ + + for (i = 0; i < info->param_count; i++) { + status = + acpi_ut_copy_eobject_to_iobject(&external_params-> + pointer[i], + &info-> + parameters[i]); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + } + + info->parameters[info->param_count] = NULL; + } + +#if 0 + + /* + * Begin incoming argument count analysis. Check for too few args + * and too many args. + */ + + switch (acpi_ns_get_type(info->node)) { + case ACPI_TYPE_METHOD: + + /* Check incoming argument count against the method definition */ + + if (info->obj_desc->method.param_count > info->param_count) { + ACPI_ERROR((AE_INFO, + "Insufficient arguments (%u) - %u are required", + info->param_count, + info->obj_desc->method.param_count)); + + status = AE_MISSING_ARGUMENTS; + goto cleanup; + } + + else if (info->obj_desc->method.param_count < info->param_count) { + ACPI_WARNING((AE_INFO, + "Excess arguments (%u) - only %u are required", + info->param_count, + info->obj_desc->method.param_count)); + + /* Just pass the required number of arguments */ + + info->param_count = info->obj_desc->method.param_count; + } + + /* + * Any incoming external objects to be passed as arguments to the + * method must be converted to internal objects + */ + if (info->param_count) { + /* + * Allocate a new parameter block for the internal objects + * Add 1 to count to allow for null terminated internal list + */ + info->parameters = ACPI_ALLOCATE_ZEROED(((acpi_size) + info-> + param_count + + 1) * + sizeof(void *)); + if (!info->parameters) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Convert each external object in the list to an internal object */ + + for (i = 0; i < info->param_count; i++) { + status = + acpi_ut_copy_eobject_to_iobject + (&external_params->pointer[i], + &info->parameters[i]); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + } + + info->parameters[info->param_count] = NULL; + } + break; + + default: + + /* Warn if arguments passed to an object that is not a method */ + + if (info->param_count) { + ACPI_WARNING((AE_INFO, + "%u arguments were passed to a non-method ACPI object", + info->param_count)); + } + break; + } + +#endif + + /* Now we can evaluate the object */ + + status = acpi_ns_evaluate(info); + + /* + * If we are expecting a return value, and all went well above, + * copy the return value to an external object. + */ + if (return_buffer) { + if (!info->return_object) { + return_buffer->length = 0; + } else { + if (ACPI_GET_DESCRIPTOR_TYPE(info->return_object) == + ACPI_DESC_TYPE_NAMED) { + /* + * If we received a NS Node as a return object, this means that + * the object we are evaluating has nothing interesting to + * return (such as a mutex, etc.) We return an error because + * these types are essentially unsupported by this interface. + * We don't check up front because this makes it easier to add + * support for various types at a later date if necessary. + */ + status = AE_TYPE; + info->return_object = NULL; /* No need to delete a NS Node */ + return_buffer->length = 0; + } + + if (ACPI_SUCCESS(status)) { + + /* Dereference Index and ref_of references */ + + acpi_ns_resolve_references(info); + + /* Get the size of the returned object */ + + status = + acpi_ut_get_object_size(info->return_object, + &buffer_space_needed); + if (ACPI_SUCCESS(status)) { + + /* Validate/Allocate/Clear caller buffer */ + + status = + acpi_ut_initialize_buffer + (return_buffer, + buffer_space_needed); + if (ACPI_FAILURE(status)) { + /* + * Caller's buffer is too small or a new one can't + * be allocated + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Needed buffer size %X, %s\n", + (u32) + buffer_space_needed, + acpi_format_exception + (status))); + } else { + /* We have enough space for the object, build it */ + + status = + acpi_ut_copy_iobject_to_eobject + (info->return_object, + return_buffer); + } + } + } + } + } + + if (info->return_object) { + /* + * Delete the internal return object. NOTE: Interpreter must be + * locked to avoid race condition. + */ + acpi_ex_enter_interpreter(); + + /* Remove one reference on the return object (should delete it) */ + + acpi_ut_remove_reference(info->return_object); + acpi_ex_exit_interpreter(); + } + +cleanup: + + /* Free the input parameter list (if we created one) */ + + if (info->parameters) { + + /* Free the allocated parameter block */ + + acpi_ut_delete_internal_object_list(info->parameters); + } + + ACPI_FREE(info); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_evaluate_object) + +/******************************************************************************* + * + * FUNCTION: acpi_ns_resolve_references + * + * PARAMETERS: info - Evaluation info block + * + * RETURN: Info->return_object is replaced with the dereferenced object + * + * DESCRIPTION: Dereference certain reference objects. Called before an + * internal return object is converted to an external union acpi_object. + * + * Performs an automatic dereference of Index and ref_of reference objects. + * These reference objects are not supported by the union acpi_object, so this is a + * last resort effort to return something useful. Also, provides compatibility + * with other ACPI implementations. + * + * NOTE: does not handle references within returned package objects or nested + * references, but this support could be added later if found to be necessary. + * + ******************************************************************************/ +static void acpi_ns_resolve_references(struct acpi_evaluate_info *info) +{ + union acpi_operand_object *obj_desc = NULL; + struct acpi_namespace_node *node; + + /* We are interested in reference objects only */ + + if ((info->return_object)->common.type != ACPI_TYPE_LOCAL_REFERENCE) { + return; + } + + /* + * Two types of references are supported - those created by Index and + * ref_of operators. A name reference (AML_NAMEPATH_OP) can be converted + * to an union acpi_object, so it is not dereferenced here. A ddb_handle + * (AML_LOAD_OP) cannot be dereferenced, nor can it be converted to + * an union acpi_object. + */ + switch (info->return_object->reference.class) { + case ACPI_REFCLASS_INDEX: + + obj_desc = *(info->return_object->reference.where); + break; + + case ACPI_REFCLASS_REFOF: + + node = info->return_object->reference.object; + if (node) { + obj_desc = node->object; + } + break; + + default: + + return; + } + + /* Replace the existing reference object */ + + if (obj_desc) { + acpi_ut_add_reference(obj_desc); + acpi_ut_remove_reference(info->return_object); + info->return_object = obj_desc; + } + + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_walk_namespace + * + * PARAMETERS: type - acpi_object_type to search for + * start_object - Handle in namespace where search begins + * max_depth - Depth to which search is to reach + * descending_callback - Called during tree descent + * when an object of "Type" is found + * ascending_callback - Called during tree ascent + * when an object of "Type" is found + * context - Passed to user function(s) above + * return_value - Location where return value of + * user_function is put if terminated early + * + * RETURNS Return value from the user_function if terminated early. + * Otherwise, returns NULL. + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the object specified by start_handle. + * The callback function is called whenever an object that matches + * the type parameter is found. If the callback function returns + * a non-zero value, the search is terminated immediately and this + * value is returned to the caller. + * + * The point of this procedure is to provide a generic namespace + * walk routine that can be called from multiple places to + * provide multiple services; the callback function(s) can be + * tailored to each task, whether it is a print function, + * a compare function, etc. + * + ******************************************************************************/ + +acpi_status +acpi_walk_namespace(acpi_object_type type, + acpi_handle start_object, + u32 max_depth, + acpi_walk_callback descending_callback, + acpi_walk_callback ascending_callback, + void *context, void **return_value) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_walk_namespace); + + /* Parameter validation */ + + if ((type > ACPI_TYPE_LOCAL_MAX) || + (!max_depth) || (!descending_callback && !ascending_callback)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * Need to acquire the namespace reader lock to prevent interference + * with any concurrent table unloads (which causes the deletion of + * namespace objects). We cannot allow the deletion of a namespace node + * while the user function is using it. The exception to this are the + * nodes created and deleted during control method execution -- these + * nodes are marked as temporary nodes and are ignored by the namespace + * walk. Thus, control methods can be executed while holding the + * namespace deletion lock (and the user function can execute control + * methods.) + */ + status = acpi_ut_acquire_read_lock(&acpi_gbl_namespace_rw_lock); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Lock the namespace around the walk. The namespace will be + * unlocked/locked around each call to the user function - since the user + * function must be allowed to make ACPICA calls itself (for example, it + * will typically execute control methods during device enumeration.) + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* Now we can validate the starting node */ + + if (!acpi_ns_validate_handle(start_object)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit2; + } + + status = acpi_ns_walk_namespace(type, start_object, max_depth, + ACPI_NS_WALK_UNLOCK, + descending_callback, ascending_callback, + context, return_value); + +unlock_and_exit2: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + +unlock_and_exit: + (void)acpi_ut_release_read_lock(&acpi_gbl_namespace_rw_lock); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_walk_namespace) + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_device_callback + * + * PARAMETERS: Callback from acpi_get_device + * + * RETURN: Status + * + * DESCRIPTION: Takes callbacks from walk_namespace and filters out all non- + * present devices, or if they specified a HID, it filters based + * on that. + * + ******************************************************************************/ +static acpi_status +acpi_ns_get_device_callback(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_get_devices_info *info = context; + acpi_status status; + struct acpi_namespace_node *node; + u32 flags; + struct acpi_pnp_device_id *hid; + struct acpi_pnp_device_id_list *cid; + u32 i; + u8 found; + int no_match; + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + node = acpi_ns_validate_handle(obj_handle); + status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (!node) { + return (AE_BAD_PARAMETER); + } + + /* + * First, filter based on the device HID and CID. + * + * 01/2010: For this case where a specific HID is requested, we don't + * want to run _STA until we have an actual HID match. Thus, we will + * not unnecessarily execute _STA on devices for which the caller + * doesn't care about. Previously, _STA was executed unconditionally + * on all devices found here. + * + * A side-effect of this change is that now we will continue to search + * for a matching HID even under device trees where the parent device + * would have returned a _STA that indicates it is not present or + * not functioning (thus aborting the search on that branch). + */ + if (info->hid != NULL) { + status = acpi_ut_execute_HID(node, &hid); + if (status == AE_NOT_FOUND) { + return (AE_OK); + } else if (ACPI_FAILURE(status)) { + return (AE_CTRL_DEPTH); + } + + no_match = ACPI_STRCMP(hid->string, info->hid); + ACPI_FREE(hid); + + if (no_match) { + /* + * HID does not match, attempt match within the + * list of Compatible IDs (CIDs) + */ + status = acpi_ut_execute_CID(node, &cid); + if (status == AE_NOT_FOUND) { + return (AE_OK); + } else if (ACPI_FAILURE(status)) { + return (AE_CTRL_DEPTH); + } + + /* Walk the CID list */ + + found = FALSE; + for (i = 0; i < cid->count; i++) { + if (ACPI_STRCMP(cid->ids[i].string, info->hid) + == 0) { + + /* Found a matching CID */ + + found = TRUE; + break; + } + } + + ACPI_FREE(cid); + if (!found) { + return (AE_OK); + } + } + } + + /* Run _STA to determine if device is present */ + + status = acpi_ut_execute_STA(node, &flags); + if (ACPI_FAILURE(status)) { + return (AE_CTRL_DEPTH); + } + + if (!(flags & ACPI_STA_DEVICE_PRESENT) && + !(flags & ACPI_STA_DEVICE_FUNCTIONING)) { + /* + * Don't examine the children of the device only when the + * device is neither present nor functional. See ACPI spec, + * description of _STA for more information. + */ + return (AE_CTRL_DEPTH); + } + + /* We have a valid device, invoke the user function */ + + status = info->user_function(obj_handle, nesting_level, info->context, + return_value); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_get_devices + * + * PARAMETERS: HID - HID to search for. Can be NULL. + * user_function - Called when a matching object is found + * context - Passed to user function + * return_value - Location where return value of + * user_function is put if terminated early + * + * RETURNS Return value from the user_function if terminated early. + * Otherwise, returns NULL. + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the object specified by start_handle. + * The user_function is called whenever an object of type + * Device is found. If the user function returns + * a non-zero value, the search is terminated immediately and this + * value is returned to the caller. + * + * This is a wrapper for walk_namespace, but the callback performs + * additional filtering. Please see acpi_ns_get_device_callback. + * + ******************************************************************************/ + +acpi_status +acpi_get_devices(const char *HID, + acpi_walk_callback user_function, + void *context, void **return_value) +{ + acpi_status status; + struct acpi_get_devices_info info; + + ACPI_FUNCTION_TRACE(acpi_get_devices); + + /* Parameter validation */ + + if (!user_function) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * We're going to call their callback from OUR callback, so we need + * to know what it is, and their context parameter. + */ + info.hid = HID; + info.context = context; + info.user_function = user_function; + + /* + * Lock the namespace around the walk. + * The namespace will be unlocked/locked around each call + * to the user function - since this function + * must be allowed to make Acpi calls itself. + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, + acpi_ns_get_device_callback, NULL, + &info, return_value); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_devices) + +/******************************************************************************* + * + * FUNCTION: acpi_attach_data + * + * PARAMETERS: obj_handle - Namespace node + * handler - Handler for this attachment + * data - Pointer to data to be attached + * + * RETURN: Status + * + * DESCRIPTION: Attach arbitrary data and handler to a namespace node. + * + ******************************************************************************/ +acpi_status +acpi_attach_data(acpi_handle obj_handle, + acpi_object_handler handler, void *data) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Parameter validation */ + + if (!obj_handle || !handler || !data) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_validate_handle(obj_handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ns_attach_data(node, handler, data); + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_attach_data) + +/******************************************************************************* + * + * FUNCTION: acpi_detach_data + * + * PARAMETERS: obj_handle - Namespace node handle + * handler - Handler used in call to acpi_attach_data + * + * RETURN: Status + * + * DESCRIPTION: Remove data that was previously attached to a node. + * + ******************************************************************************/ +acpi_status +acpi_detach_data(acpi_handle obj_handle, acpi_object_handler handler) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Parameter validation */ + + if (!obj_handle || !handler) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_validate_handle(obj_handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ns_detach_data(node, handler); + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_detach_data) + +/******************************************************************************* + * + * FUNCTION: acpi_get_data_full + * + * PARAMETERS: obj_handle - Namespace node + * handler - Handler used in call to attach_data + * data - Where the data is returned + * callback - function to execute before returning + * + * RETURN: Status + * + * DESCRIPTION: Retrieve data that was previously attached to a namespace node + * and execute a callback before returning. + * + ******************************************************************************/ +acpi_status +acpi_get_data_full(acpi_handle obj_handle, acpi_object_handler handler, + void **data, void (*callback)(void *)) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Parameter validation */ + + if (!obj_handle || !handler || !data) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_validate_handle(obj_handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ns_get_attached_data(node, handler, data); + if (ACPI_SUCCESS(status) && callback) { + callback(*data); + } + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_data_full) + +/******************************************************************************* + * + * FUNCTION: acpi_get_data + * + * PARAMETERS: obj_handle - Namespace node + * handler - Handler used in call to attach_data + * data - Where the data is returned + * + * RETURN: Status + * + * DESCRIPTION: Retrieve data that was previously attached to a namespace node. + * + ******************************************************************************/ +acpi_status +acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data) +{ + return acpi_get_data_full(obj_handle, handler, data, NULL); +} + +ACPI_EXPORT_SYMBOL(acpi_get_data) diff --git a/kernel/drivers/acpi/acpica/nsxfname.c b/kernel/drivers/acpi/acpica/nsxfname.c new file mode 100644 index 000000000..d66c32648 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsxfname.c @@ -0,0 +1,663 @@ +/****************************************************************************** + * + * Module Name: nsxfname - Public interfaces to the ACPI subsystem + * ACPI Namespace oriented interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acparser.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsxfname") + +/* Local prototypes */ +static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest, + struct acpi_pnp_device_id *source, + char *string_area); + +/****************************************************************************** + * + * FUNCTION: acpi_get_handle + * + * PARAMETERS: parent - Object to search under (search scope). + * pathname - Pointer to an asciiz string containing the + * name + * ret_handle - Where the return handle is returned + * + * RETURN: Status + * + * DESCRIPTION: This routine will search for a caller specified name in the + * name space. The caller can restrict the search region by + * specifying a non NULL parent. The parent value is itself a + * namespace handle. + * + ******************************************************************************/ + +acpi_status +acpi_get_handle(acpi_handle parent, + acpi_string pathname, acpi_handle * ret_handle) +{ + acpi_status status; + struct acpi_namespace_node *node = NULL; + struct acpi_namespace_node *prefix_node = NULL; + + ACPI_FUNCTION_ENTRY(); + + /* Parameter Validation */ + + if (!ret_handle || !pathname) { + return (AE_BAD_PARAMETER); + } + + /* Convert a parent handle to a prefix node */ + + if (parent) { + prefix_node = acpi_ns_validate_handle(parent); + if (!prefix_node) { + return (AE_BAD_PARAMETER); + } + } + + /* + * Valid cases are: + * 1) Fully qualified pathname + * 2) Parent + Relative pathname + * + * Error for + */ + if (ACPI_IS_ROOT_PREFIX(pathname[0])) { + + /* Pathname is fully qualified (starts with '\') */ + + /* Special case for root-only, since we can't search for it */ + + if (!ACPI_STRCMP(pathname, ACPI_NS_ROOT_PATH)) { + *ret_handle = + ACPI_CAST_PTR(acpi_handle, acpi_gbl_root_node); + return (AE_OK); + } + } else if (!prefix_node) { + + /* Relative path with null prefix is disallowed */ + + return (AE_BAD_PARAMETER); + } + + /* Find the Node and convert to a handle */ + + status = + acpi_ns_get_node(prefix_node, pathname, ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_SUCCESS(status)) { + *ret_handle = ACPI_CAST_PTR(acpi_handle, node); + } + + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_handle) + +/****************************************************************************** + * + * FUNCTION: acpi_get_name + * + * PARAMETERS: handle - Handle to be converted to a pathname + * name_type - Full pathname or single segment + * buffer - Buffer for returned path + * + * RETURN: Pointer to a string containing the fully qualified Name. + * + * DESCRIPTION: This routine returns the fully qualified name associated with + * the Handle parameter. This and the acpi_pathname_to_handle are + * complementary functions. + * + ******************************************************************************/ +acpi_status +acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer * buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + char *node_name; + + /* Parameter validation */ + + if (name_type > ACPI_NAME_TYPE_MAX) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_validate_buffer(buffer); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (name_type == ACPI_FULL_PATHNAME) { + + /* Get the full pathname (From the namespace root) */ + + status = acpi_ns_handle_to_pathname(handle, buffer); + return (status); + } + + /* + * Wants the single segment ACPI name. + * Validate handle and convert to a namespace Node + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + node = acpi_ns_validate_handle(handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer(buffer, ACPI_PATH_SEGMENT_LENGTH); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* Just copy the ACPI name from the Node and zero terminate it */ + + node_name = acpi_ut_get_node_name(node); + ACPI_MOVE_NAME(buffer->pointer, node_name); + ((char *)buffer->pointer)[ACPI_NAME_SIZE] = 0; + status = AE_OK; + +unlock_and_exit: + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_name) + +/****************************************************************************** + * + * FUNCTION: acpi_ns_copy_device_id + * + * PARAMETERS: dest - Pointer to the destination PNP_DEVICE_ID + * source - Pointer to the source PNP_DEVICE_ID + * string_area - Pointer to where to copy the dest string + * + * RETURN: Pointer to the next string area + * + * DESCRIPTION: Copy a single PNP_DEVICE_ID, including the string data. + * + ******************************************************************************/ +static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest, + struct acpi_pnp_device_id *source, + char *string_area) +{ + + /* Create the destination PNP_DEVICE_ID */ + + dest->string = string_area; + dest->length = source->length; + + /* Copy actual string and return a pointer to the next string area */ + + ACPI_MEMCPY(string_area, source->string, source->length); + return (string_area + source->length); +} + +/****************************************************************************** + * + * FUNCTION: acpi_get_object_info + * + * PARAMETERS: handle - Object Handle + * return_buffer - Where the info is returned + * + * RETURN: Status + * + * DESCRIPTION: Returns information about an object as gleaned from the + * namespace node and possibly by running several standard + * control methods (Such as in the case of a device.) + * + * For Device and Processor objects, run the Device _HID, _UID, _CID, _SUB, + * _STA, _ADR, _sx_w, and _sx_d methods. + * + * Note: Allocates the return buffer, must be freed by the caller. + * + ******************************************************************************/ + +acpi_status +acpi_get_object_info(acpi_handle handle, + struct acpi_device_info **return_buffer) +{ + struct acpi_namespace_node *node; + struct acpi_device_info *info; + struct acpi_pnp_device_id_list *cid_list = NULL; + struct acpi_pnp_device_id *hid = NULL; + struct acpi_pnp_device_id *uid = NULL; + struct acpi_pnp_device_id *sub = NULL; + char *next_id_string; + acpi_object_type type; + acpi_name name; + u8 param_count = 0; + u8 valid = 0; + u32 info_size; + u32 i; + acpi_status status; + + /* Parameter validation */ + + if (!handle || !return_buffer) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + node = acpi_ns_validate_handle(handle); + if (!node) { + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (AE_BAD_PARAMETER); + } + + /* Get the namespace node data while the namespace is locked */ + + info_size = sizeof(struct acpi_device_info); + type = node->type; + name = node->name.integer; + + if (node->type == ACPI_TYPE_METHOD) { + param_count = node->object->method.param_count; + } + + status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { + /* + * Get extra info for ACPI Device/Processor objects only: + * Run the Device _HID, _UID, _SUB, and _CID methods. + * + * Note: none of these methods are required, so they may or may + * not be present for this device. The Info->Valid bitfield is used + * to indicate which methods were found and run successfully. + */ + + /* Execute the Device._HID method */ + + status = acpi_ut_execute_HID(node, &hid); + if (ACPI_SUCCESS(status)) { + info_size += hid->length; + valid |= ACPI_VALID_HID; + } + + /* Execute the Device._UID method */ + + status = acpi_ut_execute_UID(node, &uid); + if (ACPI_SUCCESS(status)) { + info_size += uid->length; + valid |= ACPI_VALID_UID; + } + + /* Execute the Device._SUB method */ + + status = acpi_ut_execute_SUB(node, &sub); + if (ACPI_SUCCESS(status)) { + info_size += sub->length; + valid |= ACPI_VALID_SUB; + } + + /* Execute the Device._CID method */ + + status = acpi_ut_execute_CID(node, &cid_list); + if (ACPI_SUCCESS(status)) { + + /* Add size of CID strings and CID pointer array */ + + info_size += + (cid_list->list_size - + sizeof(struct acpi_pnp_device_id_list)); + valid |= ACPI_VALID_CID; + } + } + + /* + * Now that we have the variable-length data, we can allocate the + * return buffer + */ + info = ACPI_ALLOCATE_ZEROED(info_size); + if (!info) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Get the fixed-length data */ + + if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { + /* + * Get extra info for ACPI Device/Processor objects only: + * Run the _STA, _ADR and, sx_w, and _sx_d methods. + * + * Notes: none of these methods are required, so they may or may + * not be present for this device. The Info->Valid bitfield is used + * to indicate which methods were found and run successfully. + * + * For _STA, if the method does not exist, then (as per the ACPI + * specification), the returned current_status flags will indicate + * that the device is present/functional/enabled. Otherwise, the + * current_status flags reflect the value returned from _STA. + */ + + /* Execute the Device._STA method */ + + status = acpi_ut_execute_STA(node, &info->current_status); + if (ACPI_SUCCESS(status)) { + valid |= ACPI_VALID_STA; + } + + /* Execute the Device._ADR method */ + + status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, node, + &info->address); + if (ACPI_SUCCESS(status)) { + valid |= ACPI_VALID_ADR; + } + + /* Execute the Device._sx_w methods */ + + status = acpi_ut_execute_power_methods(node, + acpi_gbl_lowest_dstate_names, + ACPI_NUM_sx_w_METHODS, + info->lowest_dstates); + if (ACPI_SUCCESS(status)) { + valid |= ACPI_VALID_SXWS; + } + + /* Execute the Device._sx_d methods */ + + status = acpi_ut_execute_power_methods(node, + acpi_gbl_highest_dstate_names, + ACPI_NUM_sx_d_METHODS, + info->highest_dstates); + if (ACPI_SUCCESS(status)) { + valid |= ACPI_VALID_SXDS; + } + } + + /* + * Create a pointer to the string area of the return buffer. + * Point to the end of the base struct acpi_device_info structure. + */ + next_id_string = ACPI_CAST_PTR(char, info->compatible_id_list.ids); + if (cid_list) { + + /* Point past the CID PNP_DEVICE_ID array */ + + next_id_string += + ((acpi_size) cid_list->count * + sizeof(struct acpi_pnp_device_id)); + } + + /* + * Copy the HID, UID, SUB, and CIDs to the return buffer. + * The variable-length strings are copied to the reserved area + * at the end of the buffer. + * + * For HID and CID, check if the ID is a PCI Root Bridge. + */ + if (hid) { + next_id_string = acpi_ns_copy_device_id(&info->hardware_id, + hid, next_id_string); + + if (acpi_ut_is_pci_root_bridge(hid->string)) { + info->flags |= ACPI_PCI_ROOT_BRIDGE; + } + } + + if (uid) { + next_id_string = acpi_ns_copy_device_id(&info->unique_id, + uid, next_id_string); + } + + if (sub) { + next_id_string = acpi_ns_copy_device_id(&info->subsystem_id, + sub, next_id_string); + } + + if (cid_list) { + info->compatible_id_list.count = cid_list->count; + info->compatible_id_list.list_size = cid_list->list_size; + + /* Copy each CID */ + + for (i = 0; i < cid_list->count; i++) { + next_id_string = + acpi_ns_copy_device_id(&info->compatible_id_list. + ids[i], &cid_list->ids[i], + next_id_string); + + if (acpi_ut_is_pci_root_bridge(cid_list->ids[i].string)) { + info->flags |= ACPI_PCI_ROOT_BRIDGE; + } + } + } + + /* Copy the fixed-length data */ + + info->info_size = info_size; + info->type = type; + info->name = name; + info->param_count = param_count; + info->valid = valid; + + *return_buffer = info; + status = AE_OK; + +cleanup: + if (hid) { + ACPI_FREE(hid); + } + if (uid) { + ACPI_FREE(uid); + } + if (sub) { + ACPI_FREE(sub); + } + if (cid_list) { + ACPI_FREE(cid_list); + } + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_object_info) + +/****************************************************************************** + * + * FUNCTION: acpi_install_method + * + * PARAMETERS: buffer - An ACPI table containing one control method + * + * RETURN: Status + * + * DESCRIPTION: Install a control method into the namespace. If the method + * name already exists in the namespace, it is overwritten. The + * input buffer must contain a valid DSDT or SSDT containing a + * single control method. + * + ******************************************************************************/ +acpi_status acpi_install_method(u8 *buffer) +{ + struct acpi_table_header *table = + ACPI_CAST_PTR(struct acpi_table_header, buffer); + u8 *aml_buffer; + u8 *aml_start; + char *path; + struct acpi_namespace_node *node; + union acpi_operand_object *method_obj; + struct acpi_parse_state parser_state; + u32 aml_length; + u16 opcode; + u8 method_flags; + acpi_status status; + + /* Parameter validation */ + + if (!buffer) { + return (AE_BAD_PARAMETER); + } + + /* Table must be a DSDT or SSDT */ + + if (!ACPI_COMPARE_NAME(table->signature, ACPI_SIG_DSDT) && + !ACPI_COMPARE_NAME(table->signature, ACPI_SIG_SSDT)) { + return (AE_BAD_HEADER); + } + + /* First AML opcode in the table must be a control method */ + + parser_state.aml = buffer + sizeof(struct acpi_table_header); + opcode = acpi_ps_peek_opcode(&parser_state); + if (opcode != AML_METHOD_OP) { + return (AE_BAD_PARAMETER); + } + + /* Extract method information from the raw AML */ + + parser_state.aml += acpi_ps_get_opcode_size(opcode); + parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state); + path = acpi_ps_get_next_namestring(&parser_state); + method_flags = *parser_state.aml++; + aml_start = parser_state.aml; + aml_length = ACPI_PTR_DIFF(parser_state.pkg_end, aml_start); + + /* + * Allocate resources up-front. We don't want to have to delete a new + * node from the namespace if we cannot allocate memory. + */ + aml_buffer = ACPI_ALLOCATE(aml_length); + if (!aml_buffer) { + return (AE_NO_MEMORY); + } + + method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); + if (!method_obj) { + ACPI_FREE(aml_buffer); + return (AE_NO_MEMORY); + } + + /* Lock namespace for acpi_ns_lookup, we may be creating a new node */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + /* The lookup either returns an existing node or creates a new one */ + + status = + acpi_ns_lookup(NULL, path, ACPI_TYPE_METHOD, ACPI_IMODE_LOAD_PASS1, + ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND, + NULL, &node); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE(status)) { /* ns_lookup */ + if (status != AE_ALREADY_EXISTS) { + goto error_exit; + } + + /* Node existed previously, make sure it is a method node */ + + if (node->type != ACPI_TYPE_METHOD) { + status = AE_TYPE; + goto error_exit; + } + } + + /* Copy the method AML to the local buffer */ + + ACPI_MEMCPY(aml_buffer, aml_start, aml_length); + + /* Initialize the method object with the new method's information */ + + method_obj->method.aml_start = aml_buffer; + method_obj->method.aml_length = aml_length; + + method_obj->method.param_count = (u8) + (method_flags & AML_METHOD_ARG_COUNT); + + if (method_flags & AML_METHOD_SERIALIZED) { + method_obj->method.info_flags = ACPI_METHOD_SERIALIZED; + + method_obj->method.sync_level = (u8) + ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4); + } + + /* + * Now that it is complete, we can attach the new method object to + * the method Node (detaches/deletes any existing object) + */ + status = acpi_ns_attach_object(node, method_obj, ACPI_TYPE_METHOD); + + /* + * Flag indicates AML buffer is dynamic, must be deleted later. + * Must be set only after attach above. + */ + node->flags |= ANOBJ_ALLOCATED_BUFFER; + + /* Remove local reference to the method object */ + + acpi_ut_remove_reference(method_obj); + return (status); + +error_exit: + + ACPI_FREE(aml_buffer); + ACPI_FREE(method_obj); + return (status); +} +ACPI_EXPORT_SYMBOL(acpi_install_method) diff --git a/kernel/drivers/acpi/acpica/nsxfobj.c b/kernel/drivers/acpi/acpica/nsxfobj.c new file mode 100644 index 000000000..793383501 --- /dev/null +++ b/kernel/drivers/acpi/acpica/nsxfobj.c @@ -0,0 +1,246 @@ +/******************************************************************************* + * + * Module Name: nsxfobj - Public interfaces to the ACPI subsystem + * ACPI Object oriented interfaces + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsxfobj") + +/******************************************************************************* + * + * FUNCTION: acpi_get_type + * + * PARAMETERS: handle - Handle of object whose type is desired + * ret_type - Where the type will be placed + * + * RETURN: Status + * + * DESCRIPTION: This routine returns the type associatd with a particular handle + * + ******************************************************************************/ +acpi_status acpi_get_type(acpi_handle handle, acpi_object_type * ret_type) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Parameter Validation */ + + if (!ret_type) { + return (AE_BAD_PARAMETER); + } + + /* + * Special case for the predefined Root Node + * (return type ANY) + */ + if (handle == ACPI_ROOT_OBJECT) { + *ret_type = ACPI_TYPE_ANY; + return (AE_OK); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_validate_handle(handle); + if (!node) { + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (AE_BAD_PARAMETER); + } + + *ret_type = node->type; + + status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_type) + +/******************************************************************************* + * + * FUNCTION: acpi_get_parent + * + * PARAMETERS: handle - Handle of object whose parent is desired + * ret_handle - Where the parent handle will be placed + * + * RETURN: Status + * + * DESCRIPTION: Returns a handle to the parent of the object represented by + * Handle. + * + ******************************************************************************/ +acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle) +{ + struct acpi_namespace_node *node; + struct acpi_namespace_node *parent_node; + acpi_status status; + + if (!ret_handle) { + return (AE_BAD_PARAMETER); + } + + /* Special case for the predefined Root Node (no parent) */ + + if (handle == ACPI_ROOT_OBJECT) { + return (AE_NULL_ENTRY); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_validate_handle(handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Get the parent entry */ + + parent_node = node->parent; + *ret_handle = ACPI_CAST_PTR(acpi_handle, parent_node); + + /* Return exception if parent is null */ + + if (!parent_node) { + status = AE_NULL_ENTRY; + } + +unlock_and_exit: + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_parent) + +/******************************************************************************* + * + * FUNCTION: acpi_get_next_object + * + * PARAMETERS: type - Type of object to be searched for + * parent - Parent object whose children we are getting + * last_child - Previous child that was found. + * The NEXT child will be returned + * ret_handle - Where handle to the next object is placed + * + * RETURN: Status + * + * DESCRIPTION: Return the next peer object within the namespace. If Handle is + * valid, Scope is ignored. Otherwise, the first object within + * Scope is returned. + * + ******************************************************************************/ +acpi_status +acpi_get_next_object(acpi_object_type type, + acpi_handle parent, + acpi_handle child, acpi_handle * ret_handle) +{ + acpi_status status; + struct acpi_namespace_node *node; + struct acpi_namespace_node *parent_node = NULL; + struct acpi_namespace_node *child_node = NULL; + + /* Parameter validation */ + + if (type > ACPI_TYPE_EXTERNAL_MAX) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* If null handle, use the parent */ + + if (!child) { + + /* Start search at the beginning of the specified scope */ + + parent_node = acpi_ns_validate_handle(parent); + if (!parent_node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } else { + /* Non-null handle, ignore the parent */ + /* Convert and validate the handle */ + + child_node = acpi_ns_validate_handle(child); + if (!child_node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + /* Internal function does the real work */ + + node = acpi_ns_get_next_node_typed(type, parent_node, child_node); + if (!node) { + status = AE_NOT_FOUND; + goto unlock_and_exit; + } + + if (ret_handle) { + *ret_handle = ACPI_CAST_PTR(acpi_handle, node); + } + +unlock_and_exit: + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_next_object) diff --git a/kernel/drivers/acpi/acpica/psargs.c b/kernel/drivers/acpi/acpica/psargs.c new file mode 100644 index 000000000..6d0387705 --- /dev/null +++ b/kernel/drivers/acpi/acpica/psargs.c @@ -0,0 +1,874 @@ +/****************************************************************************** + * + * Module Name: psargs - Parse AML opcode arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acnamesp.h" +#include "acdispat.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psargs") + +/* Local prototypes */ +static u32 +acpi_ps_get_next_package_length(struct acpi_parse_state *parser_state); + +static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state + *parser_state); + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_package_length + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Decoded package length. On completion, the AML pointer points + * past the length byte or bytes. + * + * DESCRIPTION: Decode and return a package length field. + * Note: Largest package length is 28 bits, from ACPI specification + * + ******************************************************************************/ + +static u32 +acpi_ps_get_next_package_length(struct acpi_parse_state *parser_state) +{ + u8 *aml = parser_state->aml; + u32 package_length = 0; + u32 byte_count; + u8 byte_zero_mask = 0x3F; /* Default [0:5] */ + + ACPI_FUNCTION_TRACE(ps_get_next_package_length); + + /* + * Byte 0 bits [6:7] contain the number of additional bytes + * used to encode the package length, either 0,1,2, or 3 + */ + byte_count = (aml[0] >> 6); + parser_state->aml += ((acpi_size) byte_count + 1); + + /* Get bytes 3, 2, 1 as needed */ + + while (byte_count) { + /* + * Final bit positions for the package length bytes: + * Byte3->[20:27] + * Byte2->[12:19] + * Byte1->[04:11] + * Byte0->[00:03] + */ + package_length |= (aml[byte_count] << ((byte_count << 3) - 4)); + + byte_zero_mask = 0x0F; /* Use bits [0:3] of byte 0 */ + byte_count--; + } + + /* Byte 0 is a special case, either bits [0:3] or [0:5] are used */ + + package_length |= (aml[0] & byte_zero_mask); + return_UINT32(package_length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_package_end + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Pointer to end-of-package +1 + * + * DESCRIPTION: Get next package length and return a pointer past the end of + * the package. Consumes the package length field + * + ******************************************************************************/ + +u8 *acpi_ps_get_next_package_end(struct acpi_parse_state *parser_state) +{ + u8 *start = parser_state->aml; + u32 package_length; + + ACPI_FUNCTION_TRACE(ps_get_next_package_end); + + /* Function below updates parser_state->Aml */ + + package_length = acpi_ps_get_next_package_length(parser_state); + + return_PTR(start + package_length); /* end of package */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_namestring + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Pointer to the start of the name string (pointer points into + * the AML. + * + * DESCRIPTION: Get next raw namestring within the AML stream. Handles all name + * prefix characters. Set parser state to point past the string. + * (Name is consumed from the AML.) + * + ******************************************************************************/ + +char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state) +{ + u8 *start = parser_state->aml; + u8 *end = parser_state->aml; + + ACPI_FUNCTION_TRACE(ps_get_next_namestring); + + /* Point past any namestring prefix characters (backslash or carat) */ + + while (ACPI_IS_ROOT_PREFIX(*end) || ACPI_IS_PARENT_PREFIX(*end)) { + end++; + } + + /* Decode the path prefix character */ + + switch (*end) { + case 0: + + /* null_name */ + + if (end == start) { + start = NULL; + } + end++; + break; + + case AML_DUAL_NAME_PREFIX: + + /* Two name segments */ + + end += 1 + (2 * ACPI_NAME_SIZE); + break; + + case AML_MULTI_NAME_PREFIX_OP: + + /* Multiple name segments, 4 chars each, count in next byte */ + + end += 2 + (*(end + 1) * ACPI_NAME_SIZE); + break; + + default: + + /* Single name segment */ + + end += ACPI_NAME_SIZE; + break; + } + + parser_state->aml = end; + return_PTR((char *)start); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_namepath + * + * PARAMETERS: parser_state - Current parser state object + * arg - Where the namepath will be stored + * arg_count - If the namepath points to a control method + * the method's argument is returned here. + * possible_method_call - Whether the namepath can possibly be the + * start of a method call + * + * RETURN: Status + * + * DESCRIPTION: Get next name (if method call, return # of required args). + * Names are looked up in the internal namespace to determine + * if the name represents a control method. If a method + * is found, the number of arguments to the method is returned. + * This information is critical for parsing to continue correctly. + * + ******************************************************************************/ + +acpi_status +acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + union acpi_parse_object *arg, u8 possible_method_call) +{ + acpi_status status; + char *path; + union acpi_parse_object *name_op; + union acpi_operand_object *method_desc; + struct acpi_namespace_node *node; + u8 *start = parser_state->aml; + + ACPI_FUNCTION_TRACE(ps_get_next_namepath); + + path = acpi_ps_get_next_namestring(parser_state); + acpi_ps_init_op(arg, AML_INT_NAMEPATH_OP); + + /* Null path case is allowed, just exit */ + + if (!path) { + arg->common.value.name = path; + return_ACPI_STATUS(AE_OK); + } + + /* + * Lookup the name in the internal namespace, starting with the current + * scope. We don't want to add anything new to the namespace here, + * however, so we use MODE_EXECUTE. + * Allow searching of the parent tree, but don't open a new scope - + * we just want to lookup the object (must be mode EXECUTE to perform + * the upsearch) + */ + status = acpi_ns_lookup(walk_state->scope_info, path, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, + NULL, &node); + + /* + * If this name is a control method invocation, we must + * setup the method call + */ + if (ACPI_SUCCESS(status) && + possible_method_call && (node->type == ACPI_TYPE_METHOD)) { + if (walk_state->opcode == AML_UNLOAD_OP) { + /* + * acpi_ps_get_next_namestring has increased the AML pointer, + * so we need to restore the saved AML pointer for method call. + */ + walk_state->parser_state.aml = start; + walk_state->arg_count = 1; + acpi_ps_init_op(arg, AML_INT_METHODCALL_OP); + return_ACPI_STATUS(AE_OK); + } + + /* This name is actually a control method invocation */ + + method_desc = acpi_ns_get_attached_object(node); + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Control Method - %p Desc %p Path=%p\n", node, + method_desc, path)); + + name_op = acpi_ps_alloc_op(AML_INT_NAMEPATH_OP); + if (!name_op) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Change Arg into a METHOD CALL and attach name to it */ + + acpi_ps_init_op(arg, AML_INT_METHODCALL_OP); + name_op->common.value.name = path; + + /* Point METHODCALL/NAME to the METHOD Node */ + + name_op->common.node = node; + acpi_ps_append_arg(arg, name_op); + + if (!method_desc) { + ACPI_ERROR((AE_INFO, + "Control Method %p has no attached object", + node)); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Control Method - %p Args %X\n", + node, method_desc->method.param_count)); + + /* Get the number of arguments to expect */ + + walk_state->arg_count = method_desc->method.param_count; + return_ACPI_STATUS(AE_OK); + } + + /* + * Special handling if the name was not found during the lookup - + * some not_found cases are allowed + */ + if (status == AE_NOT_FOUND) { + + /* 1) not_found is ok during load pass 1/2 (allow forward references) */ + + if ((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) != + ACPI_PARSE_EXECUTE) { + status = AE_OK; + } + + /* 2) not_found during a cond_ref_of(x) is ok by definition */ + + else if (walk_state->op->common.aml_opcode == + AML_COND_REF_OF_OP) { + status = AE_OK; + } + + /* + * 3) not_found while building a Package is ok at this point, we + * may flag as an error later if slack mode is not enabled. + * (Some ASL code depends on allowing this behavior) + */ + else if ((arg->common.parent) && + ((arg->common.parent->common.aml_opcode == + AML_PACKAGE_OP) + || (arg->common.parent->common.aml_opcode == + AML_VAR_PACKAGE_OP))) { + status = AE_OK; + } + } + + /* Final exception check (may have been changed from code above) */ + + if (ACPI_FAILURE(status)) { + ACPI_ERROR_NAMESPACE(path, status); + + if ((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == + ACPI_PARSE_EXECUTE) { + + /* Report a control method execution error */ + + status = acpi_ds_method_error(status, walk_state); + } + } + + /* Save the namepath */ + + arg->common.value.name = path; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_simple_arg + * + * PARAMETERS: parser_state - Current parser state object + * arg_type - The argument type (AML_*_ARG) + * arg - Where the argument is returned + * + * RETURN: None + * + * DESCRIPTION: Get the next simple argument (constant, string, or namestring) + * + ******************************************************************************/ + +void +acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, + u32 arg_type, union acpi_parse_object *arg) +{ + u32 length; + u16 opcode; + u8 *aml = parser_state->aml; + + ACPI_FUNCTION_TRACE_U32(ps_get_next_simple_arg, arg_type); + + switch (arg_type) { + case ARGP_BYTEDATA: + + /* Get 1 byte from the AML stream */ + + opcode = AML_BYTE_OP; + arg->common.value.integer = (u64) *aml; + length = 1; + break; + + case ARGP_WORDDATA: + + /* Get 2 bytes from the AML stream */ + + opcode = AML_WORD_OP; + ACPI_MOVE_16_TO_64(&arg->common.value.integer, aml); + length = 2; + break; + + case ARGP_DWORDDATA: + + /* Get 4 bytes from the AML stream */ + + opcode = AML_DWORD_OP; + ACPI_MOVE_32_TO_64(&arg->common.value.integer, aml); + length = 4; + break; + + case ARGP_QWORDDATA: + + /* Get 8 bytes from the AML stream */ + + opcode = AML_QWORD_OP; + ACPI_MOVE_64_TO_64(&arg->common.value.integer, aml); + length = 8; + break; + + case ARGP_CHARLIST: + + /* Get a pointer to the string, point past the string */ + + opcode = AML_STRING_OP; + arg->common.value.string = ACPI_CAST_PTR(char, aml); + + /* Find the null terminator */ + + length = 0; + while (aml[length]) { + length++; + } + length++; + break; + + case ARGP_NAME: + case ARGP_NAMESTRING: + + acpi_ps_init_op(arg, AML_INT_NAMEPATH_OP); + arg->common.value.name = + acpi_ps_get_next_namestring(parser_state); + return_VOID; + + default: + + ACPI_ERROR((AE_INFO, "Invalid ArgType 0x%X", arg_type)); + return_VOID; + } + + acpi_ps_init_op(arg, opcode); + parser_state->aml += length; + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_field + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: A newly allocated FIELD op + * + * DESCRIPTION: Get next field (named_field, reserved_field, or access_field) + * + ******************************************************************************/ + +static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state + *parser_state) +{ + u32 aml_offset; + union acpi_parse_object *field; + union acpi_parse_object *arg = NULL; + u16 opcode; + u32 name; + u8 access_type; + u8 access_attribute; + u8 access_length; + u32 pkg_length; + u8 *pkg_end; + u32 buffer_length; + + ACPI_FUNCTION_TRACE(ps_get_next_field); + + aml_offset = + (u32)ACPI_PTR_DIFF(parser_state->aml, parser_state->aml_start); + + /* Determine field type */ + + switch (ACPI_GET8(parser_state->aml)) { + case AML_FIELD_OFFSET_OP: + + opcode = AML_INT_RESERVEDFIELD_OP; + parser_state->aml++; + break; + + case AML_FIELD_ACCESS_OP: + + opcode = AML_INT_ACCESSFIELD_OP; + parser_state->aml++; + break; + + case AML_FIELD_CONNECTION_OP: + + opcode = AML_INT_CONNECTION_OP; + parser_state->aml++; + break; + + case AML_FIELD_EXT_ACCESS_OP: + + opcode = AML_INT_EXTACCESSFIELD_OP; + parser_state->aml++; + break; + + default: + + opcode = AML_INT_NAMEDFIELD_OP; + break; + } + + /* Allocate a new field op */ + + field = acpi_ps_alloc_op(opcode); + if (!field) { + return_PTR(NULL); + } + + field->common.aml_offset = aml_offset; + + /* Decode the field type */ + + switch (opcode) { + case AML_INT_NAMEDFIELD_OP: + + /* Get the 4-character name */ + + ACPI_MOVE_32_TO_32(&name, parser_state->aml); + acpi_ps_set_name(field, name); + parser_state->aml += ACPI_NAME_SIZE; + + /* Get the length which is encoded as a package length */ + + field->common.value.size = + acpi_ps_get_next_package_length(parser_state); + break; + + case AML_INT_RESERVEDFIELD_OP: + + /* Get the length which is encoded as a package length */ + + field->common.value.size = + acpi_ps_get_next_package_length(parser_state); + break; + + case AML_INT_ACCESSFIELD_OP: + case AML_INT_EXTACCESSFIELD_OP: + + /* + * Get access_type and access_attrib and merge into the field Op + * access_type is first operand, access_attribute is second. stuff + * these bytes into the node integer value for convenience. + */ + + /* Get the two bytes (Type/Attribute) */ + + access_type = ACPI_GET8(parser_state->aml); + parser_state->aml++; + access_attribute = ACPI_GET8(parser_state->aml); + parser_state->aml++; + + field->common.value.integer = (u8)access_type; + field->common.value.integer |= (u16)(access_attribute << 8); + + /* This opcode has a third byte, access_length */ + + if (opcode == AML_INT_EXTACCESSFIELD_OP) { + access_length = ACPI_GET8(parser_state->aml); + parser_state->aml++; + + field->common.value.integer |= + (u32)(access_length << 16); + } + break; + + case AML_INT_CONNECTION_OP: + + /* + * Argument for Connection operator can be either a Buffer + * (resource descriptor), or a name_string. + */ + if (ACPI_GET8(parser_state->aml) == AML_BUFFER_OP) { + parser_state->aml++; + + pkg_end = parser_state->aml; + pkg_length = + acpi_ps_get_next_package_length(parser_state); + pkg_end += pkg_length; + + if (parser_state->aml < pkg_end) { + + /* Non-empty list */ + + arg = acpi_ps_alloc_op(AML_INT_BYTELIST_OP); + if (!arg) { + acpi_ps_free_op(field); + return_PTR(NULL); + } + + /* Get the actual buffer length argument */ + + opcode = ACPI_GET8(parser_state->aml); + parser_state->aml++; + + switch (opcode) { + case AML_BYTE_OP: /* AML_BYTEDATA_ARG */ + + buffer_length = + ACPI_GET8(parser_state->aml); + parser_state->aml += 1; + break; + + case AML_WORD_OP: /* AML_WORDDATA_ARG */ + + buffer_length = + ACPI_GET16(parser_state->aml); + parser_state->aml += 2; + break; + + case AML_DWORD_OP: /* AML_DWORDATA_ARG */ + + buffer_length = + ACPI_GET32(parser_state->aml); + parser_state->aml += 4; + break; + + default: + + buffer_length = 0; + break; + } + + /* Fill in bytelist data */ + + arg->named.value.size = buffer_length; + arg->named.data = parser_state->aml; + } + + /* Skip to End of byte data */ + + parser_state->aml = pkg_end; + } else { + arg = acpi_ps_alloc_op(AML_INT_NAMEPATH_OP); + if (!arg) { + acpi_ps_free_op(field); + return_PTR(NULL); + } + + /* Get the Namestring argument */ + + arg->common.value.name = + acpi_ps_get_next_namestring(parser_state); + } + + /* Link the buffer/namestring to parent (CONNECTION_OP) */ + + acpi_ps_append_arg(field, arg); + break; + + default: + + /* Opcode was set in previous switch */ + break; + } + + return_PTR(field); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_arg + * + * PARAMETERS: walk_state - Current state + * parser_state - Current parser state object + * arg_type - The argument type (AML_*_ARG) + * return_arg - Where the next arg is returned + * + * RETURN: Status, and an op object containing the next argument. + * + * DESCRIPTION: Get next argument (including complex list arguments that require + * pushing the parser stack) + * + ******************************************************************************/ + +acpi_status +acpi_ps_get_next_arg(struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + u32 arg_type, union acpi_parse_object **return_arg) +{ + union acpi_parse_object *arg = NULL; + union acpi_parse_object *prev = NULL; + union acpi_parse_object *field; + u32 subop; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_PTR(ps_get_next_arg, parser_state); + + switch (arg_type) { + case ARGP_BYTEDATA: + case ARGP_WORDDATA: + case ARGP_DWORDDATA: + case ARGP_CHARLIST: + case ARGP_NAME: + case ARGP_NAMESTRING: + + /* Constants, strings, and namestrings are all the same size */ + + arg = acpi_ps_alloc_op(AML_BYTE_OP); + if (!arg) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + acpi_ps_get_next_simple_arg(parser_state, arg_type, arg); + break; + + case ARGP_PKGLENGTH: + + /* Package length, nothing returned */ + + parser_state->pkg_end = + acpi_ps_get_next_package_end(parser_state); + break; + + case ARGP_FIELDLIST: + + if (parser_state->aml < parser_state->pkg_end) { + + /* Non-empty list */ + + while (parser_state->aml < parser_state->pkg_end) { + field = acpi_ps_get_next_field(parser_state); + if (!field) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + if (prev) { + prev->common.next = field; + } else { + arg = field; + } + prev = field; + } + + /* Skip to End of byte data */ + + parser_state->aml = parser_state->pkg_end; + } + break; + + case ARGP_BYTELIST: + + if (parser_state->aml < parser_state->pkg_end) { + + /* Non-empty list */ + + arg = acpi_ps_alloc_op(AML_INT_BYTELIST_OP); + if (!arg) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Fill in bytelist data */ + + arg->common.value.size = (u32) + ACPI_PTR_DIFF(parser_state->pkg_end, + parser_state->aml); + arg->named.data = parser_state->aml; + + /* Skip to End of byte data */ + + parser_state->aml = parser_state->pkg_end; + } + break; + + case ARGP_TARGET: + case ARGP_SUPERNAME: + case ARGP_SIMPLENAME: + + subop = acpi_ps_peek_opcode(parser_state); + if (subop == 0 || + acpi_ps_is_leading_char(subop) || + ACPI_IS_ROOT_PREFIX(subop) || + ACPI_IS_PARENT_PREFIX(subop)) { + + /* null_name or name_string */ + + arg = acpi_ps_alloc_op(AML_INT_NAMEPATH_OP); + if (!arg) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* To support super_name arg of Unload */ + + if (walk_state->opcode == AML_UNLOAD_OP) { + status = + acpi_ps_get_next_namepath(walk_state, + parser_state, arg, + 1); + + /* + * If the super_name arg of Unload is a method call, + * we have restored the AML pointer, just free this Arg + */ + if (arg->common.aml_opcode == + AML_INT_METHODCALL_OP) { + acpi_ps_free_op(arg); + arg = NULL; + } + } else { + status = + acpi_ps_get_next_namepath(walk_state, + parser_state, arg, + 0); + } + } else { + /* Single complex argument, nothing returned */ + + walk_state->arg_count = 1; + } + break; + + case ARGP_DATAOBJ: + case ARGP_TERMARG: + + /* Single complex argument, nothing returned */ + + walk_state->arg_count = 1; + break; + + case ARGP_DATAOBJLIST: + case ARGP_TERMLIST: + case ARGP_OBJLIST: + + if (parser_state->aml < parser_state->pkg_end) { + + /* Non-empty list of variable arguments, nothing returned */ + + walk_state->arg_count = ACPI_VAR_ARGS; + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Invalid ArgType: 0x%X", arg_type)); + status = AE_AML_OPERAND_TYPE; + break; + } + + *return_arg = arg; + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/psloop.c b/kernel/drivers/acpi/acpica/psloop.c new file mode 100644 index 000000000..90437227d --- /dev/null +++ b/kernel/drivers/acpi/acpica/psloop.c @@ -0,0 +1,626 @@ +/****************************************************************************** + * + * Module Name: psloop - Main AML parse loop + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Parse the AML and build an operation tree as most interpreters, (such as + * Perl) do. Parsing is done by hand rather than with a YACC generated parser + * to tightly constrain stack and dynamic memory usage. Parsing is kept + * flexible and the code fairly compact by parsing based on a list of AML + * opcode templates in aml_op_info[]. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psloop") + +/* Local prototypes */ +static acpi_status +acpi_ps_get_arguments(struct acpi_walk_state *walk_state, + u8 * aml_op_start, union acpi_parse_object *op); + +static void +acpi_ps_link_module_code(union acpi_parse_object *parent_op, + u8 *aml_start, u32 aml_length, acpi_owner_id owner_id); + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_arguments + * + * PARAMETERS: walk_state - Current state + * aml_op_start - Op start in AML + * op - Current Op + * + * RETURN: Status + * + * DESCRIPTION: Get arguments for passed Op. + * + ******************************************************************************/ + +static acpi_status +acpi_ps_get_arguments(struct acpi_walk_state *walk_state, + u8 * aml_op_start, union acpi_parse_object *op) +{ + acpi_status status = AE_OK; + union acpi_parse_object *arg = NULL; + const struct acpi_opcode_info *op_info; + + ACPI_FUNCTION_TRACE_PTR(ps_get_arguments, walk_state); + + switch (op->common.aml_opcode) { + case AML_BYTE_OP: /* AML_BYTEDATA_ARG */ + case AML_WORD_OP: /* AML_WORDDATA_ARG */ + case AML_DWORD_OP: /* AML_DWORDATA_ARG */ + case AML_QWORD_OP: /* AML_QWORDATA_ARG */ + case AML_STRING_OP: /* AML_ASCIICHARLIST_ARG */ + + /* Fill in constant or string argument directly */ + + acpi_ps_get_next_simple_arg(&(walk_state->parser_state), + GET_CURRENT_ARG_TYPE(walk_state-> + arg_types), + op); + break; + + case AML_INT_NAMEPATH_OP: /* AML_NAMESTRING_ARG */ + + status = + acpi_ps_get_next_namepath(walk_state, + &(walk_state->parser_state), op, + 1); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + walk_state->arg_types = 0; + break; + + default: + /* + * Op is not a constant or string, append each argument to the Op + */ + while (GET_CURRENT_ARG_TYPE(walk_state->arg_types) + && !walk_state->arg_count) { + walk_state->aml_offset = + (u32) ACPI_PTR_DIFF(walk_state->parser_state.aml, + walk_state->parser_state. + aml_start); + + status = + acpi_ps_get_next_arg(walk_state, + &(walk_state->parser_state), + GET_CURRENT_ARG_TYPE + (walk_state->arg_types), &arg); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (arg) { + arg->common.aml_offset = walk_state->aml_offset; + acpi_ps_append_arg(op, arg); + } + + INCREMENT_ARG_LIST(walk_state->arg_types); + } + + /* + * Handle executable code at "module-level". This refers to + * executable opcodes that appear outside of any control method. + */ + if ((walk_state->pass_number <= ACPI_IMODE_LOAD_PASS2) && + ((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE) == 0)) { + /* + * We want to skip If/Else/While constructs during Pass1 because we + * want to actually conditionally execute the code during Pass2. + * + * Except for disassembly, where we always want to walk the + * If/Else/While packages + */ + switch (op->common.aml_opcode) { + case AML_IF_OP: + case AML_ELSE_OP: + case AML_WHILE_OP: + /* + * Currently supported module-level opcodes are: + * IF/ELSE/WHILE. These appear to be the most common, + * and easiest to support since they open an AML + * package. + */ + if (walk_state->pass_number == + ACPI_IMODE_LOAD_PASS1) { + acpi_ps_link_module_code(op->common. + parent, + aml_op_start, + (u32) + (walk_state-> + parser_state. + pkg_end - + aml_op_start), + walk_state-> + owner_id); + } + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Pass1: Skipping an If/Else/While body\n")); + + /* Skip body of if/else/while in pass 1 */ + + walk_state->parser_state.aml = + walk_state->parser_state.pkg_end; + walk_state->arg_count = 0; + break; + + default: + /* + * Check for an unsupported executable opcode at module + * level. We must be in PASS1, the parent must be a SCOPE, + * The opcode class must be EXECUTE, and the opcode must + * not be an argument to another opcode. + */ + if ((walk_state->pass_number == + ACPI_IMODE_LOAD_PASS1) + && (op->common.parent->common.aml_opcode == + AML_SCOPE_OP)) { + op_info = + acpi_ps_get_opcode_info(op->common. + aml_opcode); + if ((op_info->class == + AML_CLASS_EXECUTE) && (!arg)) { + ACPI_WARNING((AE_INFO, + "Unsupported module-level executable opcode " + "0x%.2X at table offset 0x%.4X", + op->common. + aml_opcode, + (u32) + (ACPI_PTR_DIFF + (aml_op_start, + walk_state-> + parser_state. + aml_start) + + sizeof(struct + acpi_table_header)))); + } + } + break; + } + } + + /* Special processing for certain opcodes */ + + switch (op->common.aml_opcode) { + case AML_METHOD_OP: + /* + * Skip parsing of control method because we don't have enough + * info in the first pass to parse it correctly. + * + * Save the length and address of the body + */ + op->named.data = walk_state->parser_state.aml; + op->named.length = (u32) + (walk_state->parser_state.pkg_end - + walk_state->parser_state.aml); + + /* Skip body of method */ + + walk_state->parser_state.aml = + walk_state->parser_state.pkg_end; + walk_state->arg_count = 0; + break; + + case AML_BUFFER_OP: + case AML_PACKAGE_OP: + case AML_VAR_PACKAGE_OP: + + if ((op->common.parent) && + (op->common.parent->common.aml_opcode == + AML_NAME_OP) + && (walk_state->pass_number <= + ACPI_IMODE_LOAD_PASS2)) { + /* + * Skip parsing of Buffers and Packages because we don't have + * enough info in the first pass to parse them correctly. + */ + op->named.data = aml_op_start; + op->named.length = (u32) + (walk_state->parser_state.pkg_end - + aml_op_start); + + /* Skip body */ + + walk_state->parser_state.aml = + walk_state->parser_state.pkg_end; + walk_state->arg_count = 0; + } + break; + + case AML_WHILE_OP: + + if (walk_state->control_state) { + walk_state->control_state->control.package_end = + walk_state->parser_state.pkg_end; + } + break; + + default: + + /* No action for all other opcodes */ + + break; + } + + break; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_link_module_code + * + * PARAMETERS: parent_op - Parent parser op + * aml_start - Pointer to the AML + * aml_length - Length of executable AML + * owner_id - owner_id of module level code + * + * RETURN: None. + * + * DESCRIPTION: Wrap the module-level code with a method object and link the + * object to the global list. Note, the mutex field of the method + * object is used to link multiple module-level code objects. + * + ******************************************************************************/ + +static void +acpi_ps_link_module_code(union acpi_parse_object *parent_op, + u8 *aml_start, u32 aml_length, acpi_owner_id owner_id) +{ + union acpi_operand_object *prev; + union acpi_operand_object *next; + union acpi_operand_object *method_obj; + struct acpi_namespace_node *parent_node; + + /* Get the tail of the list */ + + prev = next = acpi_gbl_module_code_list; + while (next) { + prev = next; + next = next->method.mutex; + } + + /* + * Insert the module level code into the list. Merge it if it is + * adjacent to the previous element. + */ + if (!prev || + ((prev->method.aml_start + prev->method.aml_length) != aml_start)) { + + /* Create, initialize, and link a new temporary method object */ + + method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); + if (!method_obj) { + return; + } + + if (parent_op->common.node) { + parent_node = parent_op->common.node; + } else { + parent_node = acpi_gbl_root_node; + } + + method_obj->method.aml_start = aml_start; + method_obj->method.aml_length = aml_length; + method_obj->method.owner_id = owner_id; + method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL; + + /* + * Save the parent node in next_object. This is cheating, but we + * don't want to expand the method object. + */ + method_obj->method.next_object = + ACPI_CAST_PTR(union acpi_operand_object, parent_node); + + if (!prev) { + acpi_gbl_module_code_list = method_obj; + } else { + prev->method.mutex = method_obj; + } + } else { + prev->method.aml_length += aml_length; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_parse_loop + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Parse AML (pointed to by the current parser state) and return + * a tree of ops. + * + ******************************************************************************/ + +acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_parse_object *op = NULL; /* current op */ + struct acpi_parse_state *parser_state; + u8 *aml_op_start = NULL; + + ACPI_FUNCTION_TRACE_PTR(ps_parse_loop, walk_state); + + if (walk_state->descending_callback == NULL) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + parser_state = &walk_state->parser_state; + walk_state->arg_types = 0; + +#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) + + if (walk_state->walk_type & ACPI_WALK_METHOD_RESTART) { + + /* We are restarting a preempted control method */ + + if (acpi_ps_has_completed_scope(parser_state)) { + /* + * We must check if a predicate to an IF or WHILE statement + * was just completed + */ + if ((parser_state->scope->parse_scope.op) && + ((parser_state->scope->parse_scope.op->common. + aml_opcode == AML_IF_OP) + || (parser_state->scope->parse_scope.op->common. + aml_opcode == AML_WHILE_OP)) + && (walk_state->control_state) + && (walk_state->control_state->common.state == + ACPI_CONTROL_PREDICATE_EXECUTING)) { + /* + * A predicate was just completed, get the value of the + * predicate and branch based on that value + */ + walk_state->op = NULL; + status = + acpi_ds_get_predicate_value(walk_state, + ACPI_TO_POINTER + (TRUE)); + if (ACPI_FAILURE(status) + && ((status & AE_CODE_MASK) != + AE_CODE_CONTROL)) { + if (status == AE_AML_NO_RETURN_VALUE) { + ACPI_EXCEPTION((AE_INFO, status, + "Invoked method did not return a value")); + } + + ACPI_EXCEPTION((AE_INFO, status, + "GetPredicate Failed")); + return_ACPI_STATUS(status); + } + + status = + acpi_ps_next_parse_state(walk_state, op, + status); + } + + acpi_ps_pop_scope(parser_state, &op, + &walk_state->arg_types, + &walk_state->arg_count); + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Popped scope, Op=%p\n", op)); + } else if (walk_state->prev_op) { + + /* We were in the middle of an op */ + + op = walk_state->prev_op; + walk_state->arg_types = walk_state->prev_arg_types; + } + } +#endif + + /* Iterative parsing loop, while there is more AML to process: */ + + while ((parser_state->aml < parser_state->aml_end) || (op)) { + aml_op_start = parser_state->aml; + if (!op) { + status = + acpi_ps_create_op(walk_state, aml_op_start, &op); + if (ACPI_FAILURE(status)) { + if (status == AE_CTRL_PARSE_CONTINUE) { + continue; + } + + if (status == AE_CTRL_PARSE_PENDING) { + status = AE_OK; + } + + if (status == AE_CTRL_TERMINATE) { + return_ACPI_STATUS(status); + } + + status = + acpi_ps_complete_op(walk_state, &op, + status); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + continue; + } + + op->common.aml_offset = walk_state->aml_offset; + + if (walk_state->op_info) { + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Opcode %4.4X [%s] Op %p Aml %p AmlOffset %5.5X\n", + (u32) op->common.aml_opcode, + walk_state->op_info->name, op, + parser_state->aml, + op->common.aml_offset)); + } + } + + /* + * Start arg_count at zero because we don't know if there are + * any args yet + */ + walk_state->arg_count = 0; + + /* Are there any arguments that must be processed? */ + + if (walk_state->arg_types) { + + /* Get arguments */ + + status = + acpi_ps_get_arguments(walk_state, aml_op_start, op); + if (ACPI_FAILURE(status)) { + status = + acpi_ps_complete_op(walk_state, &op, + status); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + continue; + } + } + + /* Check for arguments that need to be processed */ + + if (walk_state->arg_count) { + /* + * There are arguments (complex ones), push Op and + * prepare for argument + */ + status = acpi_ps_push_scope(parser_state, op, + walk_state->arg_types, + walk_state->arg_count); + if (ACPI_FAILURE(status)) { + status = + acpi_ps_complete_op(walk_state, &op, + status); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + continue; + } + + op = NULL; + continue; + } + + /* + * All arguments have been processed -- Op is complete, + * prepare for next + */ + walk_state->op_info = + acpi_ps_get_opcode_info(op->common.aml_opcode); + if (walk_state->op_info->flags & AML_NAMED) { + if (op->common.aml_opcode == AML_REGION_OP || + op->common.aml_opcode == AML_DATA_REGION_OP) { + /* + * Skip parsing of control method or opregion body, + * because we don't have enough info in the first pass + * to parse them correctly. + * + * Completed parsing an op_region declaration, we now + * know the length. + */ + op->named.length = + (u32) (parser_state->aml - op->named.data); + } + } + + if (walk_state->op_info->flags & AML_CREATE) { + /* + * Backup to beginning of create_XXXfield declaration (1 for + * Opcode) + * + * body_length is unknown until we parse the body + */ + op->named.length = + (u32) (parser_state->aml - op->named.data); + } + + if (op->common.aml_opcode == AML_BANK_FIELD_OP) { + /* + * Backup to beginning of bank_field declaration + * + * body_length is unknown until we parse the body + */ + op->named.length = + (u32) (parser_state->aml - op->named.data); + } + + /* This op complete, notify the dispatcher */ + + if (walk_state->ascending_callback != NULL) { + walk_state->op = op; + walk_state->opcode = op->common.aml_opcode; + + status = walk_state->ascending_callback(walk_state); + status = + acpi_ps_next_parse_state(walk_state, op, status); + if (status == AE_CTRL_PENDING) { + status = AE_OK; + } + } + + status = acpi_ps_complete_op(walk_state, &op, status); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + } /* while parser_state->Aml */ + + status = acpi_ps_complete_final_op(walk_state, op, status); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/psobject.c b/kernel/drivers/acpi/acpica/psobject.c new file mode 100644 index 000000000..2f5ddd806 --- /dev/null +++ b/kernel/drivers/acpi/acpica/psobject.c @@ -0,0 +1,651 @@ +/****************************************************************************** + * + * Module Name: psobject - Support for parse objects + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psobject") + +/* Local prototypes */ +static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state); + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_aml_opcode + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Extract the next AML opcode from the input stream. + * + ******************************************************************************/ + +static acpi_status acpi_ps_get_aml_opcode(struct acpi_walk_state *walk_state) +{ + + ACPI_FUNCTION_TRACE_PTR(ps_get_aml_opcode, walk_state); + + walk_state->aml_offset = + (u32)ACPI_PTR_DIFF(walk_state->parser_state.aml, + walk_state->parser_state.aml_start); + walk_state->opcode = acpi_ps_peek_opcode(&(walk_state->parser_state)); + + /* + * First cut to determine what we have found: + * 1) A valid AML opcode + * 2) A name string + * 3) An unknown/invalid opcode + */ + walk_state->op_info = acpi_ps_get_opcode_info(walk_state->opcode); + + switch (walk_state->op_info->class) { + case AML_CLASS_ASCII: + case AML_CLASS_PREFIX: + /* + * Starts with a valid prefix or ASCII char, this is a name + * string. Convert the bare name string to a namepath. + */ + walk_state->opcode = AML_INT_NAMEPATH_OP; + walk_state->arg_types = ARGP_NAMESTRING; + break; + + case AML_CLASS_UNKNOWN: + + /* The opcode is unrecognized. Complain and skip unknown opcodes */ + + if (walk_state->pass_number == 2) { + ACPI_ERROR((AE_INFO, + "Unknown opcode 0x%.2X at table offset 0x%.4X, ignoring", + walk_state->opcode, + (u32)(walk_state->aml_offset + + sizeof(struct acpi_table_header)))); + + ACPI_DUMP_BUFFER((walk_state->parser_state.aml - 16), + 48); + +#ifdef ACPI_ASL_COMPILER + /* + * This is executed for the disassembler only. Output goes + * to the disassembled ASL output file. + */ + acpi_os_printf + ("/*\nError: Unknown opcode 0x%.2X at table offset 0x%.4X, context:\n", + walk_state->opcode, + (u32)(walk_state->aml_offset + + sizeof(struct acpi_table_header))); + + /* Dump the context surrounding the invalid opcode */ + + acpi_ut_dump_buffer(((u8 *)walk_state->parser_state. + aml - 16), 48, DB_BYTE_DISPLAY, + (walk_state->aml_offset + + sizeof(struct acpi_table_header) - + 16)); + acpi_os_printf(" */\n"); +#endif + } + + /* Increment past one-byte or two-byte opcode */ + + walk_state->parser_state.aml++; + if (walk_state->opcode > 0xFF) { /* Can only happen if first byte is 0x5B */ + walk_state->parser_state.aml++; + } + + return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE); + + default: + + /* Found opcode info, this is a normal opcode */ + + walk_state->parser_state.aml += + acpi_ps_get_opcode_size(walk_state->opcode); + walk_state->arg_types = walk_state->op_info->parse_args; + break; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_build_named_op + * + * PARAMETERS: walk_state - Current state + * aml_op_start - Begin of named Op in AML + * unnamed_op - Early Op (not a named Op) + * op - Returned Op + * + * RETURN: Status + * + * DESCRIPTION: Parse a named Op + * + ******************************************************************************/ + +acpi_status +acpi_ps_build_named_op(struct acpi_walk_state *walk_state, + u8 *aml_op_start, + union acpi_parse_object *unnamed_op, + union acpi_parse_object **op) +{ + acpi_status status = AE_OK; + union acpi_parse_object *arg = NULL; + + ACPI_FUNCTION_TRACE_PTR(ps_build_named_op, walk_state); + + unnamed_op->common.value.arg = NULL; + unnamed_op->common.arg_list_length = 0; + unnamed_op->common.aml_opcode = walk_state->opcode; + + /* + * Get and append arguments until we find the node that contains + * the name (the type ARGP_NAME). + */ + while (GET_CURRENT_ARG_TYPE(walk_state->arg_types) && + (GET_CURRENT_ARG_TYPE(walk_state->arg_types) != ARGP_NAME)) { + status = + acpi_ps_get_next_arg(walk_state, + &(walk_state->parser_state), + GET_CURRENT_ARG_TYPE(walk_state-> + arg_types), &arg); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_ps_append_arg(unnamed_op, arg); + INCREMENT_ARG_LIST(walk_state->arg_types); + } + + /* + * Make sure that we found a NAME and didn't run out of arguments + */ + if (!GET_CURRENT_ARG_TYPE(walk_state->arg_types)) { + return_ACPI_STATUS(AE_AML_NO_OPERAND); + } + + /* We know that this arg is a name, move to next arg */ + + INCREMENT_ARG_LIST(walk_state->arg_types); + + /* + * Find the object. This will either insert the object into + * the namespace or simply look it up + */ + walk_state->op = NULL; + + status = walk_state->descending_callback(walk_state, op); + if (ACPI_FAILURE(status)) { + if (status != AE_CTRL_TERMINATE) { + ACPI_EXCEPTION((AE_INFO, status, + "During name lookup/catalog")); + } + return_ACPI_STATUS(status); + } + + if (!*op) { + return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE); + } + + status = acpi_ps_next_parse_state(walk_state, *op, status); + if (ACPI_FAILURE(status)) { + if (status == AE_CTRL_PENDING) { + status = AE_CTRL_PARSE_PENDING; + } + return_ACPI_STATUS(status); + } + + acpi_ps_append_arg(*op, unnamed_op->common.value.arg); + + if ((*op)->common.aml_opcode == AML_REGION_OP || + (*op)->common.aml_opcode == AML_DATA_REGION_OP) { + /* + * Defer final parsing of an operation_region body, because we don't + * have enough info in the first pass to parse it correctly (i.e., + * there may be method calls within the term_arg elements of the body.) + * + * However, we must continue parsing because the opregion is not a + * standalone package -- we don't know where the end is at this point. + * + * (Length is unknown until parse of the body complete) + */ + (*op)->named.data = aml_op_start; + (*op)->named.length = 0; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_create_op + * + * PARAMETERS: walk_state - Current state + * aml_op_start - Op start in AML + * new_op - Returned Op + * + * RETURN: Status + * + * DESCRIPTION: Get Op from AML + * + ******************************************************************************/ + +acpi_status +acpi_ps_create_op(struct acpi_walk_state *walk_state, + u8 *aml_op_start, union acpi_parse_object **new_op) +{ + acpi_status status = AE_OK; + union acpi_parse_object *op; + union acpi_parse_object *named_op = NULL; + union acpi_parse_object *parent_scope; + u8 argument_count; + const struct acpi_opcode_info *op_info; + + ACPI_FUNCTION_TRACE_PTR(ps_create_op, walk_state); + + status = acpi_ps_get_aml_opcode(walk_state); + if (status == AE_CTRL_PARSE_CONTINUE) { + return_ACPI_STATUS(AE_CTRL_PARSE_CONTINUE); + } + + /* Create Op structure and append to parent's argument list */ + + walk_state->op_info = acpi_ps_get_opcode_info(walk_state->opcode); + op = acpi_ps_alloc_op(walk_state->opcode); + if (!op) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + if (walk_state->op_info->flags & AML_NAMED) { + status = + acpi_ps_build_named_op(walk_state, aml_op_start, op, + &named_op); + acpi_ps_free_op(op); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + *new_op = named_op; + return_ACPI_STATUS(AE_OK); + } + + /* Not a named opcode, just allocate Op and append to parent */ + + if (walk_state->op_info->flags & AML_CREATE) { + /* + * Backup to beginning of create_XXXfield declaration + * body_length is unknown until we parse the body + */ + op->named.data = aml_op_start; + op->named.length = 0; + } + + if (walk_state->opcode == AML_BANK_FIELD_OP) { + /* + * Backup to beginning of bank_field declaration + * body_length is unknown until we parse the body + */ + op->named.data = aml_op_start; + op->named.length = 0; + } + + parent_scope = acpi_ps_get_parent_scope(&(walk_state->parser_state)); + acpi_ps_append_arg(parent_scope, op); + + if (parent_scope) { + op_info = + acpi_ps_get_opcode_info(parent_scope->common.aml_opcode); + if (op_info->flags & AML_HAS_TARGET) { + argument_count = + acpi_ps_get_argument_count(op_info->type); + if (parent_scope->common.arg_list_length > + argument_count) { + op->common.flags |= ACPI_PARSEOP_TARGET; + } + } else if (parent_scope->common.aml_opcode == AML_INCREMENT_OP) { + op->common.flags |= ACPI_PARSEOP_TARGET; + } + } + + if (walk_state->descending_callback != NULL) { + /* + * Find the object. This will either insert the object into + * the namespace or simply look it up + */ + walk_state->op = *new_op = op; + + status = walk_state->descending_callback(walk_state, &op); + status = acpi_ps_next_parse_state(walk_state, op, status); + if (status == AE_CTRL_PENDING) { + status = AE_CTRL_PARSE_PENDING; + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_complete_op + * + * PARAMETERS: walk_state - Current state + * op - Returned Op + * status - Parse status before complete Op + * + * RETURN: Status + * + * DESCRIPTION: Complete Op + * + ******************************************************************************/ + +acpi_status +acpi_ps_complete_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **op, acpi_status status) +{ + acpi_status status2; + + ACPI_FUNCTION_TRACE_PTR(ps_complete_op, walk_state); + + /* + * Finished one argument of the containing scope + */ + walk_state->parser_state.scope->parse_scope.arg_count--; + + /* Close this Op (will result in parse subtree deletion) */ + + status2 = acpi_ps_complete_this_op(walk_state, *op); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } + + *op = NULL; + + switch (status) { + case AE_OK: + + break; + + case AE_CTRL_TRANSFER: + + /* We are about to transfer to a called method */ + + walk_state->prev_op = NULL; + walk_state->prev_arg_types = walk_state->arg_types; + return_ACPI_STATUS(status); + + case AE_CTRL_END: + + acpi_ps_pop_scope(&(walk_state->parser_state), op, + &walk_state->arg_types, + &walk_state->arg_count); + + if (*op) { + walk_state->op = *op; + walk_state->op_info = + acpi_ps_get_opcode_info((*op)->common.aml_opcode); + walk_state->opcode = (*op)->common.aml_opcode; + + status = walk_state->ascending_callback(walk_state); + status = + acpi_ps_next_parse_state(walk_state, *op, status); + + status2 = acpi_ps_complete_this_op(walk_state, *op); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } + } + + status = AE_OK; + break; + + case AE_CTRL_BREAK: + case AE_CTRL_CONTINUE: + + /* Pop off scopes until we find the While */ + + while (!(*op) || ((*op)->common.aml_opcode != AML_WHILE_OP)) { + acpi_ps_pop_scope(&(walk_state->parser_state), op, + &walk_state->arg_types, + &walk_state->arg_count); + } + + /* Close this iteration of the While loop */ + + walk_state->op = *op; + walk_state->op_info = + acpi_ps_get_opcode_info((*op)->common.aml_opcode); + walk_state->opcode = (*op)->common.aml_opcode; + + status = walk_state->ascending_callback(walk_state); + status = acpi_ps_next_parse_state(walk_state, *op, status); + + status2 = acpi_ps_complete_this_op(walk_state, *op); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } + + status = AE_OK; + break; + + case AE_CTRL_TERMINATE: + + /* Clean up */ + do { + if (*op) { + status2 = + acpi_ps_complete_this_op(walk_state, *op); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } + + acpi_ut_delete_generic_state + (acpi_ut_pop_generic_state + (&walk_state->control_state)); + } + + acpi_ps_pop_scope(&(walk_state->parser_state), op, + &walk_state->arg_types, + &walk_state->arg_count); + + } while (*op); + + return_ACPI_STATUS(AE_OK); + + default: /* All other non-AE_OK status */ + + do { + if (*op) { + status2 = + acpi_ps_complete_this_op(walk_state, *op); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } + } + + acpi_ps_pop_scope(&(walk_state->parser_state), op, + &walk_state->arg_types, + &walk_state->arg_count); + + } while (*op); + +#if 0 + /* + * TBD: Cleanup parse ops on error + */ + if (*op == NULL) { + acpi_ps_pop_scope(parser_state, op, + &walk_state->arg_types, + &walk_state->arg_count); + } +#endif + walk_state->prev_op = NULL; + walk_state->prev_arg_types = walk_state->arg_types; + return_ACPI_STATUS(status); + } + + /* This scope complete? */ + + if (acpi_ps_has_completed_scope(&(walk_state->parser_state))) { + acpi_ps_pop_scope(&(walk_state->parser_state), op, + &walk_state->arg_types, + &walk_state->arg_count); + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "Popped scope, Op=%p\n", *op)); + } else { + *op = NULL; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_complete_final_op + * + * PARAMETERS: walk_state - Current state + * op - Current Op + * status - Current parse status before complete last + * Op + * + * RETURN: Status + * + * DESCRIPTION: Complete last Op. + * + ******************************************************************************/ + +acpi_status +acpi_ps_complete_final_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, acpi_status status) +{ + acpi_status status2; + + ACPI_FUNCTION_TRACE_PTR(ps_complete_final_op, walk_state); + + /* + * Complete the last Op (if not completed), and clear the scope stack. + * It is easily possible to end an AML "package" with an unbounded number + * of open scopes (such as when several ASL blocks are closed with + * sequential closing braces). We want to terminate each one cleanly. + */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "AML package complete at Op %p\n", + op)); + do { + if (op) { + if (walk_state->ascending_callback != NULL) { + walk_state->op = op; + walk_state->op_info = + acpi_ps_get_opcode_info(op->common. + aml_opcode); + walk_state->opcode = op->common.aml_opcode; + + status = + walk_state->ascending_callback(walk_state); + status = + acpi_ps_next_parse_state(walk_state, op, + status); + if (status == AE_CTRL_PENDING) { + status = + acpi_ps_complete_op(walk_state, &op, + AE_OK); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + if (status == AE_CTRL_TERMINATE) { + status = AE_OK; + + /* Clean up */ + do { + if (op) { + status2 = + acpi_ps_complete_this_op + (walk_state, op); + if (ACPI_FAILURE + (status2)) { + return_ACPI_STATUS + (status2); + } + } + + acpi_ps_pop_scope(& + (walk_state-> + parser_state), + &op, + &walk_state-> + arg_types, + &walk_state-> + arg_count); + + } while (op); + + return_ACPI_STATUS(status); + } + + else if (ACPI_FAILURE(status)) { + + /* First error is most important */ + + (void) + acpi_ps_complete_this_op(walk_state, + op); + return_ACPI_STATUS(status); + } + } + + status2 = acpi_ps_complete_this_op(walk_state, op); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } + } + + acpi_ps_pop_scope(&(walk_state->parser_state), &op, + &walk_state->arg_types, + &walk_state->arg_count); + + } while (op); + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/psopcode.c b/kernel/drivers/acpi/acpica/psopcode.c new file mode 100644 index 000000000..ed90fddf2 --- /dev/null +++ b/kernel/drivers/acpi/acpica/psopcode.c @@ -0,0 +1,658 @@ +/****************************************************************************** + * + * Module Name: psopcode - Parser/Interpreter opcode information table + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acopcode.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psopcode") + +/******************************************************************************* + * + * NAME: acpi_gbl_aml_op_info + * + * DESCRIPTION: Opcode table. Each entry contains + * The name is a simple ascii string, the operand specifier is an + * ascii string with one letter per operand. The letter specifies + * the operand type. + * + ******************************************************************************/ +/* + * Summary of opcode types/flags + * + + Opcodes that have associated namespace objects (AML_NSOBJECT flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_FIELD_OP + AML_INDEX_FIELD_OP + AML_BANK_FIELD_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + AML_INT_NAMEDFIELD_OP + AML_INT_METHODCALL_OP + AML_INT_NAMEPATH_OP + + Opcodes that are "namespace" opcodes (AML_NSOPCODE flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_FIELD_OP + AML_INDEX_FIELD_OP + AML_BANK_FIELD_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_INT_NAMEDFIELD_OP + + Opcodes that have an associated namespace node (AML_NSNODE flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + AML_INT_NAMEDFIELD_OP + AML_INT_METHODCALL_OP + AML_INT_NAMEPATH_OP + + Opcodes that define named ACPI objects (AML_NAMED flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_INT_NAMEDFIELD_OP + + Opcodes that contain executable AML as part of the definition that + must be deferred until needed + + AML_METHOD_OP + AML_VAR_PACKAGE_OP + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + AML_REGION_OP + AML_BUFFER_OP + + Field opcodes + + AML_CREATE_FIELD_OP + AML_FIELD_OP + AML_INDEX_FIELD_OP + AML_BANK_FIELD_OP + + Field "Create" opcodes + + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + + ******************************************************************************/ +/* + * Master Opcode information table. A summary of everything we know about each + * opcode, all in one place. + */ +const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = { +/*! [Begin] no source code translation */ +/* Index Name Parser Args Interpreter Args ObjectType Class Type Flags */ + +/* 00 */ ACPI_OP("Zero", ARGP_ZERO_OP, ARGI_ZERO_OP, ACPI_TYPE_INTEGER, + AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), +/* 01 */ ACPI_OP("One", ARGP_ONE_OP, ARGI_ONE_OP, ACPI_TYPE_INTEGER, + AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), +/* 02 */ ACPI_OP("Alias", ARGP_ALIAS_OP, ARGI_ALIAS_OP, + ACPI_TYPE_LOCAL_ALIAS, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 03 */ ACPI_OP("Name", ARGP_NAME_OP, ARGI_NAME_OP, ACPI_TYPE_ANY, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 04 */ ACPI_OP("ByteConst", ARGP_BYTE_OP, ARGI_BYTE_OP, + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), +/* 05 */ ACPI_OP("WordConst", ARGP_WORD_OP, ARGI_WORD_OP, + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), +/* 06 */ ACPI_OP("DwordConst", ARGP_DWORD_OP, ARGI_DWORD_OP, + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), +/* 07 */ ACPI_OP("String", ARGP_STRING_OP, ARGI_STRING_OP, + ACPI_TYPE_STRING, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), +/* 08 */ ACPI_OP("Scope", ARGP_SCOPE_OP, ARGI_SCOPE_OP, + ACPI_TYPE_LOCAL_SCOPE, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 09 */ ACPI_OP("Buffer", ARGP_BUFFER_OP, ARGI_BUFFER_OP, + ACPI_TYPE_BUFFER, AML_CLASS_CREATE, + AML_TYPE_CREATE_OBJECT, + AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), +/* 0A */ ACPI_OP("Package", ARGP_PACKAGE_OP, ARGI_PACKAGE_OP, + ACPI_TYPE_PACKAGE, AML_CLASS_CREATE, + AML_TYPE_CREATE_OBJECT, + AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), +/* 0B */ ACPI_OP("Method", ARGP_METHOD_OP, ARGI_METHOD_OP, + ACPI_TYPE_METHOD, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_COMPLEX, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED | AML_DEFER), +/* 0C */ ACPI_OP("Local0", ARGP_LOCAL0, ARGI_LOCAL0, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 0D */ ACPI_OP("Local1", ARGP_LOCAL1, ARGI_LOCAL1, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 0E */ ACPI_OP("Local2", ARGP_LOCAL2, ARGI_LOCAL2, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 0F */ ACPI_OP("Local3", ARGP_LOCAL3, ARGI_LOCAL3, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 10 */ ACPI_OP("Local4", ARGP_LOCAL4, ARGI_LOCAL4, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 11 */ ACPI_OP("Local5", ARGP_LOCAL5, ARGI_LOCAL5, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 12 */ ACPI_OP("Local6", ARGP_LOCAL6, ARGI_LOCAL6, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 13 */ ACPI_OP("Local7", ARGP_LOCAL7, ARGI_LOCAL7, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LOCAL_VARIABLE, 0), +/* 14 */ ACPI_OP("Arg0", ARGP_ARG0, ARGI_ARG0, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), +/* 15 */ ACPI_OP("Arg1", ARGP_ARG1, ARGI_ARG1, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), +/* 16 */ ACPI_OP("Arg2", ARGP_ARG2, ARGI_ARG2, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), +/* 17 */ ACPI_OP("Arg3", ARGP_ARG3, ARGI_ARG3, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), +/* 18 */ ACPI_OP("Arg4", ARGP_ARG4, ARGI_ARG4, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), +/* 19 */ ACPI_OP("Arg5", ARGP_ARG5, ARGI_ARG5, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), +/* 1A */ ACPI_OP("Arg6", ARGP_ARG6, ARGI_ARG6, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_METHOD_ARGUMENT, 0), +/* 1B */ ACPI_OP("Store", ARGP_STORE_OP, ARGI_STORE_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R), +/* 1C */ ACPI_OP("RefOf", ARGP_REF_OF_OP, ARGI_REF_OF_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R), +/* 1D */ ACPI_OP("Add", ARGP_ADD_OP, ARGI_ADD_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 1E */ ACPI_OP("Concatenate", ARGP_CONCAT_OP, ARGI_CONCAT_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 1F */ ACPI_OP("Subtract", ARGP_SUBTRACT_OP, ARGI_SUBTRACT_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 20 */ ACPI_OP("Increment", ARGP_INCREMENT_OP, ARGI_INCREMENT_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), +/* 21 */ ACPI_OP("Decrement", ARGP_DECREMENT_OP, ARGI_DECREMENT_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), +/* 22 */ ACPI_OP("Multiply", ARGP_MULTIPLY_OP, ARGI_MULTIPLY_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 23 */ ACPI_OP("Divide", ARGP_DIVIDE_OP, ARGI_DIVIDE_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_2T_1R, + AML_FLAGS_EXEC_2A_2T_1R | AML_CONSTANT), +/* 24 */ ACPI_OP("ShiftLeft", ARGP_SHIFT_LEFT_OP, ARGI_SHIFT_LEFT_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 25 */ ACPI_OP("ShiftRight", ARGP_SHIFT_RIGHT_OP, ARGI_SHIFT_RIGHT_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 26 */ ACPI_OP("And", ARGP_BIT_AND_OP, ARGI_BIT_AND_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 27 */ ACPI_OP("NAnd", ARGP_BIT_NAND_OP, ARGI_BIT_NAND_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 28 */ ACPI_OP("Or", ARGP_BIT_OR_OP, ARGI_BIT_OR_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 29 */ ACPI_OP("NOr", ARGP_BIT_NOR_OP, ARGI_BIT_NOR_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 2A */ ACPI_OP("XOr", ARGP_BIT_XOR_OP, ARGI_BIT_XOR_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 2B */ ACPI_OP("Not", ARGP_BIT_NOT_OP, ARGI_BIT_NOT_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 2C */ ACPI_OP("FindSetLeftBit", ARGP_FIND_SET_LEFT_BIT_OP, + ARGI_FIND_SET_LEFT_BIT_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 2D */ ACPI_OP("FindSetRightBit", ARGP_FIND_SET_RIGHT_BIT_OP, + ARGI_FIND_SET_RIGHT_BIT_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 2E */ ACPI_OP("DerefOf", ARGP_DEREF_OF_OP, ARGI_DEREF_OF_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R), +/* 2F */ ACPI_OP("Notify", ARGP_NOTIFY_OP, ARGI_NOTIFY_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_0R, AML_FLAGS_EXEC_2A_0T_0R), +/* 30 */ ACPI_OP("SizeOf", ARGP_SIZE_OF_OP, ARGI_SIZE_OF_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), +/* 31 */ ACPI_OP("Index", ARGP_INDEX_OP, ARGI_INDEX_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R), +/* 32 */ ACPI_OP("Match", ARGP_MATCH_OP, ARGI_MATCH_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R, + AML_FLAGS_EXEC_6A_0T_1R | AML_CONSTANT), +/* 33 */ ACPI_OP("CreateDWordField", ARGP_CREATE_DWORD_FIELD_OP, + ARGI_CREATE_DWORD_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), +/* 34 */ ACPI_OP("CreateWordField", ARGP_CREATE_WORD_FIELD_OP, + ARGI_CREATE_WORD_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), +/* 35 */ ACPI_OP("CreateByteField", ARGP_CREATE_BYTE_FIELD_OP, + ARGI_CREATE_BYTE_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), +/* 36 */ ACPI_OP("CreateBitField", ARGP_CREATE_BIT_FIELD_OP, + ARGI_CREATE_BIT_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), +/* 37 */ ACPI_OP("ObjectType", ARGP_TYPE_OP, ARGI_TYPE_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), +/* 38 */ ACPI_OP("LAnd", ARGP_LAND_OP, ARGI_LAND_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | + AML_CONSTANT), +/* 39 */ ACPI_OP("LOr", ARGP_LOR_OP, ARGI_LOR_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | + AML_CONSTANT), +/* 3A */ ACPI_OP("LNot", ARGP_LNOT_OP, ARGI_LNOT_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, + AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), +/* 3B */ ACPI_OP("LEqual", ARGP_LEQUAL_OP, ARGI_LEQUAL_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), +/* 3C */ ACPI_OP("LGreater", ARGP_LGREATER_OP, ARGI_LGREATER_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), +/* 3D */ ACPI_OP("LLess", ARGP_LLESS_OP, ARGI_LLESS_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), +/* 3E */ ACPI_OP("If", ARGP_IF_OP, ARGI_IF_OP, ACPI_TYPE_ANY, + AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 3F */ ACPI_OP("Else", ARGP_ELSE_OP, ARGI_ELSE_OP, ACPI_TYPE_ANY, + AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 40 */ ACPI_OP("While", ARGP_WHILE_OP, ARGI_WHILE_OP, ACPI_TYPE_ANY, + AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 41 */ ACPI_OP("Noop", ARGP_NOOP_OP, ARGI_NOOP_OP, ACPI_TYPE_ANY, + AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 42 */ ACPI_OP("Return", ARGP_RETURN_OP, ARGI_RETURN_OP, + ACPI_TYPE_ANY, AML_CLASS_CONTROL, + AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 43 */ ACPI_OP("Break", ARGP_BREAK_OP, ARGI_BREAK_OP, ACPI_TYPE_ANY, + AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 44 */ ACPI_OP("BreakPoint", ARGP_BREAK_POINT_OP, ARGI_BREAK_POINT_OP, + ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 45 */ ACPI_OP("Ones", ARGP_ONES_OP, ARGI_ONES_OP, ACPI_TYPE_INTEGER, + AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), + +/* Prefixed opcodes (Two-byte opcodes with a prefix op) */ + +/* 46 */ ACPI_OP("Mutex", ARGP_MUTEX_OP, ARGI_MUTEX_OP, ACPI_TYPE_MUTEX, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 47 */ ACPI_OP("Event", ARGP_EVENT_OP, ARGI_EVENT_OP, ACPI_TYPE_EVENT, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, + AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 48 */ ACPI_OP("CondRefOf", ARGP_COND_REF_OF_OP, ARGI_COND_REF_OF_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), +/* 49 */ ACPI_OP("CreateField", ARGP_CREATE_FIELD_OP, + ARGI_CREATE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, + AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_FIELD | AML_CREATE), +/* 4A */ ACPI_OP("Load", ARGP_LOAD_OP, ARGI_LOAD_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_0R, + AML_FLAGS_EXEC_1A_1T_0R), +/* 4B */ ACPI_OP("Stall", ARGP_STALL_OP, ARGI_STALL_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, + AML_FLAGS_EXEC_1A_0T_0R), +/* 4C */ ACPI_OP("Sleep", ARGP_SLEEP_OP, ARGI_SLEEP_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, + AML_FLAGS_EXEC_1A_0T_0R), +/* 4D */ ACPI_OP("Acquire", ARGP_ACQUIRE_OP, ARGI_ACQUIRE_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R), +/* 4E */ ACPI_OP("Signal", ARGP_SIGNAL_OP, ARGI_SIGNAL_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 4F */ ACPI_OP("Wait", ARGP_WAIT_OP, ARGI_WAIT_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, + AML_FLAGS_EXEC_2A_0T_1R), +/* 50 */ ACPI_OP("Reset", ARGP_RESET_OP, ARGI_RESET_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, + AML_FLAGS_EXEC_1A_0T_0R), +/* 51 */ ACPI_OP("Release", ARGP_RELEASE_OP, ARGI_RELEASE_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 52 */ ACPI_OP("FromBCD", ARGP_FROM_BCD_OP, ARGI_FROM_BCD_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 53 */ ACPI_OP("ToBCD", ARGP_TO_BCD_OP, ARGI_TO_BCD_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 54 */ ACPI_OP("Unload", ARGP_UNLOAD_OP, ARGI_UNLOAD_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 55 */ ACPI_OP("Revision", ARGP_REVISION_OP, ARGI_REVISION_OP, + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_CONSTANT, 0), +/* 56 */ ACPI_OP("Debug", ARGP_DEBUG_OP, ARGI_DEBUG_OP, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_CONSTANT, 0), +/* 57 */ ACPI_OP("Fatal", ARGP_FATAL_OP, ARGI_FATAL_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_0T_0R, + AML_FLAGS_EXEC_3A_0T_0R), +/* 58 */ ACPI_OP("OperationRegion", ARGP_REGION_OP, ARGI_REGION_OP, + ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_COMPLEX, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED | AML_DEFER), +/* 59 */ ACPI_OP("Field", ARGP_FIELD_OP, ARGI_FIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_FIELD), +/* 5A */ ACPI_OP("Device", ARGP_DEVICE_OP, ARGI_DEVICE_OP, + ACPI_TYPE_DEVICE, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 5B */ ACPI_OP("Processor", ARGP_PROCESSOR_OP, ARGI_PROCESSOR_OP, + ACPI_TYPE_PROCESSOR, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 5C */ ACPI_OP("PowerResource", ARGP_POWER_RES_OP, ARGI_POWER_RES_OP, + ACPI_TYPE_POWER, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_SIMPLE, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 5D */ ACPI_OP("ThermalZone", ARGP_THERMAL_ZONE_OP, + ARGI_THERMAL_ZONE_OP, ACPI_TYPE_THERMAL, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED), +/* 5E */ ACPI_OP("IndexField", ARGP_INDEX_FIELD_OP, ARGI_INDEX_FIELD_OP, + ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_FIELD), +/* 5F */ ACPI_OP("BankField", ARGP_BANK_FIELD_OP, ARGI_BANK_FIELD_OP, + ACPI_TYPE_LOCAL_BANK_FIELD, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_FIELD | AML_DEFER), + +/* Internal opcodes that map to invalid AML opcodes */ + +/* 60 */ ACPI_OP("LNotEqual", ARGP_LNOTEQUAL_OP, ARGI_LNOTEQUAL_OP, + ACPI_TYPE_ANY, AML_CLASS_INTERNAL, + AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), +/* 61 */ ACPI_OP("LLessEqual", ARGP_LLESSEQUAL_OP, ARGI_LLESSEQUAL_OP, + ACPI_TYPE_ANY, AML_CLASS_INTERNAL, + AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), +/* 62 */ ACPI_OP("LGreaterEqual", ARGP_LGREATEREQUAL_OP, + ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, + AML_HAS_ARGS | AML_CONSTANT), +/* 63 */ ACPI_OP("-NamePath-", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP, + ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE), +/* 64 */ ACPI_OP("-MethodCall-", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP, + ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL, + AML_TYPE_METHOD_CALL, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE), +/* 65 */ ACPI_OP("-ByteList-", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP, + ACPI_TYPE_ANY, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, 0), +/* 66 */ ACPI_OP("-ReservedField-", ARGP_RESERVEDFIELD_OP, + ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 67 */ ACPI_OP("-NamedField-", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP, + ACPI_TYPE_ANY, AML_CLASS_INTERNAL, + AML_TYPE_BOGUS, + AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 68 */ ACPI_OP("-AccessField-", ARGP_ACCESSFIELD_OP, + ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 69 */ ACPI_OP("-StaticString", ARGP_STATICSTRING_OP, + ARGI_STATICSTRING_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 6A */ ACPI_OP("-Return Value-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, + AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN, + AML_HAS_ARGS | AML_HAS_RETVAL), +/* 6B */ ACPI_OP("-UNKNOWN_OP-", ARG_NONE, ARG_NONE, ACPI_TYPE_INVALID, + AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 6C */ ACPI_OP("-ASCII_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, + AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 6D */ ACPI_OP("-PREFIX_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, + AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS), + +/* ACPI 2.0 opcodes */ + +/* 6E */ ACPI_OP("QwordConst", ARGP_QWORD_OP, ARGI_QWORD_OP, + ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, + AML_TYPE_LITERAL, AML_CONSTANT), + /* 6F */ ACPI_OP("Package", /* Var */ ARGP_VAR_PACKAGE_OP, + ARGI_VAR_PACKAGE_OP, ACPI_TYPE_PACKAGE, + AML_CLASS_CREATE, AML_TYPE_CREATE_OBJECT, + AML_HAS_ARGS | AML_DEFER), +/* 70 */ ACPI_OP("ConcatenateResTemplate", ARGP_CONCAT_RES_OP, + ARGI_CONCAT_RES_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 71 */ ACPI_OP("Mod", ARGP_MOD_OP, ARGI_MOD_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 72 */ ACPI_OP("CreateQWordField", ARGP_CREATE_QWORD_FIELD_OP, + ARGI_CREATE_QWORD_FIELD_OP, + ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, + AML_TYPE_CREATE_FIELD, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | + AML_DEFER | AML_CREATE), +/* 73 */ ACPI_OP("ToBuffer", ARGP_TO_BUFFER_OP, ARGI_TO_BUFFER_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 74 */ ACPI_OP("ToDecimalString", ARGP_TO_DEC_STR_OP, + ARGI_TO_DEC_STR_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 75 */ ACPI_OP("ToHexString", ARGP_TO_HEX_STR_OP, ARGI_TO_HEX_STR_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 76 */ ACPI_OP("ToInteger", ARGP_TO_INTEGER_OP, ARGI_TO_INTEGER_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, + AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 77 */ ACPI_OP("ToString", ARGP_TO_STRING_OP, ARGI_TO_STRING_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_2A_1T_1R, + AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 78 */ ACPI_OP("CopyObject", ARGP_COPY_OP, ARGI_COPY_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), +/* 79 */ ACPI_OP("Mid", ARGP_MID_OP, ARGI_MID_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R, + AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT), +/* 7A */ ACPI_OP("Continue", ARGP_CONTINUE_OP, ARGI_CONTINUE_OP, + ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 7B */ ACPI_OP("LoadTable", ARGP_LOAD_TABLE_OP, ARGI_LOAD_TABLE_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, + AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R), +/* 7C */ ACPI_OP("DataTableRegion", ARGP_DATA_REGION_OP, + ARGI_DATA_REGION_OP, ACPI_TYPE_REGION, + AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE | AML_NAMED | AML_DEFER), +/* 7D */ ACPI_OP("[EvalSubTree]", ARGP_SCOPE_OP, ARGI_SCOPE_OP, + ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, + AML_TYPE_NAMED_NO_OBJ, + AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | + AML_NSNODE), + +/* ACPI 3.0 opcodes */ + +/* 7E */ ACPI_OP("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY, + AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R, + AML_FLAGS_EXEC_0A_0T_1R), + +/* ACPI 5.0 opcodes */ + +/* 7F */ ACPI_OP("-ConnectField-", ARGP_CONNECTFIELD_OP, + ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 80 */ ACPI_OP("-ExtAccessField-", ARGP_CONNECTFIELD_OP, + ARGI_CONNECTFIELD_OP, ACPI_TYPE_ANY, + AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), + +/* ACPI 6.0 opcodes */ + + /* 81 */ ACPI_OP("External", ARGP_EXTERNAL_OP, ARGI_EXTERNAL_OP, + ACPI_TYPE_ANY, AML_CLASS_EXECUTE, /* ? */ + AML_TYPE_EXEC_3A_0T_0R, AML_FLAGS_EXEC_3A_0T_0R) + +/*! [End] no source code translation !*/ +}; diff --git a/kernel/drivers/acpi/acpica/psopinfo.c b/kernel/drivers/acpi/acpica/psopinfo.c new file mode 100644 index 000000000..20e1a3516 --- /dev/null +++ b/kernel/drivers/acpi/acpica/psopinfo.c @@ -0,0 +1,270 @@ +/****************************************************************************** + * + * Module Name: psopinfo - AML opcode information functions and dispatch tables + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acopcode.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psopinfo") + +extern const u8 acpi_gbl_short_op_index[]; +extern const u8 acpi_gbl_long_op_index[]; + +static const u8 acpi_gbl_argument_count[] = + { 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 6 }; + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_opcode_info + * + * PARAMETERS: opcode - The AML opcode + * + * RETURN: A pointer to the info about the opcode. + * + * DESCRIPTION: Find AML opcode description based on the opcode. + * NOTE: This procedure must ALWAYS return a valid pointer! + * + ******************************************************************************/ + +const struct acpi_opcode_info *acpi_ps_get_opcode_info(u16 opcode) +{ +#ifdef ACPI_DEBUG_OUTPUT + const char *opcode_name = "Unknown AML opcode"; +#endif + + ACPI_FUNCTION_NAME(ps_get_opcode_info); + + /* + * Detect normal 8-bit opcode or extended 16-bit opcode + */ + if (!(opcode & 0xFF00)) { + + /* Simple (8-bit) opcode: 0-255, can't index beyond table */ + + return (&acpi_gbl_aml_op_info + [acpi_gbl_short_op_index[(u8)opcode]]); + } + + if (((opcode & 0xFF00) == AML_EXTENDED_OPCODE) && + (((u8)opcode) <= MAX_EXTENDED_OPCODE)) { + + /* Valid extended (16-bit) opcode */ + + return (&acpi_gbl_aml_op_info + [acpi_gbl_long_op_index[(u8)opcode]]); + } +#if defined ACPI_ASL_COMPILER && defined ACPI_DEBUG_OUTPUT +#include "asldefine.h" + + switch (opcode) { + case AML_RAW_DATA_BYTE: + opcode_name = "-Raw Data Byte-"; + break; + + case AML_RAW_DATA_WORD: + opcode_name = "-Raw Data Word-"; + break; + + case AML_RAW_DATA_DWORD: + opcode_name = "-Raw Data Dword-"; + break; + + case AML_RAW_DATA_QWORD: + opcode_name = "-Raw Data Qword-"; + break; + + case AML_RAW_DATA_BUFFER: + opcode_name = "-Raw Data Buffer-"; + break; + + case AML_RAW_DATA_CHAIN: + opcode_name = "-Raw Data Buffer Chain-"; + break; + + case AML_PACKAGE_LENGTH: + opcode_name = "-Package Length-"; + break; + + case AML_UNASSIGNED_OPCODE: + opcode_name = "-Unassigned Opcode-"; + break; + + case AML_DEFAULT_ARG_OP: + opcode_name = "-Default Arg-"; + break; + + default: + break; + } +#endif + + /* Unknown AML opcode */ + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%s [%4.4X]\n", opcode_name, opcode)); + + return (&acpi_gbl_aml_op_info[_UNK]); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_opcode_name + * + * PARAMETERS: opcode - The AML opcode + * + * RETURN: A pointer to the name of the opcode (ASCII String) + * Note: Never returns NULL. + * + * DESCRIPTION: Translate an opcode into a human-readable string + * + ******************************************************************************/ + +char *acpi_ps_get_opcode_name(u16 opcode) +{ +#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT) + + const struct acpi_opcode_info *op; + + op = acpi_ps_get_opcode_info(opcode); + + /* Always guaranteed to return a valid pointer */ + + return (op->name); + +#else + return ("OpcodeName unavailable"); + +#endif +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_argument_count + * + * PARAMETERS: op_type - Type associated with the AML opcode + * + * RETURN: Argument count + * + * DESCRIPTION: Obtain the number of expected arguments for an AML opcode + * + ******************************************************************************/ + +u8 acpi_ps_get_argument_count(u32 op_type) +{ + + if (op_type <= AML_TYPE_EXEC_6A_0T_1R) { + return (acpi_gbl_argument_count[op_type]); + } + + return (0); +} + +/* + * This table is directly indexed by the opcodes It returns + * an index into the opcode table (acpi_gbl_aml_op_info) + */ +const u8 acpi_gbl_short_op_index[256] = { +/* 0 1 2 3 4 5 6 7 */ +/* 8 9 A B C D E F */ +/* 0x00 */ 0x00, 0x01, _UNK, _UNK, _UNK, _UNK, 0x02, _UNK, +/* 0x08 */ 0x03, _UNK, 0x04, 0x05, 0x06, 0x07, 0x6E, _UNK, +/* 0x10 */ 0x08, 0x09, 0x0a, 0x6F, 0x0b, 0x81, _UNK, _UNK, +/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x28 */ _UNK, _UNK, _UNK, _UNK, _UNK, 0x63, _PFX, _PFX, +/* 0x30 */ 0x67, 0x66, 0x68, 0x65, 0x69, 0x64, 0x6A, 0x7D, +/* 0x38 */ 0x7F, 0x80, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x40 */ _UNK, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, +/* 0x48 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, +/* 0x50 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, +/* 0x58 */ _ASC, _ASC, _ASC, _UNK, _PFX, _UNK, _PFX, _ASC, +/* 0x60 */ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, +/* 0x68 */ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, _UNK, +/* 0x70 */ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, +/* 0x78 */ 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, +/* 0x80 */ 0x2b, 0x2c, 0x2d, 0x2e, 0x70, 0x71, 0x2f, 0x30, +/* 0x88 */ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x72, +/* 0x90 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x73, 0x74, +/* 0x98 */ 0x75, 0x76, _UNK, _UNK, 0x77, 0x78, 0x79, 0x7A, +/* 0xA0 */ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x60, 0x61, +/* 0xA8 */ 0x62, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xB0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xB8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xC0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xC8 */ _UNK, _UNK, _UNK, _UNK, 0x44, _UNK, _UNK, _UNK, +/* 0xD0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xD8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xE0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xE8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xF0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xF8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x45, +}; + +/* + * This table is indexed by the second opcode of the extended opcode + * pair. It returns an index into the opcode table (acpi_gbl_aml_op_info) + */ +const u8 acpi_gbl_long_op_index[NUM_EXTENDED_OPCODE] = { +/* 0 1 2 3 4 5 6 7 */ +/* 8 9 A B C D E F */ +/* 0x00 */ _UNK, 0x46, 0x47, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x08 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x10 */ _UNK, _UNK, 0x48, 0x49, _UNK, _UNK, _UNK, _UNK, +/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x7B, +/* 0x20 */ 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, +/* 0x28 */ 0x52, 0x53, 0x54, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x30 */ 0x55, 0x56, 0x57, 0x7e, _UNK, _UNK, _UNK, _UNK, +/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x40 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x48 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x50 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x58 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x60 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x68 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x70 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x78 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x80 */ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, +/* 0x88 */ 0x7C, +}; diff --git a/kernel/drivers/acpi/acpica/psparse.c b/kernel/drivers/acpi/acpica/psparse.c new file mode 100644 index 000000000..a555f7f7b --- /dev/null +++ b/kernel/drivers/acpi/acpica/psparse.c @@ -0,0 +1,688 @@ +/****************************************************************************** + * + * Module Name: psparse - Parser top level AML parse routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Parse the AML and build an operation tree as most interpreters, + * like Perl, do. Parsing is done by hand rather than with a YACC + * generated parser to tightly constrain stack and dynamic memory + * usage. At the same time, parsing is kept flexible and the code + * fairly compact by parsing based on a list of AML opcode + * templates in aml_op_info[] + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "amlcode.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psparse") + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_opcode_size + * + * PARAMETERS: opcode - An AML opcode + * + * RETURN: Size of the opcode, in bytes (1 or 2) + * + * DESCRIPTION: Get the size of the current opcode. + * + ******************************************************************************/ +u32 acpi_ps_get_opcode_size(u32 opcode) +{ + + /* Extended (2-byte) opcode if > 255 */ + + if (opcode > 0x00FF) { + return (2); + } + + /* Otherwise, just a single byte opcode */ + + return (1); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_peek_opcode + * + * PARAMETERS: parser_state - A parser state object + * + * RETURN: Next AML opcode + * + * DESCRIPTION: Get next AML opcode (without incrementing AML pointer) + * + ******************************************************************************/ + +u16 acpi_ps_peek_opcode(struct acpi_parse_state * parser_state) +{ + u8 *aml; + u16 opcode; + + aml = parser_state->aml; + opcode = (u16) ACPI_GET8(aml); + + if (opcode == AML_EXTENDED_OP_PREFIX) { + + /* Extended opcode, get the second opcode byte */ + + aml++; + opcode = (u16) ((opcode << 8) | ACPI_GET8(aml)); + } + + return (opcode); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_complete_this_op + * + * PARAMETERS: walk_state - Current State + * op - Op to complete + * + * RETURN: Status + * + * DESCRIPTION: Perform any cleanup at the completion of an Op. + * + ******************************************************************************/ + +acpi_status +acpi_ps_complete_this_op(struct acpi_walk_state * walk_state, + union acpi_parse_object * op) +{ + union acpi_parse_object *prev; + union acpi_parse_object *next; + const struct acpi_opcode_info *parent_info; + union acpi_parse_object *replacement_op = NULL; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_PTR(ps_complete_this_op, op); + + /* Check for null Op, can happen if AML code is corrupt */ + + if (!op) { + return_ACPI_STATUS(AE_OK); /* OK for now */ + } + + /* Delete this op and the subtree below it if asked to */ + + if (((walk_state->parse_flags & ACPI_PARSE_TREE_MASK) != + ACPI_PARSE_DELETE_TREE) + || (walk_state->op_info->class == AML_CLASS_ARGUMENT)) { + return_ACPI_STATUS(AE_OK); + } + + /* Make sure that we only delete this subtree */ + + if (op->common.parent) { + prev = op->common.parent->common.value.arg; + if (!prev) { + + /* Nothing more to do */ + + goto cleanup; + } + + /* + * Check if we need to replace the operator and its subtree + * with a return value op (placeholder op) + */ + parent_info = + acpi_ps_get_opcode_info(op->common.parent->common. + aml_opcode); + + switch (parent_info->class) { + case AML_CLASS_CONTROL: + + break; + + case AML_CLASS_CREATE: + /* + * These opcodes contain term_arg operands. The current + * op must be replaced by a placeholder return op + */ + replacement_op = + acpi_ps_alloc_op(AML_INT_RETURN_VALUE_OP); + if (!replacement_op) { + status = AE_NO_MEMORY; + } + break; + + case AML_CLASS_NAMED_OBJECT: + /* + * These opcodes contain term_arg operands. The current + * op must be replaced by a placeholder return op + */ + if ((op->common.parent->common.aml_opcode == + AML_REGION_OP) + || (op->common.parent->common.aml_opcode == + AML_DATA_REGION_OP) + || (op->common.parent->common.aml_opcode == + AML_BUFFER_OP) + || (op->common.parent->common.aml_opcode == + AML_PACKAGE_OP) + || (op->common.parent->common.aml_opcode == + AML_BANK_FIELD_OP) + || (op->common.parent->common.aml_opcode == + AML_VAR_PACKAGE_OP)) { + replacement_op = + acpi_ps_alloc_op(AML_INT_RETURN_VALUE_OP); + if (!replacement_op) { + status = AE_NO_MEMORY; + } + } else + if ((op->common.parent->common.aml_opcode == + AML_NAME_OP) + && (walk_state->pass_number <= + ACPI_IMODE_LOAD_PASS2)) { + if ((op->common.aml_opcode == AML_BUFFER_OP) + || (op->common.aml_opcode == AML_PACKAGE_OP) + || (op->common.aml_opcode == + AML_VAR_PACKAGE_OP)) { + replacement_op = + acpi_ps_alloc_op(op->common. + aml_opcode); + if (!replacement_op) { + status = AE_NO_MEMORY; + } else { + replacement_op->named.data = + op->named.data; + replacement_op->named.length = + op->named.length; + } + } + } + break; + + default: + + replacement_op = + acpi_ps_alloc_op(AML_INT_RETURN_VALUE_OP); + if (!replacement_op) { + status = AE_NO_MEMORY; + } + } + + /* We must unlink this op from the parent tree */ + + if (prev == op) { + + /* This op is the first in the list */ + + if (replacement_op) { + replacement_op->common.parent = + op->common.parent; + replacement_op->common.value.arg = NULL; + replacement_op->common.node = op->common.node; + op->common.parent->common.value.arg = + replacement_op; + replacement_op->common.next = op->common.next; + } else { + op->common.parent->common.value.arg = + op->common.next; + } + } + + /* Search the parent list */ + + else + while (prev) { + + /* Traverse all siblings in the parent's argument list */ + + next = prev->common.next; + if (next == op) { + if (replacement_op) { + replacement_op->common.parent = + op->common.parent; + replacement_op->common.value. + arg = NULL; + replacement_op->common.node = + op->common.node; + prev->common.next = + replacement_op; + replacement_op->common.next = + op->common.next; + next = NULL; + } else { + prev->common.next = + op->common.next; + next = NULL; + } + } + prev = next; + } + } + +cleanup: + + /* Now we can actually delete the subtree rooted at Op */ + + acpi_ps_delete_parse_tree(op); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_next_parse_state + * + * PARAMETERS: walk_state - Current state + * op - Current parse op + * callback_status - Status from previous operation + * + * RETURN: Status + * + * DESCRIPTION: Update the parser state based upon the return exception from + * the parser callback. + * + ******************************************************************************/ + +acpi_status +acpi_ps_next_parse_state(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + acpi_status callback_status) +{ + struct acpi_parse_state *parser_state = &walk_state->parser_state; + acpi_status status = AE_CTRL_PENDING; + + ACPI_FUNCTION_TRACE_PTR(ps_next_parse_state, op); + + switch (callback_status) { + case AE_CTRL_TERMINATE: + /* + * A control method was terminated via a RETURN statement. + * The walk of this method is complete. + */ + parser_state->aml = parser_state->aml_end; + status = AE_CTRL_TERMINATE; + break; + + case AE_CTRL_BREAK: + + parser_state->aml = walk_state->aml_last_while; + walk_state->control_state->common.value = FALSE; + status = AE_CTRL_BREAK; + break; + + case AE_CTRL_CONTINUE: + + parser_state->aml = walk_state->aml_last_while; + status = AE_CTRL_CONTINUE; + break; + + case AE_CTRL_PENDING: + + parser_state->aml = walk_state->aml_last_while; + break; + +#if 0 + case AE_CTRL_SKIP: + + parser_state->aml = parser_state->scope->parse_scope.pkg_end; + status = AE_OK; + break; +#endif + + case AE_CTRL_TRUE: + /* + * Predicate of an IF was true, and we are at the matching ELSE. + * Just close out this package + */ + parser_state->aml = acpi_ps_get_next_package_end(parser_state); + status = AE_CTRL_PENDING; + break; + + case AE_CTRL_FALSE: + /* + * Either an IF/WHILE Predicate was false or we encountered a BREAK + * opcode. In both cases, we do not execute the rest of the + * package; We simply close out the parent (finishing the walk of + * this branch of the tree) and continue execution at the parent + * level. + */ + parser_state->aml = parser_state->scope->parse_scope.pkg_end; + + /* In the case of a BREAK, just force a predicate (if any) to FALSE */ + + walk_state->control_state->common.value = FALSE; + status = AE_CTRL_END; + break; + + case AE_CTRL_TRANSFER: + + /* A method call (invocation) -- transfer control */ + + status = AE_CTRL_TRANSFER; + walk_state->prev_op = op; + walk_state->method_call_op = op; + walk_state->method_call_node = + (op->common.value.arg)->common.node; + + /* Will return value (if any) be used by the caller? */ + + walk_state->return_used = + acpi_ds_is_result_used(op, walk_state); + break; + + default: + + status = callback_status; + if ((callback_status & AE_CODE_MASK) == AE_CODE_CONTROL) { + status = AE_OK; + } + break; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_parse_aml + * + * PARAMETERS: walk_state - Current state + * + * + * RETURN: Status + * + * DESCRIPTION: Parse raw AML and return a tree of ops + * + ******************************************************************************/ + +acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_thread_state *thread; + struct acpi_thread_state *prev_walk_list = acpi_gbl_current_walk_list; + struct acpi_walk_state *previous_walk_state; + + ACPI_FUNCTION_TRACE(ps_parse_aml); + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Entered with WalkState=%p Aml=%p size=%X\n", + walk_state, walk_state->parser_state.aml, + walk_state->parser_state.aml_size)); + + if (!walk_state->parser_state.aml) { + return_ACPI_STATUS(AE_NULL_OBJECT); + } + + /* Create and initialize a new thread state */ + + thread = acpi_ut_create_thread_state(); + if (!thread) { + if (walk_state->method_desc) { + + /* Executing a control method - additional cleanup */ + + acpi_ds_terminate_control_method(walk_state-> + method_desc, + walk_state); + } + + acpi_ds_delete_walk_state(walk_state); + return_ACPI_STATUS(AE_NO_MEMORY); + } + + walk_state->thread = thread; + + /* + * If executing a method, the starting sync_level is this method's + * sync_level + */ + if (walk_state->method_desc) { + walk_state->thread->current_sync_level = + walk_state->method_desc->method.sync_level; + } + + acpi_ds_push_walk_state(walk_state, thread); + + /* + * This global allows the AML debugger to get a handle to the currently + * executing control method. + */ + acpi_gbl_current_walk_list = thread; + + /* + * Execute the walk loop as long as there is a valid Walk State. This + * handles nested control method invocations without recursion. + */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "State=%p\n", walk_state)); + + status = AE_OK; + while (walk_state) { + if (ACPI_SUCCESS(status)) { + /* + * The parse_loop executes AML until the method terminates + * or calls another method. + */ + status = acpi_ps_parse_loop(walk_state); + } + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Completed one call to walk loop, %s State=%p\n", + acpi_format_exception(status), walk_state)); + + if (status == AE_CTRL_TRANSFER) { + /* + * A method call was detected. + * Transfer control to the called control method + */ + status = + acpi_ds_call_control_method(thread, walk_state, + NULL); + if (ACPI_FAILURE(status)) { + status = + acpi_ds_method_error(status, walk_state); + } + + /* + * If the transfer to the new method method call worked, a new walk + * state was created -- get it + */ + walk_state = acpi_ds_get_current_walk_state(thread); + continue; + } else if (status == AE_CTRL_TERMINATE) { + status = AE_OK; + } else if ((status != AE_OK) && (walk_state->method_desc)) { + + /* Either the method parse or actual execution failed */ + + ACPI_ERROR_METHOD("Method parse/execution failed", + walk_state->method_node, NULL, + status); + + /* Check for possible multi-thread reentrancy problem */ + + if ((status == AE_ALREADY_EXISTS) && + (!(walk_state->method_desc->method. + info_flags & ACPI_METHOD_SERIALIZED))) { + /* + * Method is not serialized and tried to create an object + * twice. The probable cause is that the method cannot + * handle reentrancy. Mark as "pending serialized" now, and + * then mark "serialized" when the last thread exits. + */ + walk_state->method_desc->method.info_flags |= + ACPI_METHOD_SERIALIZED_PENDING; + } + } + + /* We are done with this walk, move on to the parent if any */ + + walk_state = acpi_ds_pop_walk_state(thread); + + /* Reset the current scope to the beginning of scope stack */ + + acpi_ds_scope_stack_clear(walk_state); + + /* + * If we just returned from the execution of a control method or if we + * encountered an error during the method parse phase, there's lots of + * cleanup to do + */ + if (((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == + ACPI_PARSE_EXECUTE) || (ACPI_FAILURE(status))) { + acpi_ds_terminate_control_method(walk_state-> + method_desc, + walk_state); + } + + /* Delete this walk state and all linked control states */ + + acpi_ps_cleanup_scope(&walk_state->parser_state); + previous_walk_state = walk_state; + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "ReturnValue=%p, ImplicitValue=%p State=%p\n", + walk_state->return_desc, + walk_state->implicit_return_obj, walk_state)); + + /* Check if we have restarted a preempted walk */ + + walk_state = acpi_ds_get_current_walk_state(thread); + if (walk_state) { + if (ACPI_SUCCESS(status)) { + /* + * There is another walk state, restart it. + * If the method return value is not used by the parent, + * The object is deleted + */ + if (!previous_walk_state->return_desc) { + /* + * In slack mode execution, if there is no return value + * we should implicitly return zero (0) as a default value. + */ + if (acpi_gbl_enable_interpreter_slack && + !previous_walk_state-> + implicit_return_obj) { + previous_walk_state-> + implicit_return_obj = + acpi_ut_create_integer_object + ((u64) 0); + if (!previous_walk_state-> + implicit_return_obj) { + return_ACPI_STATUS + (AE_NO_MEMORY); + } + } + + /* Restart the calling control method */ + + status = + acpi_ds_restart_control_method + (walk_state, + previous_walk_state-> + implicit_return_obj); + } else { + /* + * We have a valid return value, delete any implicit + * return value. + */ + acpi_ds_clear_implicit_return + (previous_walk_state); + + status = + acpi_ds_restart_control_method + (walk_state, + previous_walk_state->return_desc); + } + if (ACPI_SUCCESS(status)) { + walk_state->walk_type |= + ACPI_WALK_METHOD_RESTART; + } + } else { + /* On error, delete any return object or implicit return */ + + acpi_ut_remove_reference(previous_walk_state-> + return_desc); + acpi_ds_clear_implicit_return + (previous_walk_state); + } + } + + /* + * Just completed a 1st-level method, save the final internal return + * value (if any) + */ + else if (previous_walk_state->caller_return_desc) { + if (previous_walk_state->implicit_return_obj) { + *(previous_walk_state->caller_return_desc) = + previous_walk_state->implicit_return_obj; + } else { + /* NULL if no return value */ + + *(previous_walk_state->caller_return_desc) = + previous_walk_state->return_desc; + } + } else { + if (previous_walk_state->return_desc) { + + /* Caller doesn't want it, must delete it */ + + acpi_ut_remove_reference(previous_walk_state-> + return_desc); + } + if (previous_walk_state->implicit_return_obj) { + + /* Caller doesn't want it, must delete it */ + + acpi_ut_remove_reference(previous_walk_state-> + implicit_return_obj); + } + } + + acpi_ds_delete_walk_state(previous_walk_state); + } + + /* Normal exit */ + + acpi_ex_release_all_mutexes(thread); + acpi_ut_delete_generic_state(ACPI_CAST_PTR + (union acpi_generic_state, thread)); + acpi_gbl_current_walk_list = prev_walk_list; + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/psscope.c b/kernel/drivers/acpi/acpica/psscope.c new file mode 100644 index 000000000..9d669cc6c --- /dev/null +++ b/kernel/drivers/acpi/acpica/psscope.c @@ -0,0 +1,265 @@ +/****************************************************************************** + * + * Module Name: psscope - Parser scope stack management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psscope") + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_parent_scope + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Pointer to an Op object + * + * DESCRIPTION: Get parent of current op being parsed + * + ******************************************************************************/ +union acpi_parse_object *acpi_ps_get_parent_scope(struct acpi_parse_state + *parser_state) +{ + + return (parser_state->scope->parse_scope.op); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_has_completed_scope + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Boolean, TRUE = scope completed. + * + * DESCRIPTION: Is parsing of current argument complete? Determined by + * 1) AML pointer is at or beyond the end of the scope + * 2) The scope argument count has reached zero. + * + ******************************************************************************/ + +u8 acpi_ps_has_completed_scope(struct acpi_parse_state * parser_state) +{ + + return ((u8) + ((parser_state->aml >= parser_state->scope->parse_scope.arg_end + || !parser_state->scope->parse_scope.arg_count))); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_init_scope + * + * PARAMETERS: parser_state - Current parser state object + * root - the Root Node of this new scope + * + * RETURN: Status + * + * DESCRIPTION: Allocate and init a new scope object + * + ******************************************************************************/ + +acpi_status +acpi_ps_init_scope(struct acpi_parse_state * parser_state, + union acpi_parse_object * root_op) +{ + union acpi_generic_state *scope; + + ACPI_FUNCTION_TRACE_PTR(ps_init_scope, root_op); + + scope = acpi_ut_create_generic_state(); + if (!scope) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + scope->common.descriptor_type = ACPI_DESC_TYPE_STATE_RPSCOPE; + scope->parse_scope.op = root_op; + scope->parse_scope.arg_count = ACPI_VAR_ARGS; + scope->parse_scope.arg_end = parser_state->aml_end; + scope->parse_scope.pkg_end = parser_state->aml_end; + + parser_state->scope = scope; + parser_state->start_op = root_op; + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_push_scope + * + * PARAMETERS: parser_state - Current parser state object + * op - Current op to be pushed + * remaining_args - List of args remaining + * arg_count - Fixed or variable number of args + * + * RETURN: Status + * + * DESCRIPTION: Push current op to begin parsing its argument + * + ******************************************************************************/ + +acpi_status +acpi_ps_push_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object *op, + u32 remaining_args, u32 arg_count) +{ + union acpi_generic_state *scope; + + ACPI_FUNCTION_TRACE_PTR(ps_push_scope, op); + + scope = acpi_ut_create_generic_state(); + if (!scope) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + scope->common.descriptor_type = ACPI_DESC_TYPE_STATE_PSCOPE; + scope->parse_scope.op = op; + scope->parse_scope.arg_list = remaining_args; + scope->parse_scope.arg_count = arg_count; + scope->parse_scope.pkg_end = parser_state->pkg_end; + + /* Push onto scope stack */ + + acpi_ut_push_generic_state(&parser_state->scope, scope); + + if (arg_count == ACPI_VAR_ARGS) { + + /* Multiple arguments */ + + scope->parse_scope.arg_end = parser_state->pkg_end; + } else { + /* Single argument */ + + scope->parse_scope.arg_end = ACPI_TO_POINTER(ACPI_MAX_PTR); + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_pop_scope + * + * PARAMETERS: parser_state - Current parser state object + * op - Where the popped op is returned + * arg_list - Where the popped "next argument" is + * returned + * arg_count - Count of objects in arg_list + * + * RETURN: Status + * + * DESCRIPTION: Return to parsing a previous op + * + ******************************************************************************/ + +void +acpi_ps_pop_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object **op, u32 * arg_list, u32 * arg_count) +{ + union acpi_generic_state *scope = parser_state->scope; + + ACPI_FUNCTION_TRACE(ps_pop_scope); + + /* Only pop the scope if there is in fact a next scope */ + + if (scope->common.next) { + scope = acpi_ut_pop_generic_state(&parser_state->scope); + + /* Return to parsing previous op */ + + *op = scope->parse_scope.op; + *arg_list = scope->parse_scope.arg_list; + *arg_count = scope->parse_scope.arg_count; + parser_state->pkg_end = scope->parse_scope.pkg_end; + + /* All done with this scope state structure */ + + acpi_ut_delete_generic_state(scope); + } else { + /* Empty parse stack, prepare to fetch next opcode */ + + *op = NULL; + *arg_list = 0; + *arg_count = 0; + } + + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Popped Op %p Args %X\n", *op, *arg_count)); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_cleanup_scope + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: None + * + * DESCRIPTION: Destroy available list, remaining stack levels, and return + * root scope + * + ******************************************************************************/ + +void acpi_ps_cleanup_scope(struct acpi_parse_state *parser_state) +{ + union acpi_generic_state *scope; + + ACPI_FUNCTION_TRACE_PTR(ps_cleanup_scope, parser_state); + + if (!parser_state) { + return_VOID; + } + + /* Delete anything on the scope stack */ + + while (parser_state->scope) { + scope = acpi_ut_pop_generic_state(&parser_state->scope); + acpi_ut_delete_generic_state(scope); + } + + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/pstree.c b/kernel/drivers/acpi/acpica/pstree.c new file mode 100644 index 000000000..89984f30a --- /dev/null +++ b/kernel/drivers/acpi/acpica/pstree.c @@ -0,0 +1,320 @@ +/****************************************************************************** + * + * Module Name: pstree - Parser op tree manipulation/traversal/search + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("pstree") + +/* Local prototypes */ +#ifdef ACPI_OBSOLETE_FUNCTIONS +union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op); +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_arg + * + * PARAMETERS: op - Get an argument for this op + * argn - Nth argument to get + * + * RETURN: The argument (as an Op object). NULL if argument does not exist + * + * DESCRIPTION: Get the specified op's argument. + * + ******************************************************************************/ + +union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn) +{ + union acpi_parse_object *arg = NULL; + const struct acpi_opcode_info *op_info; + + ACPI_FUNCTION_ENTRY(); + +/* + if (Op->Common.aml_opcode == AML_INT_CONNECTION_OP) + { + return (Op->Common.Value.Arg); + } +*/ + /* Get the info structure for this opcode */ + + op_info = acpi_ps_get_opcode_info(op->common.aml_opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + + /* Invalid opcode or ASCII character */ + + return (NULL); + } + + /* Check if this opcode requires argument sub-objects */ + + if (!(op_info->flags & AML_HAS_ARGS)) { + + /* Has no linked argument objects */ + + return (NULL); + } + + /* Get the requested argument object */ + + arg = op->common.value.arg; + while (arg && argn) { + argn--; + arg = arg->common.next; + } + + return (arg); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_append_arg + * + * PARAMETERS: op - Append an argument to this Op. + * arg - Argument Op to append + * + * RETURN: None. + * + * DESCRIPTION: Append an argument to an op's argument list (a NULL arg is OK) + * + ******************************************************************************/ + +void +acpi_ps_append_arg(union acpi_parse_object *op, union acpi_parse_object *arg) +{ + union acpi_parse_object *prev_arg; + const struct acpi_opcode_info *op_info; + + ACPI_FUNCTION_ENTRY(); + + if (!op) { + return; + } + + /* Get the info structure for this opcode */ + + op_info = acpi_ps_get_opcode_info(op->common.aml_opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + + /* Invalid opcode */ + + ACPI_ERROR((AE_INFO, "Invalid AML Opcode: 0x%2.2X", + op->common.aml_opcode)); + return; + } + + /* Check if this opcode requires argument sub-objects */ + + if (!(op_info->flags & AML_HAS_ARGS)) { + + /* Has no linked argument objects */ + + return; + } + + /* Append the argument to the linked argument list */ + + if (op->common.value.arg) { + + /* Append to existing argument list */ + + prev_arg = op->common.value.arg; + while (prev_arg->common.next) { + prev_arg = prev_arg->common.next; + } + prev_arg->common.next = arg; + } else { + /* No argument list, this will be the first argument */ + + op->common.value.arg = arg; + } + + /* Set the parent in this arg and any args linked after it */ + + while (arg) { + arg->common.parent = op; + arg = arg->common.next; + + op->common.arg_list_length++; + } +} + +#ifdef ACPI_FUTURE_USAGE +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_depth_next + * + * PARAMETERS: origin - Root of subtree to search + * op - Last (previous) Op that was found + * + * RETURN: Next Op found in the search. + * + * DESCRIPTION: Get next op in tree (walking the tree in depth-first order) + * Return NULL when reaching "origin" or when walking up from root + * + ******************************************************************************/ + +union acpi_parse_object *acpi_ps_get_depth_next(union acpi_parse_object *origin, + union acpi_parse_object *op) +{ + union acpi_parse_object *next = NULL; + union acpi_parse_object *parent; + union acpi_parse_object *arg; + + ACPI_FUNCTION_ENTRY(); + + if (!op) { + return (NULL); + } + + /* Look for an argument or child */ + + next = acpi_ps_get_arg(op, 0); + if (next) { + return (next); + } + + /* Look for a sibling */ + + next = op->common.next; + if (next) { + return (next); + } + + /* Look for a sibling of parent */ + + parent = op->common.parent; + + while (parent) { + arg = acpi_ps_get_arg(parent, 0); + while (arg && (arg != origin) && (arg != op)) { + arg = arg->common.next; + } + + if (arg == origin) { + + /* Reached parent of origin, end search */ + + return (NULL); + } + + if (parent->common.next) { + + /* Found sibling of parent */ + + return (parent->common.next); + } + + op = parent; + parent = parent->common.parent; + } + + return (next); +} + +#ifdef ACPI_OBSOLETE_FUNCTIONS +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_child + * + * PARAMETERS: op - Get the child of this Op + * + * RETURN: Child Op, Null if none is found. + * + * DESCRIPTION: Get op's children or NULL if none + * + ******************************************************************************/ + +union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op) +{ + union acpi_parse_object *child = NULL; + + ACPI_FUNCTION_ENTRY(); + + switch (op->common.aml_opcode) { + case AML_SCOPE_OP: + case AML_ELSE_OP: + case AML_DEVICE_OP: + case AML_THERMAL_ZONE_OP: + case AML_INT_METHODCALL_OP: + + child = acpi_ps_get_arg(op, 0); + break; + + case AML_BUFFER_OP: + case AML_PACKAGE_OP: + case AML_METHOD_OP: + case AML_IF_OP: + case AML_WHILE_OP: + case AML_FIELD_OP: + + child = acpi_ps_get_arg(op, 1); + break; + + case AML_POWER_RES_OP: + case AML_INDEX_FIELD_OP: + + child = acpi_ps_get_arg(op, 2); + break; + + case AML_PROCESSOR_OP: + case AML_BANK_FIELD_OP: + + child = acpi_ps_get_arg(op, 3); + break; + + default: + + /* All others have no children */ + + break; + } + + return (child); +} +#endif +#endif /* ACPI_FUTURE_USAGE */ diff --git a/kernel/drivers/acpi/acpica/psutils.c b/kernel/drivers/acpi/acpica/psutils.c new file mode 100644 index 000000000..960505ab4 --- /dev/null +++ b/kernel/drivers/acpi/acpica/psutils.c @@ -0,0 +1,236 @@ +/****************************************************************************** + * + * Module Name: psutils - Parser miscellaneous utilities (Parser only) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psutils") + +/******************************************************************************* + * + * FUNCTION: acpi_ps_create_scope_op + * + * PARAMETERS: None + * + * RETURN: A new Scope object, null on failure + * + * DESCRIPTION: Create a Scope and associated namepath op with the root name + * + ******************************************************************************/ +union acpi_parse_object *acpi_ps_create_scope_op(void) +{ + union acpi_parse_object *scope_op; + + scope_op = acpi_ps_alloc_op(AML_SCOPE_OP); + if (!scope_op) { + return (NULL); + } + + scope_op->named.name = ACPI_ROOT_NAME; + return (scope_op); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_init_op + * + * PARAMETERS: op - A newly allocated Op object + * opcode - Opcode to store in the Op + * + * RETURN: None + * + * DESCRIPTION: Initialize a parse (Op) object + * + ******************************************************************************/ + +void acpi_ps_init_op(union acpi_parse_object *op, u16 opcode) +{ + ACPI_FUNCTION_ENTRY(); + + op->common.descriptor_type = ACPI_DESC_TYPE_PARSER; + op->common.aml_opcode = opcode; + + ACPI_DISASM_ONLY_MEMBERS(ACPI_STRNCPY(op->common.aml_op_name, + (acpi_ps_get_opcode_info + (opcode))->name, + sizeof(op->common.aml_op_name))); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_alloc_op + * + * PARAMETERS: opcode - Opcode that will be stored in the new Op + * + * RETURN: Pointer to the new Op, null on failure + * + * DESCRIPTION: Allocate an acpi_op, choose op type (and thus size) based on + * opcode. A cache of opcodes is available for the pure + * GENERIC_OP, since this is by far the most commonly used. + * + ******************************************************************************/ + +union acpi_parse_object *acpi_ps_alloc_op(u16 opcode) +{ + union acpi_parse_object *op; + const struct acpi_opcode_info *op_info; + u8 flags = ACPI_PARSEOP_GENERIC; + + ACPI_FUNCTION_ENTRY(); + + op_info = acpi_ps_get_opcode_info(opcode); + + /* Determine type of parse_op required */ + + if (op_info->flags & AML_DEFER) { + flags = ACPI_PARSEOP_DEFERRED; + } else if (op_info->flags & AML_NAMED) { + flags = ACPI_PARSEOP_NAMED; + } else if (opcode == AML_INT_BYTELIST_OP) { + flags = ACPI_PARSEOP_BYTELIST; + } + + /* Allocate the minimum required size object */ + + if (flags == ACPI_PARSEOP_GENERIC) { + + /* The generic op (default) is by far the most common (16 to 1) */ + + op = acpi_os_acquire_object(acpi_gbl_ps_node_cache); + } else { + /* Extended parseop */ + + op = acpi_os_acquire_object(acpi_gbl_ps_node_ext_cache); + } + + /* Initialize the Op */ + + if (op) { + acpi_ps_init_op(op, opcode); + op->common.flags = flags; + } + + return (op); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_free_op + * + * PARAMETERS: op - Op to be freed + * + * RETURN: None. + * + * DESCRIPTION: Free an Op object. Either put it on the GENERIC_OP cache list + * or actually free it. + * + ******************************************************************************/ + +void acpi_ps_free_op(union acpi_parse_object *op) +{ + ACPI_FUNCTION_NAME(ps_free_op); + + if (op->common.aml_opcode == AML_INT_RETURN_VALUE_OP) { + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Free retval op: %p\n", + op)); + } + + if (op->common.flags & ACPI_PARSEOP_GENERIC) { + (void)acpi_os_release_object(acpi_gbl_ps_node_cache, op); + } else { + (void)acpi_os_release_object(acpi_gbl_ps_node_ext_cache, op); + } +} + +/******************************************************************************* + * + * FUNCTION: Utility functions + * + * DESCRIPTION: Low level character and object functions + * + ******************************************************************************/ + +/* + * Is "c" a namestring lead character? + */ +u8 acpi_ps_is_leading_char(u32 c) +{ + return ((u8) (c == '_' || (c >= 'A' && c <= 'Z'))); +} + +/* + * Get op's name (4-byte name segment) or 0 if unnamed + */ +#ifdef ACPI_FUTURE_USAGE +u32 acpi_ps_get_name(union acpi_parse_object * op) +{ + + /* The "generic" object has no name associated with it */ + + if (op->common.flags & ACPI_PARSEOP_GENERIC) { + return (0); + } + + /* Only the "Extended" parse objects have a name */ + + return (op->named.name); +} +#endif /* ACPI_FUTURE_USAGE */ + +/* + * Set op's name + */ +void acpi_ps_set_name(union acpi_parse_object *op, u32 name) +{ + + /* The "generic" object has no name associated with it */ + + if (op->common.flags & ACPI_PARSEOP_GENERIC) { + return; + } + + op->named.name = name; +} diff --git a/kernel/drivers/acpi/acpica/pswalk.c b/kernel/drivers/acpi/acpica/pswalk.c new file mode 100644 index 000000000..ba5f69171 --- /dev/null +++ b/kernel/drivers/acpi/acpica/pswalk.c @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * Module Name: pswalk - Parser routines to walk parsed op tree(s) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("pswalk") + +/******************************************************************************* + * + * FUNCTION: acpi_ps_delete_parse_tree + * + * PARAMETERS: subtree_root - Root of tree (or subtree) to delete + * + * RETURN: None + * + * DESCRIPTION: Delete a portion of or an entire parse tree. + * + ******************************************************************************/ +void acpi_ps_delete_parse_tree(union acpi_parse_object *subtree_root) +{ + union acpi_parse_object *op = subtree_root; + union acpi_parse_object *next = NULL; + union acpi_parse_object *parent = NULL; + + ACPI_FUNCTION_TRACE_PTR(ps_delete_parse_tree, subtree_root); + + /* Visit all nodes in the subtree */ + + while (op) { + + /* Check if we are not ascending */ + + if (op != parent) { + + /* Look for an argument or child of the current op */ + + next = acpi_ps_get_arg(op, 0); + if (next) { + + /* Still going downward in tree (Op is not completed yet) */ + + op = next; + continue; + } + } + + /* No more children, this Op is complete. */ + + next = op->common.next; + parent = op->common.parent; + + acpi_ps_free_op(op); + + /* If we are back to the starting point, the walk is complete. */ + + if (op == subtree_root) { + return_VOID; + } + if (next) { + op = next; + } else { + op = parent; + } + } + + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/psxface.c b/kernel/drivers/acpi/acpica/psxface.c new file mode 100644 index 000000000..841a5ea06 --- /dev/null +++ b/kernel/drivers/acpi/acpica/psxface.c @@ -0,0 +1,390 @@ +/****************************************************************************** + * + * Module Name: psxface - Parser external interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "acinterp.h" +#include "actables.h" + +#define _COMPONENT ACPI_PARSER +ACPI_MODULE_NAME("psxface") + +/* Local Prototypes */ +static void acpi_ps_start_trace(struct acpi_evaluate_info *info); + +static void acpi_ps_stop_trace(struct acpi_evaluate_info *info); + +static void +acpi_ps_update_parameter_list(struct acpi_evaluate_info *info, u16 action); + +/******************************************************************************* + * + * FUNCTION: acpi_debug_trace + * + * PARAMETERS: method_name - Valid ACPI name string + * debug_level - Optional level mask. 0 to use default + * debug_layer - Optional layer mask. 0 to use default + * flags - bit 1: one shot(1) or persistent(0) + * + * RETURN: Status + * + * DESCRIPTION: External interface to enable debug tracing during control + * method execution + * + ******************************************************************************/ + +acpi_status +acpi_debug_trace(char *name, u32 debug_level, u32 debug_layer, u32 flags) +{ + acpi_status status; + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* TBDs: Validate name, allow full path or just nameseg */ + + acpi_gbl_trace_method_name = *ACPI_CAST_PTR(u32, name); + acpi_gbl_trace_flags = flags; + + if (debug_level) { + acpi_gbl_trace_dbg_level = debug_level; + } + if (debug_layer) { + acpi_gbl_trace_dbg_layer = debug_layer; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_start_trace + * + * PARAMETERS: info - Method info struct + * + * RETURN: None + * + * DESCRIPTION: Start control method execution trace + * + ******************************************************************************/ + +static void acpi_ps_start_trace(struct acpi_evaluate_info *info) +{ + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return; + } + + if ((!acpi_gbl_trace_method_name) || + (acpi_gbl_trace_method_name != info->node->name.integer)) { + goto exit; + } + + acpi_gbl_original_dbg_level = acpi_dbg_level; + acpi_gbl_original_dbg_layer = acpi_dbg_layer; + + acpi_dbg_level = 0x00FFFFFF; + acpi_dbg_layer = ACPI_UINT32_MAX; + + if (acpi_gbl_trace_dbg_level) { + acpi_dbg_level = acpi_gbl_trace_dbg_level; + } + if (acpi_gbl_trace_dbg_layer) { + acpi_dbg_layer = acpi_gbl_trace_dbg_layer; + } + +exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_stop_trace + * + * PARAMETERS: info - Method info struct + * + * RETURN: None + * + * DESCRIPTION: Stop control method execution trace + * + ******************************************************************************/ + +static void acpi_ps_stop_trace(struct acpi_evaluate_info *info) +{ + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return; + } + + if ((!acpi_gbl_trace_method_name) || + (acpi_gbl_trace_method_name != info->node->name.integer)) { + goto exit; + } + + /* Disable further tracing if type is one-shot */ + + if (acpi_gbl_trace_flags & 1) { + acpi_gbl_trace_method_name = 0; + acpi_gbl_trace_dbg_level = 0; + acpi_gbl_trace_dbg_layer = 0; + } + + acpi_dbg_level = acpi_gbl_original_dbg_level; + acpi_dbg_layer = acpi_gbl_original_dbg_layer; + +exit: + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_execute_method + * + * PARAMETERS: info - Method info block, contains: + * node - Method Node to execute + * obj_desc - Method object + * parameters - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * parameter_type - Type of Parameter list + * return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * pass_number - Parse or execute pass + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method + * + ******************************************************************************/ + +acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) +{ + acpi_status status; + union acpi_parse_object *op; + struct acpi_walk_state *walk_state; + + ACPI_FUNCTION_TRACE(ps_execute_method); + + /* Quick validation of DSDT header */ + + acpi_tb_check_dsdt_header(); + + /* Validate the Info and method Node */ + + if (!info || !info->node) { + return_ACPI_STATUS(AE_NULL_ENTRY); + } + + /* Init for new method, wait on concurrency semaphore */ + + status = + acpi_ds_begin_method_execution(info->node, info->obj_desc, NULL); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * The caller "owns" the parameters, so give each one an extra reference + */ + acpi_ps_update_parameter_list(info, REF_INCREMENT); + + /* Begin tracing if requested */ + + acpi_ps_start_trace(info); + + /* + * Execute the method. Performs parse simultaneously + */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "**** Begin Method Parse/Execute [%4.4s] **** Node=%p Obj=%p\n", + info->node->name.ascii, info->node, info->obj_desc)); + + /* Create and init a Root Node */ + + op = acpi_ps_create_scope_op(); + if (!op) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Create and initialize a new walk state */ + + info->pass_number = ACPI_IMODE_EXECUTE; + walk_state = + acpi_ds_create_walk_state(info->obj_desc->method.owner_id, NULL, + NULL, NULL); + if (!walk_state) { + status = AE_NO_MEMORY; + goto cleanup; + } + + status = acpi_ds_init_aml_walk(walk_state, op, info->node, + info->obj_desc->method.aml_start, + info->obj_desc->method.aml_length, info, + info->pass_number); + if (ACPI_FAILURE(status)) { + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + + if (info->obj_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) { + walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL; + } + + /* Invoke an internal method if necessary */ + + if (info->obj_desc->method.info_flags & ACPI_METHOD_INTERNAL_ONLY) { + status = + info->obj_desc->method.dispatch.implementation(walk_state); + info->return_object = walk_state->return_desc; + + /* Cleanup states */ + + acpi_ds_scope_stack_clear(walk_state); + acpi_ps_cleanup_scope(&walk_state->parser_state); + acpi_ds_terminate_control_method(walk_state->method_desc, + walk_state); + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + + /* + * Start method evaluation with an implicit return of zero. + * This is done for Windows compatibility. + */ + if (acpi_gbl_enable_interpreter_slack) { + walk_state->implicit_return_obj = + acpi_ut_create_integer_object((u64) 0); + if (!walk_state->implicit_return_obj) { + status = AE_NO_MEMORY; + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + } + + /* Parse the AML */ + + status = acpi_ps_parse_aml(walk_state); + + /* walk_state was deleted by parse_aml */ + +cleanup: + acpi_ps_delete_parse_tree(op); + + /* End optional tracing */ + + acpi_ps_stop_trace(info); + + /* Take away the extra reference that we gave the parameters above */ + + acpi_ps_update_parameter_list(info, REF_DECREMENT); + + /* Exit now if error above */ + + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * If the method has returned an object, signal this to the caller with + * a control exception code + */ + if (info->return_object) { + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "Method returned ObjDesc=%p\n", + info->return_object)); + ACPI_DUMP_STACK_ENTRY(info->return_object); + + status = AE_CTRL_RETURN_VALUE; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ps_update_parameter_list + * + * PARAMETERS: info - See struct acpi_evaluate_info + * (Used: parameter_type and Parameters) + * action - Add or Remove reference + * + * RETURN: Status + * + * DESCRIPTION: Update reference count on all method parameter objects + * + ******************************************************************************/ + +static void +acpi_ps_update_parameter_list(struct acpi_evaluate_info *info, u16 action) +{ + u32 i; + + if (info->parameters) { + + /* Update reference count for each parameter */ + + for (i = 0; info->parameters[i]; i++) { + + /* Ignore errors, just do them all */ + + (void)acpi_ut_update_object_reference(info-> + parameters[i], + action); + } + } +} diff --git a/kernel/drivers/acpi/acpica/rsaddr.c b/kernel/drivers/acpi/acpica/rsaddr.c new file mode 100644 index 000000000..66d406e8f --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsaddr.c @@ -0,0 +1,382 @@ +/******************************************************************************* + * + * Module Name: rsaddr - Address resource descriptors (16/32/64) + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsaddr") + +/******************************************************************************* + * + * acpi_rs_convert_address16 - All WORD (16-bit) address resources + * + ******************************************************************************/ +struct acpi_rsconvert_info acpi_rs_convert_address16[5] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_ADDRESS16, + ACPI_RS_SIZE(struct acpi_resource_address16), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_address16)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_ADDRESS16, + sizeof(struct aml_resource_address16), + 0}, + + /* Resource Type, General Flags, and Type-Specific Flags */ + + {ACPI_RSC_ADDRESS, 0, 0, 0}, + + /* + * These fields are contiguous in both the source and destination: + * Address Granularity + * Address Range Minimum + * Address Range Maximum + * Address Translation Offset + * Address Length + */ + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.address16.address.granularity), + AML_OFFSET(address16.granularity), + 5}, + + /* Optional resource_source (Index and String) */ + + {ACPI_RSC_SOURCE, ACPI_RS_OFFSET(data.address16.resource_source), + 0, + sizeof(struct aml_resource_address16)} +}; + +/******************************************************************************* + * + * acpi_rs_convert_address32 - All DWORD (32-bit) address resources + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_address32[5] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_ADDRESS32, + ACPI_RS_SIZE(struct acpi_resource_address32), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_address32)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_ADDRESS32, + sizeof(struct aml_resource_address32), + 0}, + + /* Resource Type, General Flags, and Type-Specific Flags */ + + {ACPI_RSC_ADDRESS, 0, 0, 0}, + + /* + * These fields are contiguous in both the source and destination: + * Address Granularity + * Address Range Minimum + * Address Range Maximum + * Address Translation Offset + * Address Length + */ + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.address32.address.granularity), + AML_OFFSET(address32.granularity), + 5}, + + /* Optional resource_source (Index and String) */ + + {ACPI_RSC_SOURCE, ACPI_RS_OFFSET(data.address32.resource_source), + 0, + sizeof(struct aml_resource_address32)} +}; + +/******************************************************************************* + * + * acpi_rs_convert_address64 - All QWORD (64-bit) address resources + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_address64[5] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_ADDRESS64, + ACPI_RS_SIZE(struct acpi_resource_address64), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_address64)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_ADDRESS64, + sizeof(struct aml_resource_address64), + 0}, + + /* Resource Type, General Flags, and Type-Specific Flags */ + + {ACPI_RSC_ADDRESS, 0, 0, 0}, + + /* + * These fields are contiguous in both the source and destination: + * Address Granularity + * Address Range Minimum + * Address Range Maximum + * Address Translation Offset + * Address Length + */ + {ACPI_RSC_MOVE64, ACPI_RS_OFFSET(data.address64.address.granularity), + AML_OFFSET(address64.granularity), + 5}, + + /* Optional resource_source (Index and String) */ + + {ACPI_RSC_SOURCE, ACPI_RS_OFFSET(data.address64.resource_source), + 0, + sizeof(struct aml_resource_address64)} +}; + +/******************************************************************************* + * + * acpi_rs_convert_ext_address64 - All Extended (64-bit) address resources + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_ext_address64[5] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64, + ACPI_RS_SIZE(struct acpi_resource_extended_address64), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_ext_address64)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64, + sizeof(struct aml_resource_extended_address64), + 0}, + + /* Resource Type, General Flags, and Type-Specific Flags */ + + {ACPI_RSC_ADDRESS, 0, 0, 0}, + + /* Revision ID */ + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.ext_address64.revision_ID), + AML_OFFSET(ext_address64.revision_ID), + 1}, + /* + * These fields are contiguous in both the source and destination: + * Address Granularity + * Address Range Minimum + * Address Range Maximum + * Address Translation Offset + * Address Length + * Type-Specific Attribute + */ + {ACPI_RSC_MOVE64, + ACPI_RS_OFFSET(data.ext_address64.address.granularity), + AML_OFFSET(ext_address64.granularity), + 6} +}; + +/******************************************************************************* + * + * acpi_rs_convert_general_flags - Flags common to all address descriptors + * + ******************************************************************************/ + +static struct acpi_rsconvert_info acpi_rs_convert_general_flags[6] = { + {ACPI_RSC_FLAGINIT, 0, AML_OFFSET(address.flags), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_general_flags)}, + + /* Resource Type (Memory, Io, bus_number, etc.) */ + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.address.resource_type), + AML_OFFSET(address.resource_type), + 1}, + + /* General flags - Consume, Decode, min_fixed, max_fixed */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.producer_consumer), + AML_OFFSET(address.flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.decode), + AML_OFFSET(address.flags), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.min_address_fixed), + AML_OFFSET(address.flags), + 2}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.max_address_fixed), + AML_OFFSET(address.flags), + 3} +}; + +/******************************************************************************* + * + * acpi_rs_convert_mem_flags - Flags common to Memory address descriptors + * + ******************************************************************************/ + +static struct acpi_rsconvert_info acpi_rs_convert_mem_flags[5] = { + {ACPI_RSC_FLAGINIT, 0, AML_OFFSET(address.specific_flags), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_mem_flags)}, + + /* Memory-specific flags */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.write_protect), + AML_OFFSET(address.specific_flags), + 0}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.caching), + AML_OFFSET(address.specific_flags), + 1}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.range_type), + AML_OFFSET(address.specific_flags), + 3}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.info.mem.translation), + AML_OFFSET(address.specific_flags), + 5} +}; + +/******************************************************************************* + * + * acpi_rs_convert_io_flags - Flags common to I/O address descriptors + * + ******************************************************************************/ + +static struct acpi_rsconvert_info acpi_rs_convert_io_flags[4] = { + {ACPI_RSC_FLAGINIT, 0, AML_OFFSET(address.specific_flags), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_io_flags)}, + + /* I/O-specific flags */ + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.address.info.io.range_type), + AML_OFFSET(address.specific_flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.address.info.io.translation), + AML_OFFSET(address.specific_flags), + 4}, + + {ACPI_RSC_1BITFLAG, + ACPI_RS_OFFSET(data.address.info.io.translation_type), + AML_OFFSET(address.specific_flags), + 5} +}; + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_address_common + * + * PARAMETERS: resource - Pointer to the internal resource struct + * aml - Pointer to the AML resource descriptor + * + * RETURN: TRUE if the resource_type field is OK, FALSE otherwise + * + * DESCRIPTION: Convert common flag fields from a raw AML resource descriptor + * to an internal resource descriptor + * + ******************************************************************************/ + +u8 +acpi_rs_get_address_common(struct acpi_resource *resource, + union aml_resource *aml) +{ + ACPI_FUNCTION_ENTRY(); + + /* Validate the Resource Type */ + + if ((aml->address.resource_type > 2) + && (aml->address.resource_type < 0xC0)) { + return (FALSE); + } + + /* Get the Resource Type and General Flags */ + + (void)acpi_rs_convert_aml_to_resource(resource, aml, + acpi_rs_convert_general_flags); + + /* Get the Type-Specific Flags (Memory and I/O descriptors only) */ + + if (resource->data.address.resource_type == ACPI_MEMORY_RANGE) { + (void)acpi_rs_convert_aml_to_resource(resource, aml, + acpi_rs_convert_mem_flags); + } else if (resource->data.address.resource_type == ACPI_IO_RANGE) { + (void)acpi_rs_convert_aml_to_resource(resource, aml, + acpi_rs_convert_io_flags); + } else { + /* Generic resource type, just grab the type_specific byte */ + + resource->data.address.info.type_specific = + aml->address.specific_flags; + } + + return (TRUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_set_address_common + * + * PARAMETERS: aml - Pointer to the AML resource descriptor + * resource - Pointer to the internal resource struct + * + * RETURN: None + * + * DESCRIPTION: Convert common flag fields from a resource descriptor to an + * AML descriptor + * + ******************************************************************************/ + +void +acpi_rs_set_address_common(union aml_resource *aml, + struct acpi_resource *resource) +{ + ACPI_FUNCTION_ENTRY(); + + /* Set the Resource Type and General Flags */ + + (void)acpi_rs_convert_resource_to_aml(resource, aml, + acpi_rs_convert_general_flags); + + /* Set the Type-Specific Flags (Memory and I/O descriptors only) */ + + if (resource->data.address.resource_type == ACPI_MEMORY_RANGE) { + (void)acpi_rs_convert_resource_to_aml(resource, aml, + acpi_rs_convert_mem_flags); + } else if (resource->data.address.resource_type == ACPI_IO_RANGE) { + (void)acpi_rs_convert_resource_to_aml(resource, aml, + acpi_rs_convert_io_flags); + } else { + /* Generic resource type, just copy the type_specific byte */ + + aml->address.specific_flags = + resource->data.address.info.type_specific; + } +} diff --git a/kernel/drivers/acpi/acpica/rscalc.c b/kernel/drivers/acpi/acpica/rscalc.c new file mode 100644 index 000000000..cb739a694 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rscalc.c @@ -0,0 +1,718 @@ +/******************************************************************************* + * + * Module Name: rscalc - Calculate stream and list lengths + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rscalc") + +/* Local prototypes */ +static u8 acpi_rs_count_set_bits(u16 bit_field); + +static acpi_rs_length +acpi_rs_struct_option_length(struct acpi_resource_source *resource_source); + +static u32 +acpi_rs_stream_option_length(u32 resource_length, u32 minimum_total_length); + +/******************************************************************************* + * + * FUNCTION: acpi_rs_count_set_bits + * + * PARAMETERS: bit_field - Field in which to count bits + * + * RETURN: Number of bits set within the field + * + * DESCRIPTION: Count the number of bits set in a resource field. Used for + * (Short descriptor) interrupt and DMA lists. + * + ******************************************************************************/ + +static u8 acpi_rs_count_set_bits(u16 bit_field) +{ + u8 bits_set; + + ACPI_FUNCTION_ENTRY(); + + for (bits_set = 0; bit_field; bits_set++) { + + /* Zero the least significant bit that is set */ + + bit_field &= (u16) (bit_field - 1); + } + + return (bits_set); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_struct_option_length + * + * PARAMETERS: resource_source - Pointer to optional descriptor field + * + * RETURN: Status + * + * DESCRIPTION: Common code to handle optional resource_source_index and + * resource_source fields in some Large descriptors. Used during + * list-to-stream conversion + * + ******************************************************************************/ + +static acpi_rs_length +acpi_rs_struct_option_length(struct acpi_resource_source *resource_source) +{ + ACPI_FUNCTION_ENTRY(); + + /* + * If the resource_source string is valid, return the size of the string + * (string_length includes the NULL terminator) plus the size of the + * resource_source_index (1). + */ + if (resource_source->string_ptr) { + return ((acpi_rs_length) (resource_source->string_length + 1)); + } + + return (0); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_stream_option_length + * + * PARAMETERS: resource_length - Length from the resource header + * minimum_total_length - Minimum length of this resource, before + * any optional fields. Includes header size + * + * RETURN: Length of optional string (0 if no string present) + * + * DESCRIPTION: Common code to handle optional resource_source_index and + * resource_source fields in some Large descriptors. Used during + * stream-to-list conversion + * + ******************************************************************************/ + +static u32 +acpi_rs_stream_option_length(u32 resource_length, + u32 minimum_aml_resource_length) +{ + u32 string_length = 0; + + ACPI_FUNCTION_ENTRY(); + + /* + * The resource_source_index and resource_source are optional elements of some + * Large-type resource descriptors. + */ + + /* + * If the length of the actual resource descriptor is greater than the ACPI + * spec-defined minimum length, it means that a resource_source_index exists + * and is followed by a (required) null terminated string. The string length + * (including the null terminator) is the resource length minus the minimum + * length, minus one byte for the resource_source_index itself. + */ + if (resource_length > minimum_aml_resource_length) { + + /* Compute the length of the optional string */ + + string_length = + resource_length - minimum_aml_resource_length - 1; + } + + /* + * Round the length up to a multiple of the native word in order to + * guarantee that the entire resource descriptor is native word aligned + */ + return ((u32) ACPI_ROUND_UP_TO_NATIVE_WORD(string_length)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_aml_length + * + * PARAMETERS: resource - Pointer to the resource linked list + * resource_list_size - Size of the resource linked list + * size_needed - Where the required size is returned + * + * RETURN: Status + * + * DESCRIPTION: Takes a linked list of internal resource descriptors and + * calculates the size buffer needed to hold the corresponding + * external resource byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_aml_length(struct acpi_resource *resource, + acpi_size resource_list_size, acpi_size * size_needed) +{ + acpi_size aml_size_needed = 0; + struct acpi_resource *resource_end; + acpi_rs_length total_size; + + ACPI_FUNCTION_TRACE(rs_get_aml_length); + + /* Traverse entire list of internal resource descriptors */ + + resource_end = + ACPI_ADD_PTR(struct acpi_resource, resource, resource_list_size); + while (resource < resource_end) { + + /* Validate the descriptor type */ + + if (resource->type > ACPI_RESOURCE_TYPE_MAX) { + return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE); + } + + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource->length) { + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); + } + + /* Get the base size of the (external stream) resource descriptor */ + + total_size = acpi_gbl_aml_resource_sizes[resource->type]; + + /* + * Augment the base size for descriptors with optional and/or + * variable-length fields + */ + switch (resource->type) { + case ACPI_RESOURCE_TYPE_IRQ: + + /* Length can be 3 or 2 */ + + if (resource->data.irq.descriptor_length == 2) { + total_size--; + } + break; + + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + + /* Length can be 1 or 0 */ + + if (resource->data.irq.descriptor_length == 0) { + total_size--; + } + break; + + case ACPI_RESOURCE_TYPE_VENDOR: + /* + * Vendor Defined Resource: + * For a Vendor Specific resource, if the Length is between 1 and 7 + * it will be created as a Small Resource data type, otherwise it + * is a Large Resource data type. + */ + if (resource->data.vendor.byte_length > 7) { + + /* Base size of a Large resource descriptor */ + + total_size = + sizeof(struct aml_resource_large_header); + } + + /* Add the size of the vendor-specific data */ + + total_size = (acpi_rs_length) + (total_size + resource->data.vendor.byte_length); + break; + + case ACPI_RESOURCE_TYPE_END_TAG: + /* + * End Tag: + * We are done -- return the accumulated total size. + */ + *size_needed = aml_size_needed + total_size; + + /* Normal exit */ + + return_ACPI_STATUS(AE_OK); + + case ACPI_RESOURCE_TYPE_ADDRESS16: + /* + * 16-Bit Address Resource: + * Add the size of the optional resource_source info + */ + total_size = (acpi_rs_length) + (total_size + + acpi_rs_struct_option_length(&resource->data. + address16. + resource_source)); + break; + + case ACPI_RESOURCE_TYPE_ADDRESS32: + /* + * 32-Bit Address Resource: + * Add the size of the optional resource_source info + */ + total_size = (acpi_rs_length) + (total_size + + acpi_rs_struct_option_length(&resource->data. + address32. + resource_source)); + break; + + case ACPI_RESOURCE_TYPE_ADDRESS64: + /* + * 64-Bit Address Resource: + * Add the size of the optional resource_source info + */ + total_size = (acpi_rs_length) + (total_size + + acpi_rs_struct_option_length(&resource->data. + address64. + resource_source)); + break; + + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + /* + * Extended IRQ Resource: + * Add the size of each additional optional interrupt beyond the + * required 1 (4 bytes for each u32 interrupt number) + */ + total_size = (acpi_rs_length) + (total_size + + ((resource->data.extended_irq.interrupt_count - + 1) * 4) + + /* Add the size of the optional resource_source info */ + acpi_rs_struct_option_length(&resource->data. + extended_irq. + resource_source)); + break; + + case ACPI_RESOURCE_TYPE_GPIO: + + total_size = + (acpi_rs_length) (total_size + + (resource->data.gpio. + pin_table_length * 2) + + resource->data.gpio. + resource_source.string_length + + resource->data.gpio. + vendor_length); + + break; + + case ACPI_RESOURCE_TYPE_SERIAL_BUS: + + total_size = + acpi_gbl_aml_resource_serial_bus_sizes[resource-> + data. + common_serial_bus. + type]; + + total_size = (acpi_rs_length) (total_size + + resource->data. + i2c_serial_bus. + resource_source. + string_length + + resource->data. + i2c_serial_bus. + vendor_length); + + break; + + default: + + break; + } + + /* Update the total */ + + aml_size_needed += total_size; + + /* Point to the next object */ + + resource = + ACPI_ADD_PTR(struct acpi_resource, resource, + resource->length); + } + + /* Did not find an end_tag resource descriptor */ + + return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_list_length + * + * PARAMETERS: aml_buffer - Pointer to the resource byte stream + * aml_buffer_length - Size of aml_buffer + * size_needed - Where the size needed is returned + * + * RETURN: Status + * + * DESCRIPTION: Takes an external resource byte stream and calculates the size + * buffer needed to hold the corresponding internal resource + * descriptor linked list. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_list_length(u8 * aml_buffer, + u32 aml_buffer_length, acpi_size * size_needed) +{ + acpi_status status; + u8 *end_aml; + u8 *buffer; + u32 buffer_size; + u16 temp16; + u16 resource_length; + u32 extra_struct_bytes; + u8 resource_index; + u8 minimum_aml_resource_length; + union aml_resource *aml_resource; + + ACPI_FUNCTION_TRACE(rs_get_list_length); + + *size_needed = ACPI_RS_SIZE_MIN; /* Minimum size is one end_tag */ + end_aml = aml_buffer + aml_buffer_length; + + /* Walk the list of AML resource descriptors */ + + while (aml_buffer < end_aml) { + + /* Validate the Resource Type and Resource Length */ + + status = + acpi_ut_validate_resource(NULL, aml_buffer, + &resource_index); + if (ACPI_FAILURE(status)) { + /* + * Exit on failure. Cannot continue because the descriptor length + * may be bogus also. + */ + return_ACPI_STATUS(status); + } + + aml_resource = (void *)aml_buffer; + + /* Get the resource length and base (minimum) AML size */ + + resource_length = acpi_ut_get_resource_length(aml_buffer); + minimum_aml_resource_length = + acpi_gbl_resource_aml_sizes[resource_index]; + + /* + * Augment the size for descriptors with optional + * and/or variable length fields + */ + extra_struct_bytes = 0; + buffer = + aml_buffer + acpi_ut_get_resource_header_length(aml_buffer); + + switch (acpi_ut_get_resource_type(aml_buffer)) { + case ACPI_RESOURCE_NAME_IRQ: + /* + * IRQ Resource: + * Get the number of bits set in the 16-bit IRQ mask + */ + ACPI_MOVE_16_TO_16(&temp16, buffer); + extra_struct_bytes = acpi_rs_count_set_bits(temp16); + break; + + case ACPI_RESOURCE_NAME_DMA: + /* + * DMA Resource: + * Get the number of bits set in the 8-bit DMA mask + */ + extra_struct_bytes = acpi_rs_count_set_bits(*buffer); + break; + + case ACPI_RESOURCE_NAME_VENDOR_SMALL: + case ACPI_RESOURCE_NAME_VENDOR_LARGE: + /* + * Vendor Resource: + * Get the number of vendor data bytes + */ + extra_struct_bytes = resource_length; + + /* + * There is already one byte included in the minimum + * descriptor size. If there are extra struct bytes, + * subtract one from the count. + */ + if (extra_struct_bytes) { + extra_struct_bytes--; + } + break; + + case ACPI_RESOURCE_NAME_END_TAG: + /* + * End Tag: This is the normal exit + */ + return_ACPI_STATUS(AE_OK); + + case ACPI_RESOURCE_NAME_ADDRESS32: + case ACPI_RESOURCE_NAME_ADDRESS16: + case ACPI_RESOURCE_NAME_ADDRESS64: + /* + * Address Resource: + * Add the size of the optional resource_source + */ + extra_struct_bytes = + acpi_rs_stream_option_length(resource_length, + minimum_aml_resource_length); + break; + + case ACPI_RESOURCE_NAME_EXTENDED_IRQ: + /* + * Extended IRQ Resource: + * Using the interrupt_table_length, add 4 bytes for each additional + * interrupt. Note: at least one interrupt is required and is + * included in the minimum descriptor size (reason for the -1) + */ + extra_struct_bytes = (buffer[1] - 1) * sizeof(u32); + + /* Add the size of the optional resource_source */ + + extra_struct_bytes += + acpi_rs_stream_option_length(resource_length - + extra_struct_bytes, + minimum_aml_resource_length); + break; + + case ACPI_RESOURCE_NAME_GPIO: + + /* Vendor data is optional */ + + if (aml_resource->gpio.vendor_length) { + extra_struct_bytes += + aml_resource->gpio.vendor_offset - + aml_resource->gpio.pin_table_offset + + aml_resource->gpio.vendor_length; + } else { + extra_struct_bytes += + aml_resource->large_header.resource_length + + sizeof(struct aml_resource_large_header) - + aml_resource->gpio.pin_table_offset; + } + break; + + case ACPI_RESOURCE_NAME_SERIAL_BUS: + + minimum_aml_resource_length = + acpi_gbl_resource_aml_serial_bus_sizes + [aml_resource->common_serial_bus.type]; + extra_struct_bytes += + aml_resource->common_serial_bus.resource_length - + minimum_aml_resource_length; + break; + + default: + + break; + } + + /* + * Update the required buffer size for the internal descriptor structs + * + * Important: Round the size up for the appropriate alignment. This + * is a requirement on IA64. + */ + if (acpi_ut_get_resource_type(aml_buffer) == + ACPI_RESOURCE_NAME_SERIAL_BUS) { + buffer_size = + acpi_gbl_resource_struct_serial_bus_sizes + [aml_resource->common_serial_bus.type] + + extra_struct_bytes; + } else { + buffer_size = + acpi_gbl_resource_struct_sizes[resource_index] + + extra_struct_bytes; + } + buffer_size = (u32)ACPI_ROUND_UP_TO_NATIVE_WORD(buffer_size); + + *size_needed += buffer_size; + + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, + "Type %.2X, AmlLength %.2X InternalLength %.2X\n", + acpi_ut_get_resource_type(aml_buffer), + acpi_ut_get_descriptor_length(aml_buffer), + buffer_size)); + + /* + * Point to the next resource within the AML stream using the length + * contained in the resource descriptor header + */ + aml_buffer += acpi_ut_get_descriptor_length(aml_buffer); + } + + /* Did not find an end_tag resource descriptor */ + + return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_pci_routing_table_length + * + * PARAMETERS: package_object - Pointer to the package object + * buffer_size_needed - u32 pointer of the size buffer + * needed to properly return the + * parsed data + * + * RETURN: Status + * + * DESCRIPTION: Given a package representing a PCI routing table, this + * calculates the size of the corresponding linked list of + * descriptions. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, + acpi_size * buffer_size_needed) +{ + u32 number_of_elements; + acpi_size temp_size_needed = 0; + union acpi_operand_object **top_object_list; + u32 index; + union acpi_operand_object *package_element; + union acpi_operand_object **sub_object_list; + u8 name_found; + u32 table_index; + + ACPI_FUNCTION_TRACE(rs_get_pci_routing_table_length); + + number_of_elements = package_object->package.count; + + /* + * Calculate the size of the return buffer. + * The base size is the number of elements * the sizes of the + * structures. Additional space for the strings is added below. + * The minus one is to subtract the size of the u8 Source[1] + * member because it is added below. + * + * But each PRT_ENTRY structure has a pointer to a string and + * the size of that string must be found. + */ + top_object_list = package_object->package.elements; + + for (index = 0; index < number_of_elements; index++) { + + /* Dereference the subpackage */ + + package_element = *top_object_list; + + /* We must have a valid Package object */ + + if (!package_element || + (package_element->common.type != ACPI_TYPE_PACKAGE)) { + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); + } + + /* + * The sub_object_list will now point to an array of the + * four IRQ elements: Address, Pin, Source and source_index + */ + sub_object_list = package_element->package.elements; + + /* Scan the irq_table_elements for the Source Name String */ + + name_found = FALSE; + + for (table_index = 0; + table_index < package_element->package.count + && !name_found; table_index++) { + if (*sub_object_list && /* Null object allowed */ + ((ACPI_TYPE_STRING == + (*sub_object_list)->common.type) || + ((ACPI_TYPE_LOCAL_REFERENCE == + (*sub_object_list)->common.type) && + ((*sub_object_list)->reference.class == + ACPI_REFCLASS_NAME)))) { + name_found = TRUE; + } else { + /* Look at the next element */ + + sub_object_list++; + } + } + + temp_size_needed += (sizeof(struct acpi_pci_routing_table) - 4); + + /* Was a String type found? */ + + if (name_found) { + if ((*sub_object_list)->common.type == ACPI_TYPE_STRING) { + /* + * The length String.Length field does not include the + * terminating NULL, add 1 + */ + temp_size_needed += ((acpi_size) + (*sub_object_list)->string. + length + 1); + } else { + temp_size_needed += acpi_ns_get_pathname_length((*sub_object_list)->reference.node); + } + } else { + /* + * If no name was found, then this is a NULL, which is + * translated as a u32 zero. + */ + temp_size_needed += sizeof(u32); + } + + /* Round up the size since each element must be aligned */ + + temp_size_needed = ACPI_ROUND_UP_TO_64BIT(temp_size_needed); + + /* Point to the next union acpi_operand_object */ + + top_object_list++; + } + + /* + * Add an extra element to the end of the list, essentially a + * NULL terminator + */ + *buffer_size_needed = + temp_size_needed + sizeof(struct acpi_pci_routing_table); + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/rscreate.c b/kernel/drivers/acpi/acpica/rscreate.c new file mode 100644 index 000000000..15434e4c9 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rscreate.c @@ -0,0 +1,482 @@ +/******************************************************************************* + * + * Module Name: rscreate - Create resource lists/tables + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rscreate") + +/******************************************************************************* + * + * FUNCTION: acpi_buffer_to_resource + * + * PARAMETERS: aml_buffer - Pointer to the resource byte stream + * aml_buffer_length - Length of the aml_buffer + * resource_ptr - Where the converted resource is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a raw AML buffer to a resource list + * + ******************************************************************************/ +acpi_status +acpi_buffer_to_resource(u8 *aml_buffer, + u16 aml_buffer_length, + struct acpi_resource **resource_ptr) +{ + acpi_status status; + acpi_size list_size_needed; + void *resource; + void *current_resource_ptr; + + ACPI_FUNCTION_TRACE(acpi_buffer_to_resource); + + /* + * Note: we allow AE_AML_NO_RESOURCE_END_TAG, since an end tag + * is not required here. + */ + + /* Get the required length for the converted resource */ + + status = acpi_rs_get_list_length(aml_buffer, aml_buffer_length, + &list_size_needed); + if (status == AE_AML_NO_RESOURCE_END_TAG) { + status = AE_OK; + } + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Allocate a buffer for the converted resource */ + + resource = ACPI_ALLOCATE_ZEROED(list_size_needed); + current_resource_ptr = resource; + if (!resource) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Perform the AML-to-Resource conversion */ + + status = acpi_ut_walk_aml_resources(NULL, aml_buffer, aml_buffer_length, + acpi_rs_convert_aml_to_resources, + ¤t_resource_ptr); + if (status == AE_AML_NO_RESOURCE_END_TAG) { + status = AE_OK; + } + if (ACPI_FAILURE(status)) { + ACPI_FREE(resource); + } else { + *resource_ptr = resource; + } + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_buffer_to_resource) + +/******************************************************************************* + * + * FUNCTION: acpi_rs_create_resource_list + * + * PARAMETERS: aml_buffer - Pointer to the resource byte stream + * output_buffer - Pointer to the user's buffer + * + * RETURN: Status: AE_OK if okay, else a valid acpi_status code + * If output_buffer is not large enough, output_buffer_length + * indicates how large output_buffer should be, else it + * indicates how may u8 elements of output_buffer are valid. + * + * DESCRIPTION: Takes the byte stream returned from a _CRS, _PRS control method + * execution and parses the stream to create a linked list + * of device resources. + * + ******************************************************************************/ +acpi_status +acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer, + struct acpi_buffer *output_buffer) +{ + + acpi_status status; + u8 *aml_start; + acpi_size list_size_needed = 0; + u32 aml_buffer_length; + void *resource; + + ACPI_FUNCTION_TRACE(rs_create_resource_list); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "AmlBuffer = %p\n", aml_buffer)); + + /* Params already validated, so we don't re-validate here */ + + aml_buffer_length = aml_buffer->buffer.length; + aml_start = aml_buffer->buffer.pointer; + + /* + * Pass the aml_buffer into a module that can calculate + * the buffer size needed for the linked list + */ + status = acpi_rs_get_list_length(aml_start, aml_buffer_length, + &list_size_needed); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Status=%X ListSizeNeeded=%X\n", + status, (u32) list_size_needed)); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer(output_buffer, list_size_needed); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Do the conversion */ + + resource = output_buffer->pointer; + status = acpi_ut_walk_aml_resources(NULL, aml_start, aml_buffer_length, + acpi_rs_convert_aml_to_resources, + &resource); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "OutputBuffer %p Length %X\n", + output_buffer->pointer, (u32) output_buffer->length)); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_create_pci_routing_table + * + * PARAMETERS: package_object - Pointer to a package containing one + * of more ACPI_OPERAND_OBJECTs + * output_buffer - Pointer to the user's buffer + * + * RETURN: Status AE_OK if okay, else a valid acpi_status code. + * If the output_buffer is too small, the error will be + * AE_BUFFER_OVERFLOW and output_buffer->Length will point + * to the size buffer needed. + * + * DESCRIPTION: Takes the union acpi_operand_object package and creates a + * linked list of PCI interrupt descriptions + * + * NOTE: It is the caller's responsibility to ensure that the start of the + * output buffer is aligned properly (if necessary). + * + ******************************************************************************/ + +acpi_status +acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, + struct acpi_buffer *output_buffer) +{ + u8 *buffer; + union acpi_operand_object **top_object_list; + union acpi_operand_object **sub_object_list; + union acpi_operand_object *obj_desc; + acpi_size buffer_size_needed = 0; + u32 number_of_elements; + u32 index; + struct acpi_pci_routing_table *user_prt; + struct acpi_namespace_node *node; + acpi_status status; + struct acpi_buffer path_buffer; + + ACPI_FUNCTION_TRACE(rs_create_pci_routing_table); + + /* Params already validated, so we don't re-validate here */ + + /* Get the required buffer length */ + + status = acpi_rs_get_pci_routing_table_length(package_object, + &buffer_size_needed); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "BufferSizeNeeded = %X\n", + (u32) buffer_size_needed)); + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer(output_buffer, buffer_size_needed); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Loop through the ACPI_INTERNAL_OBJECTS - Each object should be a + * package that in turn contains an u64 Address, a u8 Pin, + * a Name, and a u8 source_index. + */ + top_object_list = package_object->package.elements; + number_of_elements = package_object->package.count; + buffer = output_buffer->pointer; + user_prt = ACPI_CAST_PTR(struct acpi_pci_routing_table, buffer); + + for (index = 0; index < number_of_elements; index++) { + + /* + * Point user_prt past this current structure + * + * NOTE: On the first iteration, user_prt->Length will + * be zero because we cleared the return buffer earlier + */ + buffer += user_prt->length; + user_prt = ACPI_CAST_PTR(struct acpi_pci_routing_table, buffer); + + /* + * Fill in the Length field with the information we have at this point. + * The minus four is to subtract the size of the u8 Source[4] member + * because it is added below. + */ + user_prt->length = (sizeof(struct acpi_pci_routing_table) - 4); + + /* Each subpackage must be of length 4 */ + + if ((*top_object_list)->package.count != 4) { + ACPI_ERROR((AE_INFO, + "(PRT[%u]) Need package of length 4, found length %u", + index, (*top_object_list)->package.count)); + return_ACPI_STATUS(AE_AML_PACKAGE_LIMIT); + } + + /* + * Dereference the subpackage. + * The sub_object_list will now point to an array of the four IRQ + * elements: [Address, Pin, Source, source_index] + */ + sub_object_list = (*top_object_list)->package.elements; + + /* 1) First subobject: Dereference the PRT.Address */ + + obj_desc = sub_object_list[0]; + if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, + "(PRT[%u].Address) Need Integer, found %s", + index, + acpi_ut_get_object_type_name(obj_desc))); + return_ACPI_STATUS(AE_BAD_DATA); + } + + user_prt->address = obj_desc->integer.value; + + /* 2) Second subobject: Dereference the PRT.Pin */ + + obj_desc = sub_object_list[1]; + if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, + "(PRT[%u].Pin) Need Integer, found %s", + index, + acpi_ut_get_object_type_name(obj_desc))); + return_ACPI_STATUS(AE_BAD_DATA); + } + + user_prt->pin = (u32) obj_desc->integer.value; + + /* + * 3) Third subobject: Dereference the PRT.source_name + * The name may be unresolved (slack mode), so allow a null object + */ + obj_desc = sub_object_list[2]; + if (obj_desc) { + switch (obj_desc->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + + if (obj_desc->reference.class != + ACPI_REFCLASS_NAME) { + ACPI_ERROR((AE_INFO, + "(PRT[%u].Source) Need name, found Reference Class 0x%X", + index, + obj_desc->reference.class)); + return_ACPI_STATUS(AE_BAD_DATA); + } + + node = obj_desc->reference.node; + + /* Use *remaining* length of the buffer as max for pathname */ + + path_buffer.length = output_buffer->length - + (u32) ((u8 *) user_prt->source - + (u8 *) output_buffer->pointer); + path_buffer.pointer = user_prt->source; + + status = + acpi_ns_handle_to_pathname((acpi_handle) + node, + &path_buffer); + + /* +1 to include null terminator */ + + user_prt->length += + (u32) ACPI_STRLEN(user_prt->source) + 1; + break; + + case ACPI_TYPE_STRING: + + ACPI_STRCPY(user_prt->source, + obj_desc->string.pointer); + + /* + * Add to the Length field the length of the string + * (add 1 for terminator) + */ + user_prt->length += obj_desc->string.length + 1; + break; + + case ACPI_TYPE_INTEGER: + /* + * If this is a number, then the Source Name is NULL, since the + * entire buffer was zeroed out, we can leave this alone. + * + * Add to the Length field the length of the u32 NULL + */ + user_prt->length += sizeof(u32); + break; + + default: + + ACPI_ERROR((AE_INFO, + "(PRT[%u].Source) Need Ref/String/Integer, found %s", + index, + acpi_ut_get_object_type_name + (obj_desc))); + return_ACPI_STATUS(AE_BAD_DATA); + } + } + + /* Now align the current length */ + + user_prt->length = + (u32) ACPI_ROUND_UP_TO_64BIT(user_prt->length); + + /* 4) Fourth subobject: Dereference the PRT.source_index */ + + obj_desc = sub_object_list[3]; + if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, + "(PRT[%u].SourceIndex) Need Integer, found %s", + index, + acpi_ut_get_object_type_name(obj_desc))); + return_ACPI_STATUS(AE_BAD_DATA); + } + + user_prt->source_index = (u32) obj_desc->integer.value; + + /* Point to the next union acpi_operand_object in the top level package */ + + top_object_list++; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "OutputBuffer %p Length %X\n", + output_buffer->pointer, (u32) output_buffer->length)); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_create_aml_resources + * + * PARAMETERS: resource_list - Pointer to the resource list buffer + * output_buffer - Where the AML buffer is returned + * + * RETURN: Status AE_OK if okay, else a valid acpi_status code. + * If the output_buffer is too small, the error will be + * AE_BUFFER_OVERFLOW and output_buffer->Length will point + * to the size buffer needed. + * + * DESCRIPTION: Converts a list of device resources to an AML bytestream + * to be used as input for the _SRS control method. + * + ******************************************************************************/ + +acpi_status +acpi_rs_create_aml_resources(struct acpi_buffer *resource_list, + struct acpi_buffer *output_buffer) +{ + acpi_status status; + acpi_size aml_size_needed = 0; + + ACPI_FUNCTION_TRACE(rs_create_aml_resources); + + /* Params already validated, no need to re-validate here */ + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ResourceList Buffer = %p\n", + resource_list->pointer)); + + /* Get the buffer size needed for the AML byte stream */ + + status = acpi_rs_get_aml_length(resource_list->pointer, + resource_list->length, + &aml_size_needed); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "AmlSizeNeeded=%X, %s\n", + (u32)aml_size_needed, acpi_format_exception(status))); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer(output_buffer, aml_size_needed); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Do the conversion */ + + status = acpi_rs_convert_resources_to_aml(resource_list->pointer, + aml_size_needed, + output_buffer->pointer); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "OutputBuffer %p Length %X\n", + output_buffer->pointer, (u32) output_buffer->length)); + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/rsdump.c b/kernel/drivers/acpi/acpica/rsdump.c new file mode 100644 index 000000000..c428bb332 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsdump.c @@ -0,0 +1,569 @@ +/******************************************************************************* + * + * Module Name: rsdump - AML debugger support for resource structures. + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsdump") + +/* + * All functions in this module are used by the AML Debugger only + */ +#if defined(ACPI_DEBUGGER) +/* Local prototypes */ +static void acpi_rs_out_string(char *title, char *value); + +static void acpi_rs_out_integer8(char *title, u8 value); + +static void acpi_rs_out_integer16(char *title, u16 value); + +static void acpi_rs_out_integer32(char *title, u32 value); + +static void acpi_rs_out_integer64(char *title, u64 value); + +static void acpi_rs_out_title(char *title); + +static void acpi_rs_dump_byte_list(u16 length, u8 *data); + +static void acpi_rs_dump_word_list(u16 length, u16 *data); + +static void acpi_rs_dump_dword_list(u8 length, u32 *data); + +static void acpi_rs_dump_short_byte_list(u8 length, u8 *data); + +static void +acpi_rs_dump_resource_source(struct acpi_resource_source *resource_source); + +static void acpi_rs_dump_address_common(union acpi_resource_data *resource); + +static void +acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table); + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_resource_list + * + * PARAMETERS: resource_list - Pointer to a resource descriptor list + * + * RETURN: None + * + * DESCRIPTION: Dispatches the structure to the correct dump routine. + * + ******************************************************************************/ + +void acpi_rs_dump_resource_list(struct acpi_resource *resource_list) +{ + u32 count = 0; + u32 type; + + ACPI_FUNCTION_ENTRY(); + + /* Check if debug output enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) { + return; + } + + /* Walk list and dump all resource descriptors (END_TAG terminates) */ + + do { + acpi_os_printf("\n[%02X] ", count); + count++; + + /* Validate Type before dispatch */ + + type = resource_list->type; + if (type > ACPI_RESOURCE_TYPE_MAX) { + acpi_os_printf + ("Invalid descriptor type (%X) in resource list\n", + resource_list->type); + return; + } + + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource_list->length) { + acpi_os_printf + ("Invalid zero length descriptor in resource list\n"); + return; + } + + /* Dump the resource descriptor */ + + if (type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + acpi_rs_dump_descriptor(&resource_list->data, + acpi_gbl_dump_serial_bus_dispatch + [resource_list->data. + common_serial_bus.type]); + } else { + acpi_rs_dump_descriptor(&resource_list->data, + acpi_gbl_dump_resource_dispatch + [type]); + } + + /* Point to the next resource structure */ + + resource_list = ACPI_NEXT_RESOURCE(resource_list); + + /* Exit when END_TAG descriptor is reached */ + + } while (type != ACPI_RESOURCE_TYPE_END_TAG); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_irq_list + * + * PARAMETERS: route_table - Pointer to the routing table to dump. + * + * RETURN: None + * + * DESCRIPTION: Print IRQ routing table + * + ******************************************************************************/ + +void acpi_rs_dump_irq_list(u8 *route_table) +{ + struct acpi_pci_routing_table *prt_element; + u8 count; + + ACPI_FUNCTION_ENTRY(); + + /* Check if debug output enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(ACPI_LV_RESOURCES, _COMPONENT)) { + return; + } + + prt_element = ACPI_CAST_PTR(struct acpi_pci_routing_table, route_table); + + /* Dump all table elements, Exit on zero length element */ + + for (count = 0; prt_element->length; count++) { + acpi_os_printf("\n[%02X] PCI IRQ Routing Table Package\n", + count); + acpi_rs_dump_descriptor(prt_element, acpi_rs_dump_prt); + + prt_element = ACPI_ADD_PTR(struct acpi_pci_routing_table, + prt_element, prt_element->length); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_descriptor + * + * PARAMETERS: resource - Buffer containing the resource + * table - Table entry to decode the resource + * + * RETURN: None + * + * DESCRIPTION: Dump a resource descriptor based on a dump table entry. + * + ******************************************************************************/ + +static void +acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) +{ + u8 *target = NULL; + u8 *previous_target; + char *name; + u8 count; + + /* First table entry must contain the table length (# of table entries) */ + + count = table->offset; + + while (count) { + previous_target = target; + target = ACPI_ADD_PTR(u8, resource, table->offset); + name = table->name; + + switch (table->opcode) { + case ACPI_RSD_TITLE: + /* + * Optional resource title + */ + if (table->name) { + acpi_os_printf("%s Resource\n", name); + } + break; + + /* Strings */ + + case ACPI_RSD_LITERAL: + + acpi_rs_out_string(name, + ACPI_CAST_PTR(char, table->pointer)); + break; + + case ACPI_RSD_STRING: + + acpi_rs_out_string(name, ACPI_CAST_PTR(char, target)); + break; + + /* Data items, 8/16/32/64 bit */ + + case ACPI_RSD_UINT8: + + if (table->pointer) { + acpi_rs_out_string(name, ACPI_CAST_PTR(char, + table-> + pointer + [*target])); + } else { + acpi_rs_out_integer8(name, ACPI_GET8(target)); + } + break; + + case ACPI_RSD_UINT16: + + acpi_rs_out_integer16(name, ACPI_GET16(target)); + break; + + case ACPI_RSD_UINT32: + + acpi_rs_out_integer32(name, ACPI_GET32(target)); + break; + + case ACPI_RSD_UINT64: + + acpi_rs_out_integer64(name, ACPI_GET64(target)); + break; + + /* Flags: 1-bit and 2-bit flags supported */ + + case ACPI_RSD_1BITFLAG: + + acpi_rs_out_string(name, ACPI_CAST_PTR(char, + table-> + pointer[*target & + 0x01])); + break; + + case ACPI_RSD_2BITFLAG: + + acpi_rs_out_string(name, ACPI_CAST_PTR(char, + table-> + pointer[*target & + 0x03])); + break; + + case ACPI_RSD_3BITFLAG: + + acpi_rs_out_string(name, ACPI_CAST_PTR(char, + table-> + pointer[*target & + 0x07])); + break; + + case ACPI_RSD_SHORTLIST: + /* + * Short byte list (single line output) for DMA and IRQ resources + * Note: The list length is obtained from the previous table entry + */ + if (previous_target) { + acpi_rs_out_title(name); + acpi_rs_dump_short_byte_list(*previous_target, + target); + } + break; + + case ACPI_RSD_SHORTLISTX: + /* + * Short byte list (single line output) for GPIO vendor data + * Note: The list length is obtained from the previous table entry + */ + if (previous_target) { + acpi_rs_out_title(name); + acpi_rs_dump_short_byte_list(*previous_target, + * + (ACPI_CAST_INDIRECT_PTR + (u8, target))); + } + break; + + case ACPI_RSD_LONGLIST: + /* + * Long byte list for Vendor resource data + * Note: The list length is obtained from the previous table entry + */ + if (previous_target) { + acpi_rs_dump_byte_list(ACPI_GET16 + (previous_target), + target); + } + break; + + case ACPI_RSD_DWORDLIST: + /* + * Dword list for Extended Interrupt resources + * Note: The list length is obtained from the previous table entry + */ + if (previous_target) { + acpi_rs_dump_dword_list(*previous_target, + ACPI_CAST_PTR(u32, + target)); + } + break; + + case ACPI_RSD_WORDLIST: + /* + * Word list for GPIO Pin Table + * Note: The list length is obtained from the previous table entry + */ + if (previous_target) { + acpi_rs_dump_word_list(*previous_target, + *(ACPI_CAST_INDIRECT_PTR + (u16, target))); + } + break; + + case ACPI_RSD_ADDRESS: + /* + * Common flags for all Address resources + */ + acpi_rs_dump_address_common(ACPI_CAST_PTR + (union acpi_resource_data, + target)); + break; + + case ACPI_RSD_SOURCE: + /* + * Optional resource_source for Address resources + */ + acpi_rs_dump_resource_source(ACPI_CAST_PTR + (struct + acpi_resource_source, + target)); + break; + + default: + + acpi_os_printf("**** Invalid table opcode [%X] ****\n", + table->opcode); + return; + } + + table++; + count--; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_resource_source + * + * PARAMETERS: resource_source - Pointer to a Resource Source struct + * + * RETURN: None + * + * DESCRIPTION: Common routine for dumping the optional resource_source and the + * corresponding resource_source_index. + * + ******************************************************************************/ + +static void +acpi_rs_dump_resource_source(struct acpi_resource_source *resource_source) +{ + ACPI_FUNCTION_ENTRY(); + + if (resource_source->index == 0xFF) { + return; + } + + acpi_rs_out_integer8("Resource Source Index", resource_source->index); + + acpi_rs_out_string("Resource Source", + resource_source->string_ptr ? + resource_source->string_ptr : "[Not Specified]"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_address_common + * + * PARAMETERS: resource - Pointer to an internal resource descriptor + * + * RETURN: None + * + * DESCRIPTION: Dump the fields that are common to all Address resource + * descriptors + * + ******************************************************************************/ + +static void acpi_rs_dump_address_common(union acpi_resource_data *resource) +{ + ACPI_FUNCTION_ENTRY(); + + /* Decode the type-specific flags */ + + switch (resource->address.resource_type) { + case ACPI_MEMORY_RANGE: + + acpi_rs_dump_descriptor(resource, acpi_rs_dump_memory_flags); + break; + + case ACPI_IO_RANGE: + + acpi_rs_dump_descriptor(resource, acpi_rs_dump_io_flags); + break; + + case ACPI_BUS_NUMBER_RANGE: + + acpi_rs_out_string("Resource Type", "Bus Number Range"); + break; + + default: + + acpi_rs_out_integer8("Resource Type", + (u8) resource->address.resource_type); + break; + } + + /* Decode the general flags */ + + acpi_rs_dump_descriptor(resource, acpi_rs_dump_general_flags); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_out* + * + * PARAMETERS: title - Name of the resource field + * value - Value of the resource field + * + * RETURN: None + * + * DESCRIPTION: Miscellaneous helper functions to consistently format the + * output of the resource dump routines + * + ******************************************************************************/ + +static void acpi_rs_out_string(char *title, char *value) +{ + acpi_os_printf("%27s : %s", title, value); + if (!*value) { + acpi_os_printf("[NULL NAMESTRING]"); + } + acpi_os_printf("\n"); +} + +static void acpi_rs_out_integer8(char *title, u8 value) +{ + acpi_os_printf("%27s : %2.2X\n", title, value); +} + +static void acpi_rs_out_integer16(char *title, u16 value) +{ + acpi_os_printf("%27s : %4.4X\n", title, value); +} + +static void acpi_rs_out_integer32(char *title, u32 value) +{ + acpi_os_printf("%27s : %8.8X\n", title, value); +} + +static void acpi_rs_out_integer64(char *title, u64 value) +{ + acpi_os_printf("%27s : %8.8X%8.8X\n", title, ACPI_FORMAT_UINT64(value)); +} + +static void acpi_rs_out_title(char *title) +{ + acpi_os_printf("%27s : ", title); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump*List + * + * PARAMETERS: length - Number of elements in the list + * data - Start of the list + * + * RETURN: None + * + * DESCRIPTION: Miscellaneous functions to dump lists of raw data + * + ******************************************************************************/ + +static void acpi_rs_dump_byte_list(u16 length, u8 * data) +{ + u8 i; + + for (i = 0; i < length; i++) { + acpi_os_printf("%25s%2.2X : %2.2X\n", "Byte", i, data[i]); + } +} + +static void acpi_rs_dump_short_byte_list(u8 length, u8 * data) +{ + u8 i; + + for (i = 0; i < length; i++) { + acpi_os_printf("%X ", data[i]); + } + acpi_os_printf("\n"); +} + +static void acpi_rs_dump_dword_list(u8 length, u32 * data) +{ + u8 i; + + for (i = 0; i < length; i++) { + acpi_os_printf("%25s%2.2X : %8.8X\n", "Dword", i, data[i]); + } +} + +static void acpi_rs_dump_word_list(u16 length, u16 *data) +{ + u16 i; + + for (i = 0; i < length; i++) { + acpi_os_printf("%25s%2.2X : %4.4X\n", "Word", i, data[i]); + } +} + +#endif diff --git a/kernel/drivers/acpi/acpica/rsdumpinfo.c b/kernel/drivers/acpi/acpica/rsdumpinfo.c new file mode 100644 index 000000000..b29d9ec63 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsdumpinfo.c @@ -0,0 +1,455 @@ +/******************************************************************************* + * + * Module Name: rsdumpinfo - Tables used to display resource descriptors. + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsdumpinfo") + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER) +#define ACPI_RSD_OFFSET(f) (u8) ACPI_OFFSET (union acpi_resource_data,f) +#define ACPI_PRT_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_pci_routing_table,f) +#define ACPI_RSD_TABLE_SIZE(name) (sizeof(name) / sizeof (struct acpi_rsdump_info)) +/******************************************************************************* + * + * Resource Descriptor info tables + * + * Note: The first table entry must be a Title or Literal and must contain + * the table length (number of table entries) + * + ******************************************************************************/ +struct acpi_rsdump_info acpi_rs_dump_irq[7] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_irq), "IRQ", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(irq.descriptor_length), + "Descriptor Length", NULL}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(irq.triggering), "Triggering", + acpi_gbl_he_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(irq.polarity), "Polarity", + acpi_gbl_ll_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(irq.sharable), "Sharing", + acpi_gbl_shr_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(irq.interrupt_count), + "Interrupt Count", NULL}, + {ACPI_RSD_SHORTLIST, ACPI_RSD_OFFSET(irq.interrupts[0]), + "Interrupt List", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_dma[6] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_dma), "DMA", NULL}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(dma.type), "Speed", + acpi_gbl_typ_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(dma.bus_master), "Mastering", + acpi_gbl_bm_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(dma.transfer), "Transfer Type", + acpi_gbl_siz_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(dma.channel_count), "Channel Count", + NULL}, + {ACPI_RSD_SHORTLIST, ACPI_RSD_OFFSET(dma.channels[0]), "Channel List", + NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_start_dpf[4] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_start_dpf), + "Start-Dependent-Functions", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(start_dpf.descriptor_length), + "Descriptor Length", NULL}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(start_dpf.compatibility_priority), + "Compatibility Priority", acpi_gbl_config_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(start_dpf.performance_robustness), + "Performance/Robustness", acpi_gbl_config_decode} +}; + +struct acpi_rsdump_info acpi_rs_dump_end_dpf[1] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_end_dpf), + "End-Dependent-Functions", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_io[6] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_io), "I/O", NULL}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(io.io_decode), "Address Decoding", + acpi_gbl_io_decode}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(io.minimum), "Address Minimum", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(io.maximum), "Address Maximum", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(io.alignment), "Alignment", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(io.address_length), "Address Length", + NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_fixed_io[3] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_fixed_io), + "Fixed I/O", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_io.address), "Address", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(fixed_io.address_length), + "Address Length", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_vendor[3] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_vendor), + "Vendor Specific", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(vendor.byte_length), "Length", NULL}, + {ACPI_RSD_LONGLIST, ACPI_RSD_OFFSET(vendor.byte_data[0]), "Vendor Data", + NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_end_tag[1] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_end_tag), "EndTag", + NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_memory24[6] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_memory24), + "24-Bit Memory Range", NULL}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(memory24.write_protect), + "Write Protect", acpi_gbl_rw_decode}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.minimum), "Address Minimum", + NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.maximum), "Address Maximum", + NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.alignment), "Alignment", + NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(memory24.address_length), + "Address Length", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_memory32[6] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_memory32), + "32-Bit Memory Range", NULL}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(memory32.write_protect), + "Write Protect", acpi_gbl_rw_decode}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.minimum), "Address Minimum", + NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.maximum), "Address Maximum", + NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.alignment), "Alignment", + NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(memory32.address_length), + "Address Length", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_fixed_memory32[4] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_fixed_memory32), + "32-Bit Fixed Memory Range", NULL}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(fixed_memory32.write_protect), + "Write Protect", acpi_gbl_rw_decode}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(fixed_memory32.address), "Address", + NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(fixed_memory32.address_length), + "Address Length", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_address16[8] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address16), + "16-Bit WORD Address Space", NULL}, + {ACPI_RSD_ADDRESS, 0, NULL, NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.granularity), + "Granularity", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.minimum), + "Address Minimum", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.maximum), + "Address Maximum", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.translation_offset), + "Translation Offset", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.address_length), + "Address Length", NULL}, + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address16.resource_source), NULL, NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_address32[8] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address32), + "32-Bit DWORD Address Space", NULL}, + {ACPI_RSD_ADDRESS, 0, NULL, NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.granularity), + "Granularity", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.minimum), + "Address Minimum", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.maximum), + "Address Maximum", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.translation_offset), + "Translation Offset", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.address_length), + "Address Length", NULL}, + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address32.resource_source), NULL, NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_address64[8] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address64), + "64-Bit QWORD Address Space", NULL}, + {ACPI_RSD_ADDRESS, 0, NULL, NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.granularity), + "Granularity", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.minimum), + "Address Minimum", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.maximum), + "Address Maximum", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.translation_offset), + "Translation Offset", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.address_length), + "Address Length", NULL}, + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address64.resource_source), NULL, NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_ext_address64[8] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_ext_address64), + "64-Bit Extended Address Space", NULL}, + {ACPI_RSD_ADDRESS, 0, NULL, NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.granularity), + "Granularity", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.minimum), + "Address Minimum", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.maximum), + "Address Maximum", NULL}, + {ACPI_RSD_UINT64, + ACPI_RSD_OFFSET(ext_address64.address.translation_offset), + "Translation Offset", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.address_length), + "Address Length", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.type_specific), + "Type-Specific Attribute", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_ext_irq[8] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_ext_irq), + "Extended IRQ", NULL}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(extended_irq.producer_consumer), + "Type", acpi_gbl_consume_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(extended_irq.triggering), + "Triggering", acpi_gbl_he_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(extended_irq.polarity), "Polarity", + acpi_gbl_ll_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(extended_irq.sharable), "Sharing", + acpi_gbl_shr_decode}, + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(extended_irq.resource_source), NULL, + NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(extended_irq.interrupt_count), + "Interrupt Count", NULL}, + {ACPI_RSD_DWORDLIST, ACPI_RSD_OFFSET(extended_irq.interrupts[0]), + "Interrupt List", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_generic_reg[6] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_generic_reg), + "Generic Register", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.space_id), "Space ID", + NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.bit_width), "Bit Width", + NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.bit_offset), "Bit Offset", + NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(generic_reg.access_size), + "Access Size", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(generic_reg.address), "Address", NULL} +}; + +struct acpi_rsdump_info acpi_rs_dump_gpio[16] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_gpio), "GPIO", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.revision_id), "RevisionId", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.connection_type), + "ConnectionType", acpi_gbl_ct_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(gpio.producer_consumer), + "ProducerConsumer", acpi_gbl_consume_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(gpio.pin_config), "PinConfig", + acpi_gbl_ppc_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.sharable), "Sharing", + acpi_gbl_shr_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.io_restriction), + "IoRestriction", acpi_gbl_ior_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(gpio.triggering), "Triggering", + acpi_gbl_he_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(gpio.polarity), "Polarity", + acpi_gbl_ll_decode}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.drive_strength), "DriveStrength", + NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.debounce_timeout), + "DebounceTimeout", NULL}, + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(gpio.resource_source), + "ResourceSource", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.pin_table_length), + "PinTableLength", NULL}, + {ACPI_RSD_WORDLIST, ACPI_RSD_OFFSET(gpio.pin_table), "PinTable", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(gpio.vendor_length), "VendorLength", + NULL}, + {ACPI_RSD_SHORTLISTX, ACPI_RSD_OFFSET(gpio.vendor_data), "VendorData", + NULL}, +}; + +struct acpi_rsdump_info acpi_rs_dump_fixed_dma[4] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_fixed_dma), + "FixedDma", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_dma.request_lines), + "RequestLines", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(fixed_dma.channels), "Channels", + NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(fixed_dma.width), "TransferWidth", + acpi_gbl_dts_decode}, +}; + +#define ACPI_RS_DUMP_COMMON_SERIAL_BUS \ + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.revision_id), "RevisionId", NULL}, \ + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.type), "Type", acpi_gbl_sbt_decode}, \ + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (common_serial_bus.producer_consumer), "ProducerConsumer", acpi_gbl_consume_decode}, \ + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET (common_serial_bus.slave_mode), "SlaveMode", acpi_gbl_sm_decode}, \ + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET (common_serial_bus.type_revision_id), "TypeRevisionId", NULL}, \ + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (common_serial_bus.type_data_length), "TypeDataLength", NULL}, \ + {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET (common_serial_bus.resource_source), "ResourceSource", NULL}, \ + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET (common_serial_bus.vendor_length), "VendorLength", NULL}, \ + {ACPI_RSD_SHORTLISTX,ACPI_RSD_OFFSET (common_serial_bus.vendor_data), "VendorData", NULL}, + +struct acpi_rsdump_info acpi_rs_dump_common_serial_bus[10] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_common_serial_bus), + "Common Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS +}; + +struct acpi_rsdump_info acpi_rs_dump_i2c_serial_bus[13] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_i2c_serial_bus), + "I2C Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_1BITFLAG, + ACPI_RSD_OFFSET(i2c_serial_bus. + access_mode), + "AccessMode", acpi_gbl_am_decode}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(i2c_serial_bus.connection_speed), + "ConnectionSpeed", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(i2c_serial_bus.slave_address), + "SlaveAddress", NULL}, +}; + +struct acpi_rsdump_info acpi_rs_dump_spi_serial_bus[17] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_spi_serial_bus), + "Spi Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_1BITFLAG, + ACPI_RSD_OFFSET(spi_serial_bus. + wire_mode), "WireMode", + acpi_gbl_wm_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(spi_serial_bus.device_polarity), + "DevicePolarity", acpi_gbl_dp_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.data_bit_length), + "DataBitLength", NULL}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.clock_phase), + "ClockPhase", acpi_gbl_cph_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(spi_serial_bus.clock_polarity), + "ClockPolarity", acpi_gbl_cpo_decode}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(spi_serial_bus.device_selection), + "DeviceSelection", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(spi_serial_bus.connection_speed), + "ConnectionSpeed", NULL}, +}; + +struct acpi_rsdump_info acpi_rs_dump_uart_serial_bus[19] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_uart_serial_bus), + "Uart Serial Bus", NULL}, + ACPI_RS_DUMP_COMMON_SERIAL_BUS {ACPI_RSD_2BITFLAG, + ACPI_RSD_OFFSET(uart_serial_bus. + flow_control), + "FlowControl", acpi_gbl_fc_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.stop_bits), + "StopBits", acpi_gbl_sb_decode}, + {ACPI_RSD_3BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.data_bits), + "DataBits", acpi_gbl_bpb_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(uart_serial_bus.endian), "Endian", + acpi_gbl_ed_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(uart_serial_bus.parity), "Parity", + acpi_gbl_pt_decode}, + {ACPI_RSD_UINT8, ACPI_RSD_OFFSET(uart_serial_bus.lines_enabled), + "LinesEnabled", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(uart_serial_bus.rx_fifo_size), + "RxFifoSize", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(uart_serial_bus.tx_fifo_size), + "TxFifoSize", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(uart_serial_bus.default_baud_rate), + "ConnectionSpeed", NULL}, +}; + +/* + * Tables used for common address descriptor flag fields + */ +struct acpi_rsdump_info acpi_rs_dump_general_flags[5] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_general_flags), NULL, + NULL}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.producer_consumer), + "Consumer/Producer", acpi_gbl_consume_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.decode), "Address Decode", + acpi_gbl_dec_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.min_address_fixed), + "Min Relocatability", acpi_gbl_min_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.max_address_fixed), + "Max Relocatability", acpi_gbl_max_decode} +}; + +struct acpi_rsdump_info acpi_rs_dump_memory_flags[5] = { + {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_memory_flags), + "Resource Type", (void *)"Memory Range"}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.mem.write_protect), + "Write Protect", acpi_gbl_rw_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(address.info.mem.caching), + "Caching", acpi_gbl_mem_decode}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(address.info.mem.range_type), + "Range Type", acpi_gbl_mtp_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.mem.translation), + "Translation", acpi_gbl_ttp_decode} +}; + +struct acpi_rsdump_info acpi_rs_dump_io_flags[4] = { + {ACPI_RSD_LITERAL, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_io_flags), + "Resource Type", (void *)"I/O Range"}, + {ACPI_RSD_2BITFLAG, ACPI_RSD_OFFSET(address.info.io.range_type), + "Range Type", acpi_gbl_rng_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.io.translation), + "Translation", acpi_gbl_ttp_decode}, + {ACPI_RSD_1BITFLAG, ACPI_RSD_OFFSET(address.info.io.translation_type), + "Translation Type", acpi_gbl_trs_decode} +}; + +/* + * Table used to dump _PRT contents + */ +struct acpi_rsdump_info acpi_rs_dump_prt[5] = { + {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_prt), NULL, NULL}, + {ACPI_RSD_UINT64, ACPI_PRT_OFFSET(address), "Address", NULL}, + {ACPI_RSD_UINT32, ACPI_PRT_OFFSET(pin), "Pin", NULL}, + {ACPI_RSD_STRING, ACPI_PRT_OFFSET(source[0]), "Source", NULL}, + {ACPI_RSD_UINT32, ACPI_PRT_OFFSET(source_index), "Source Index", NULL} +}; + +#endif diff --git a/kernel/drivers/acpi/acpica/rsinfo.c b/kernel/drivers/acpi/acpica/rsinfo.c new file mode 100644 index 000000000..edecfc675 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsinfo.c @@ -0,0 +1,248 @@ +/******************************************************************************* + * + * Module Name: rsinfo - Dispatch and Info tables + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsinfo") + +/* + * Resource dispatch and information tables. Any new resource types (either + * Large or Small) must be reflected in each of these tables, so they are here + * in one place. + * + * The tables for Large descriptors are indexed by bits 6:0 of the AML + * descriptor type byte. The tables for Small descriptors are indexed by + * bits 6:3 of the descriptor byte. The tables for internal resource + * descriptors are indexed by the acpi_resource_type field. + */ +/* Dispatch table for resource-to-AML (Set Resource) conversion functions */ +struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[] = { + acpi_rs_set_irq, /* 0x00, ACPI_RESOURCE_TYPE_IRQ */ + acpi_rs_convert_dma, /* 0x01, ACPI_RESOURCE_TYPE_DMA */ + acpi_rs_set_start_dpf, /* 0x02, ACPI_RESOURCE_TYPE_START_DEPENDENT */ + acpi_rs_convert_end_dpf, /* 0x03, ACPI_RESOURCE_TYPE_END_DEPENDENT */ + acpi_rs_convert_io, /* 0x04, ACPI_RESOURCE_TYPE_IO */ + acpi_rs_convert_fixed_io, /* 0x05, ACPI_RESOURCE_TYPE_FIXED_IO */ + acpi_rs_set_vendor, /* 0x06, ACPI_RESOURCE_TYPE_VENDOR */ + acpi_rs_convert_end_tag, /* 0x07, ACPI_RESOURCE_TYPE_END_TAG */ + acpi_rs_convert_memory24, /* 0x08, ACPI_RESOURCE_TYPE_MEMORY24 */ + acpi_rs_convert_memory32, /* 0x09, ACPI_RESOURCE_TYPE_MEMORY32 */ + acpi_rs_convert_fixed_memory32, /* 0x0A, ACPI_RESOURCE_TYPE_FIXED_MEMORY32 */ + acpi_rs_convert_address16, /* 0x0B, ACPI_RESOURCE_TYPE_ADDRESS16 */ + acpi_rs_convert_address32, /* 0x0C, ACPI_RESOURCE_TYPE_ADDRESS32 */ + acpi_rs_convert_address64, /* 0x0D, ACPI_RESOURCE_TYPE_ADDRESS64 */ + acpi_rs_convert_ext_address64, /* 0x0E, ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */ + acpi_rs_convert_ext_irq, /* 0x0F, ACPI_RESOURCE_TYPE_EXTENDED_IRQ */ + acpi_rs_convert_generic_reg, /* 0x10, ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + acpi_rs_convert_gpio, /* 0x11, ACPI_RESOURCE_TYPE_GPIO */ + acpi_rs_convert_fixed_dma, /* 0x12, ACPI_RESOURCE_TYPE_FIXED_DMA */ + NULL, /* 0x13, ACPI_RESOURCE_TYPE_SERIAL_BUS - Use subtype table below */ +}; + +/* Dispatch tables for AML-to-resource (Get Resource) conversion functions */ + +struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[] = { + /* Small descriptors */ + + NULL, /* 0x00, Reserved */ + NULL, /* 0x01, Reserved */ + NULL, /* 0x02, Reserved */ + NULL, /* 0x03, Reserved */ + acpi_rs_get_irq, /* 0x04, ACPI_RESOURCE_NAME_IRQ */ + acpi_rs_convert_dma, /* 0x05, ACPI_RESOURCE_NAME_DMA */ + acpi_rs_get_start_dpf, /* 0x06, ACPI_RESOURCE_NAME_START_DEPENDENT */ + acpi_rs_convert_end_dpf, /* 0x07, ACPI_RESOURCE_NAME_END_DEPENDENT */ + acpi_rs_convert_io, /* 0x08, ACPI_RESOURCE_NAME_IO */ + acpi_rs_convert_fixed_io, /* 0x09, ACPI_RESOURCE_NAME_FIXED_IO */ + acpi_rs_convert_fixed_dma, /* 0x0A, ACPI_RESOURCE_NAME_FIXED_DMA */ + NULL, /* 0x0B, Reserved */ + NULL, /* 0x0C, Reserved */ + NULL, /* 0x0D, Reserved */ + acpi_rs_get_vendor_small, /* 0x0E, ACPI_RESOURCE_NAME_VENDOR_SMALL */ + acpi_rs_convert_end_tag, /* 0x0F, ACPI_RESOURCE_NAME_END_TAG */ + + /* Large descriptors */ + + NULL, /* 0x00, Reserved */ + acpi_rs_convert_memory24, /* 0x01, ACPI_RESOURCE_NAME_MEMORY24 */ + acpi_rs_convert_generic_reg, /* 0x02, ACPI_RESOURCE_NAME_GENERIC_REGISTER */ + NULL, /* 0x03, Reserved */ + acpi_rs_get_vendor_large, /* 0x04, ACPI_RESOURCE_NAME_VENDOR_LARGE */ + acpi_rs_convert_memory32, /* 0x05, ACPI_RESOURCE_NAME_MEMORY32 */ + acpi_rs_convert_fixed_memory32, /* 0x06, ACPI_RESOURCE_NAME_FIXED_MEMORY32 */ + acpi_rs_convert_address32, /* 0x07, ACPI_RESOURCE_NAME_ADDRESS32 */ + acpi_rs_convert_address16, /* 0x08, ACPI_RESOURCE_NAME_ADDRESS16 */ + acpi_rs_convert_ext_irq, /* 0x09, ACPI_RESOURCE_NAME_EXTENDED_IRQ */ + acpi_rs_convert_address64, /* 0x0A, ACPI_RESOURCE_NAME_ADDRESS64 */ + acpi_rs_convert_ext_address64, /* 0x0B, ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 */ + acpi_rs_convert_gpio, /* 0x0C, ACPI_RESOURCE_NAME_GPIO */ + NULL, /* 0x0D, Reserved */ + NULL, /* 0x0E, ACPI_RESOURCE_NAME_SERIAL_BUS - Use subtype table below */ +}; + +/* Subtype table for serial_bus -- I2C, SPI, and UART */ + +struct acpi_rsconvert_info *acpi_gbl_convert_resource_serial_bus_dispatch[] = { + NULL, + acpi_rs_convert_i2c_serial_bus, + acpi_rs_convert_spi_serial_bus, + acpi_rs_convert_uart_serial_bus, +}; + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER) + +/* Dispatch table for resource dump functions */ + +struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[] = { + acpi_rs_dump_irq, /* ACPI_RESOURCE_TYPE_IRQ */ + acpi_rs_dump_dma, /* ACPI_RESOURCE_TYPE_DMA */ + acpi_rs_dump_start_dpf, /* ACPI_RESOURCE_TYPE_START_DEPENDENT */ + acpi_rs_dump_end_dpf, /* ACPI_RESOURCE_TYPE_END_DEPENDENT */ + acpi_rs_dump_io, /* ACPI_RESOURCE_TYPE_IO */ + acpi_rs_dump_fixed_io, /* ACPI_RESOURCE_TYPE_FIXED_IO */ + acpi_rs_dump_vendor, /* ACPI_RESOURCE_TYPE_VENDOR */ + acpi_rs_dump_end_tag, /* ACPI_RESOURCE_TYPE_END_TAG */ + acpi_rs_dump_memory24, /* ACPI_RESOURCE_TYPE_MEMORY24 */ + acpi_rs_dump_memory32, /* ACPI_RESOURCE_TYPE_MEMORY32 */ + acpi_rs_dump_fixed_memory32, /* ACPI_RESOURCE_TYPE_FIXED_MEMORY32 */ + acpi_rs_dump_address16, /* ACPI_RESOURCE_TYPE_ADDRESS16 */ + acpi_rs_dump_address32, /* ACPI_RESOURCE_TYPE_ADDRESS32 */ + acpi_rs_dump_address64, /* ACPI_RESOURCE_TYPE_ADDRESS64 */ + acpi_rs_dump_ext_address64, /* ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */ + acpi_rs_dump_ext_irq, /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */ + acpi_rs_dump_generic_reg, /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + acpi_rs_dump_gpio, /* ACPI_RESOURCE_TYPE_GPIO */ + acpi_rs_dump_fixed_dma, /* ACPI_RESOURCE_TYPE_FIXED_DMA */ + NULL, /* ACPI_RESOURCE_TYPE_SERIAL_BUS */ +}; + +struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[] = { + NULL, + acpi_rs_dump_i2c_serial_bus, /* AML_RESOURCE_I2C_BUS_TYPE */ + acpi_rs_dump_spi_serial_bus, /* AML_RESOURCE_SPI_BUS_TYPE */ + acpi_rs_dump_uart_serial_bus, /* AML_RESOURCE_UART_BUS_TYPE */ +}; +#endif + +/* + * Base sizes for external AML resource descriptors, indexed by internal type. + * Includes size of the descriptor header (1 byte for small descriptors, + * 3 bytes for large descriptors) + */ +const u8 acpi_gbl_aml_resource_sizes[] = { + sizeof(struct aml_resource_irq), /* ACPI_RESOURCE_TYPE_IRQ (optional Byte 3 always created) */ + sizeof(struct aml_resource_dma), /* ACPI_RESOURCE_TYPE_DMA */ + sizeof(struct aml_resource_start_dependent), /* ACPI_RESOURCE_TYPE_START_DEPENDENT (optional Byte 1 always created) */ + sizeof(struct aml_resource_end_dependent), /* ACPI_RESOURCE_TYPE_END_DEPENDENT */ + sizeof(struct aml_resource_io), /* ACPI_RESOURCE_TYPE_IO */ + sizeof(struct aml_resource_fixed_io), /* ACPI_RESOURCE_TYPE_FIXED_IO */ + sizeof(struct aml_resource_vendor_small), /* ACPI_RESOURCE_TYPE_VENDOR */ + sizeof(struct aml_resource_end_tag), /* ACPI_RESOURCE_TYPE_END_TAG */ + sizeof(struct aml_resource_memory24), /* ACPI_RESOURCE_TYPE_MEMORY24 */ + sizeof(struct aml_resource_memory32), /* ACPI_RESOURCE_TYPE_MEMORY32 */ + sizeof(struct aml_resource_fixed_memory32), /* ACPI_RESOURCE_TYPE_FIXED_MEMORY32 */ + sizeof(struct aml_resource_address16), /* ACPI_RESOURCE_TYPE_ADDRESS16 */ + sizeof(struct aml_resource_address32), /* ACPI_RESOURCE_TYPE_ADDRESS32 */ + sizeof(struct aml_resource_address64), /* ACPI_RESOURCE_TYPE_ADDRESS64 */ + sizeof(struct aml_resource_extended_address64), /*ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64 */ + sizeof(struct aml_resource_extended_irq), /* ACPI_RESOURCE_TYPE_EXTENDED_IRQ */ + sizeof(struct aml_resource_generic_register), /* ACPI_RESOURCE_TYPE_GENERIC_REGISTER */ + sizeof(struct aml_resource_gpio), /* ACPI_RESOURCE_TYPE_GPIO */ + sizeof(struct aml_resource_fixed_dma), /* ACPI_RESOURCE_TYPE_FIXED_DMA */ + sizeof(struct aml_resource_common_serialbus), /* ACPI_RESOURCE_TYPE_SERIAL_BUS */ +}; + +const u8 acpi_gbl_resource_struct_sizes[] = { + /* Small descriptors */ + + 0, + 0, + 0, + 0, + ACPI_RS_SIZE(struct acpi_resource_irq), + ACPI_RS_SIZE(struct acpi_resource_dma), + ACPI_RS_SIZE(struct acpi_resource_start_dependent), + ACPI_RS_SIZE_MIN, + ACPI_RS_SIZE(struct acpi_resource_io), + ACPI_RS_SIZE(struct acpi_resource_fixed_io), + ACPI_RS_SIZE(struct acpi_resource_fixed_dma), + 0, + 0, + 0, + ACPI_RS_SIZE(struct acpi_resource_vendor), + ACPI_RS_SIZE_MIN, + + /* Large descriptors */ + + 0, + ACPI_RS_SIZE(struct acpi_resource_memory24), + ACPI_RS_SIZE(struct acpi_resource_generic_register), + 0, + ACPI_RS_SIZE(struct acpi_resource_vendor), + ACPI_RS_SIZE(struct acpi_resource_memory32), + ACPI_RS_SIZE(struct acpi_resource_fixed_memory32), + ACPI_RS_SIZE(struct acpi_resource_address32), + ACPI_RS_SIZE(struct acpi_resource_address16), + ACPI_RS_SIZE(struct acpi_resource_extended_irq), + ACPI_RS_SIZE(struct acpi_resource_address64), + ACPI_RS_SIZE(struct acpi_resource_extended_address64), + ACPI_RS_SIZE(struct acpi_resource_gpio), + ACPI_RS_SIZE(struct acpi_resource_common_serialbus) +}; + +const u8 acpi_gbl_aml_resource_serial_bus_sizes[] = { + 0, + sizeof(struct aml_resource_i2c_serialbus), + sizeof(struct aml_resource_spi_serialbus), + sizeof(struct aml_resource_uart_serialbus), +}; + +const u8 acpi_gbl_resource_struct_serial_bus_sizes[] = { + 0, + ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus), + ACPI_RS_SIZE(struct acpi_resource_spi_serialbus), + ACPI_RS_SIZE(struct acpi_resource_uart_serialbus), +}; diff --git a/kernel/drivers/acpi/acpica/rsio.c b/kernel/drivers/acpi/acpica/rsio.c new file mode 100644 index 000000000..5adba018b --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsio.c @@ -0,0 +1,290 @@ +/******************************************************************************* + * + * Module Name: rsio - IO and DMA resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsio") + +/******************************************************************************* + * + * acpi_rs_convert_io + * + ******************************************************************************/ +struct acpi_rsconvert_info acpi_rs_convert_io[5] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_IO, + ACPI_RS_SIZE(struct acpi_resource_io), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_io)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_IO, + sizeof(struct aml_resource_io), + 0}, + + /* Decode flag */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.io.io_decode), + AML_OFFSET(io.flags), + 0}, + /* + * These fields are contiguous in both the source and destination: + * Address Alignment + * Length + * Minimum Base Address + * Maximum Base Address + */ + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.io.alignment), + AML_OFFSET(io.alignment), + 2}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.io.minimum), + AML_OFFSET(io.minimum), + 2} +}; + +/******************************************************************************* + * + * acpi_rs_convert_fixed_io + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_fixed_io[4] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_IO, + ACPI_RS_SIZE(struct acpi_resource_fixed_io), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_fixed_io)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_IO, + sizeof(struct aml_resource_fixed_io), + 0}, + /* + * These fields are contiguous in both the source and destination: + * Base Address + * Length + */ + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.fixed_io.address_length), + AML_OFFSET(fixed_io.address_length), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.fixed_io.address), + AML_OFFSET(fixed_io.address), + 1} +}; + +/******************************************************************************* + * + * acpi_rs_convert_generic_reg + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_generic_reg[4] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_GENERIC_REGISTER, + ACPI_RS_SIZE(struct acpi_resource_generic_register), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_generic_reg)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_GENERIC_REGISTER, + sizeof(struct aml_resource_generic_register), + 0}, + /* + * These fields are contiguous in both the source and destination: + * Address Space ID + * Register Bit Width + * Register Bit Offset + * Access Size + */ + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.generic_reg.space_id), + AML_OFFSET(generic_reg.address_space_id), + 4}, + + /* Get the Register Address */ + + {ACPI_RSC_MOVE64, ACPI_RS_OFFSET(data.generic_reg.address), + AML_OFFSET(generic_reg.address), + 1} +}; + +/******************************************************************************* + * + * acpi_rs_convert_end_dpf + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_end_dpf[2] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_END_DEPENDENT, + ACPI_RS_SIZE_MIN, + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_end_dpf)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_END_DEPENDENT, + sizeof(struct aml_resource_end_dependent), + 0} +}; + +/******************************************************************************* + * + * acpi_rs_convert_end_tag + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_end_tag[2] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_END_TAG, + ACPI_RS_SIZE_MIN, + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_end_tag)}, + + /* + * Note: The checksum field is set to zero, meaning that the resource + * data is treated as if the checksum operation succeeded. + * (ACPI Spec 1.0b Section 6.4.2.8) + */ + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_END_TAG, + sizeof(struct aml_resource_end_tag), + 0} +}; + +/******************************************************************************* + * + * acpi_rs_get_start_dpf + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_get_start_dpf[6] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_START_DEPENDENT, + ACPI_RS_SIZE(struct acpi_resource_start_dependent), + ACPI_RSC_TABLE_SIZE(acpi_rs_get_start_dpf)}, + + /* Defaults for Compatibility and Performance priorities */ + + {ACPI_RSC_SET8, ACPI_RS_OFFSET(data.start_dpf.compatibility_priority), + ACPI_ACCEPTABLE_CONFIGURATION, + 2}, + + /* Get the descriptor length (0 or 1 for Start Dpf descriptor) */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.start_dpf.descriptor_length), + AML_OFFSET(start_dpf.descriptor_type), + 0}, + + /* All done if there is no flag byte present in the descriptor */ + + {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_AML_LENGTH, 0, 1}, + + /* Flag byte is present, get the flags */ + + {ACPI_RSC_2BITFLAG, + ACPI_RS_OFFSET(data.start_dpf.compatibility_priority), + AML_OFFSET(start_dpf.flags), + 0}, + + {ACPI_RSC_2BITFLAG, + ACPI_RS_OFFSET(data.start_dpf.performance_robustness), + AML_OFFSET(start_dpf.flags), + 2} +}; + +/******************************************************************************* + * + * acpi_rs_set_start_dpf + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_set_start_dpf[10] = { + /* Start with a default descriptor of length 1 */ + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_START_DEPENDENT, + sizeof(struct aml_resource_start_dependent), + ACPI_RSC_TABLE_SIZE(acpi_rs_set_start_dpf)}, + + /* Set the default flag values */ + + {ACPI_RSC_2BITFLAG, + ACPI_RS_OFFSET(data.start_dpf.compatibility_priority), + AML_OFFSET(start_dpf.flags), + 0}, + + {ACPI_RSC_2BITFLAG, + ACPI_RS_OFFSET(data.start_dpf.performance_robustness), + AML_OFFSET(start_dpf.flags), + 2}, + /* + * All done if the output descriptor length is required to be 1 + * (i.e., optimization to 0 bytes cannot be attempted) + */ + {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.start_dpf.descriptor_length), + 1}, + + /* Set length to 0 bytes (no flags byte) */ + + {ACPI_RSC_LENGTH, 0, 0, + sizeof(struct aml_resource_start_dependent_noprio)}, + + /* + * All done if the output descriptor length is required to be 0. + * + * TBD: Perhaps we should check for error if input flags are not + * compatible with a 0-byte descriptor. + */ + {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.start_dpf.descriptor_length), + 0}, + + /* Reset length to 1 byte (descriptor with flags byte) */ + + {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_start_dependent)}, + + /* + * All done if flags byte is necessary -- if either priority value + * is not ACPI_ACCEPTABLE_CONFIGURATION + */ + {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.start_dpf.compatibility_priority), + ACPI_ACCEPTABLE_CONFIGURATION}, + + {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.start_dpf.performance_robustness), + ACPI_ACCEPTABLE_CONFIGURATION}, + + /* Flag byte is not necessary */ + + {ACPI_RSC_LENGTH, 0, 0, + sizeof(struct aml_resource_start_dependent_noprio)} +}; diff --git a/kernel/drivers/acpi/acpica/rsirq.c b/kernel/drivers/acpi/acpica/rsirq.c new file mode 100644 index 000000000..07cfa70a4 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsirq.c @@ -0,0 +1,307 @@ +/******************************************************************************* + * + * Module Name: rsirq - IRQ resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsirq") + +/******************************************************************************* + * + * acpi_rs_get_irq + * + ******************************************************************************/ +struct acpi_rsconvert_info acpi_rs_get_irq[9] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_IRQ, + ACPI_RS_SIZE(struct acpi_resource_irq), + ACPI_RSC_TABLE_SIZE(acpi_rs_get_irq)}, + + /* Get the IRQ mask (bytes 1:2) */ + + {ACPI_RSC_BITMASK16, ACPI_RS_OFFSET(data.irq.interrupts[0]), + AML_OFFSET(irq.irq_mask), + ACPI_RS_OFFSET(data.irq.interrupt_count)}, + + /* Set default flags (others are zero) */ + + {ACPI_RSC_SET8, ACPI_RS_OFFSET(data.irq.triggering), + ACPI_EDGE_SENSITIVE, + 1}, + + /* Get the descriptor length (2 or 3 for IRQ descriptor) */ + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.irq.descriptor_length), + AML_OFFSET(irq.descriptor_type), + 0}, + + /* All done if no flag byte present in descriptor */ + + {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_AML_LENGTH, 0, 3}, + + /* Get flags: Triggering[0], Polarity[3], Sharing[4], Wake[5] */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.triggering), + AML_OFFSET(irq.flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.polarity), + AML_OFFSET(irq.flags), + 3}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.sharable), + AML_OFFSET(irq.flags), + 4}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.wake_capable), + AML_OFFSET(irq.flags), + 5} +}; + +/******************************************************************************* + * + * acpi_rs_set_irq + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_set_irq[14] = { + /* Start with a default descriptor of length 3 */ + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_IRQ, + sizeof(struct aml_resource_irq), + ACPI_RSC_TABLE_SIZE(acpi_rs_set_irq)}, + + /* Convert interrupt list to 16-bit IRQ bitmask */ + + {ACPI_RSC_BITMASK16, ACPI_RS_OFFSET(data.irq.interrupts[0]), + AML_OFFSET(irq.irq_mask), + ACPI_RS_OFFSET(data.irq.interrupt_count)}, + + /* Set flags: Triggering[0], Polarity[3], Sharing[4], Wake[5] */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.triggering), + AML_OFFSET(irq.flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.polarity), + AML_OFFSET(irq.flags), + 3}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.sharable), + AML_OFFSET(irq.flags), + 4}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.irq.wake_capable), + AML_OFFSET(irq.flags), + 5}, + + /* + * All done if the output descriptor length is required to be 3 + * (i.e., optimization to 2 bytes cannot be attempted) + */ + {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.irq.descriptor_length), + 3}, + + /* Set length to 2 bytes (no flags byte) */ + + {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_irq_noflags)}, + + /* + * All done if the output descriptor length is required to be 2. + * + * TBD: Perhaps we should check for error if input flags are not + * compatible with a 2-byte descriptor. + */ + {ACPI_RSC_EXIT_EQ, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.irq.descriptor_length), + 2}, + + /* Reset length to 3 bytes (descriptor with flags byte) */ + + {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_irq)}, + + /* + * Check if the flags byte is necessary. Not needed if the flags are: + * ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH, ACPI_EXCLUSIVE + */ + {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.irq.triggering), + ACPI_EDGE_SENSITIVE}, + + {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.irq.polarity), + ACPI_ACTIVE_HIGH}, + + {ACPI_RSC_EXIT_NE, ACPI_RSC_COMPARE_VALUE, + ACPI_RS_OFFSET(data.irq.sharable), + ACPI_EXCLUSIVE}, + + /* We can optimize to a 2-byte irq_no_flags() descriptor */ + + {ACPI_RSC_LENGTH, 0, 0, sizeof(struct aml_resource_irq_noflags)} +}; + +/******************************************************************************* + * + * acpi_rs_convert_ext_irq + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_ext_irq[10] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_EXTENDED_IRQ, + ACPI_RS_SIZE(struct acpi_resource_extended_irq), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_ext_irq)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_EXTENDED_IRQ, + sizeof(struct aml_resource_extended_irq), + 0}, + + /* + * Flags: Producer/Consumer[0], Triggering[1], Polarity[2], + * Sharing[3], Wake[4] + */ + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.producer_consumer), + AML_OFFSET(extended_irq.flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.triggering), + AML_OFFSET(extended_irq.flags), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.polarity), + AML_OFFSET(extended_irq.flags), + 2}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.sharable), + AML_OFFSET(extended_irq.flags), + 3}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.extended_irq.wake_capable), + AML_OFFSET(extended_irq.flags), + 4}, + + /* IRQ Table length (Byte4) */ + + {ACPI_RSC_COUNT, ACPI_RS_OFFSET(data.extended_irq.interrupt_count), + AML_OFFSET(extended_irq.interrupt_count), + sizeof(u32)}, + + /* Copy every IRQ in the table, each is 32 bits */ + + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.extended_irq.interrupts[0]), + AML_OFFSET(extended_irq.interrupts[0]), + 0}, + + /* Optional resource_source (Index and String) */ + + {ACPI_RSC_SOURCEX, ACPI_RS_OFFSET(data.extended_irq.resource_source), + ACPI_RS_OFFSET(data.extended_irq.interrupts[0]), + sizeof(struct aml_resource_extended_irq)} +}; + +/******************************************************************************* + * + * acpi_rs_convert_dma + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_dma[6] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_DMA, + ACPI_RS_SIZE(struct acpi_resource_dma), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_dma)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_DMA, + sizeof(struct aml_resource_dma), + 0}, + + /* Flags: transfer preference, bus mastering, channel speed */ + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.dma.transfer), + AML_OFFSET(dma.flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.dma.bus_master), + AML_OFFSET(dma.flags), + 2}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.dma.type), + AML_OFFSET(dma.flags), + 5}, + + /* DMA channel mask bits */ + + {ACPI_RSC_BITMASK, ACPI_RS_OFFSET(data.dma.channels[0]), + AML_OFFSET(dma.dma_channel_mask), + ACPI_RS_OFFSET(data.dma.channel_count)} +}; + +/******************************************************************************* + * + * acpi_rs_convert_fixed_dma + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_fixed_dma[4] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_DMA, + ACPI_RS_SIZE(struct acpi_resource_fixed_dma), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_fixed_dma)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_DMA, + sizeof(struct aml_resource_fixed_dma), + 0}, + + /* + * These fields are contiguous in both the source and destination: + * request_lines + * Channels + */ + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.fixed_dma.request_lines), + AML_OFFSET(fixed_dma.request_lines), + 2}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.fixed_dma.width), + AML_OFFSET(fixed_dma.width), + 1}, +}; diff --git a/kernel/drivers/acpi/acpica/rslist.c b/kernel/drivers/acpi/acpica/rslist.c new file mode 100644 index 000000000..50d5be2ee --- /dev/null +++ b/kernel/drivers/acpi/acpica/rslist.c @@ -0,0 +1,259 @@ +/******************************************************************************* + * + * Module Name: rslist - Linked list utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rslist") + +/******************************************************************************* + * + * FUNCTION: acpi_rs_convert_aml_to_resources + * + * PARAMETERS: acpi_walk_aml_callback + * resource_ptr - Pointer to the buffer that will + * contain the output structures + * + * RETURN: Status + * + * DESCRIPTION: Convert an AML resource to an internal representation of the + * resource that is aligned and easier to access. + * + ******************************************************************************/ +acpi_status +acpi_rs_convert_aml_to_resources(u8 * aml, + u32 length, + u32 offset, u8 resource_index, void **context) +{ + struct acpi_resource **resource_ptr = + ACPI_CAST_INDIRECT_PTR(struct acpi_resource, context); + struct acpi_resource *resource; + union aml_resource *aml_resource; + struct acpi_rsconvert_info *conversion_table; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_convert_aml_to_resources); + + /* + * Check that the input buffer and all subsequent pointers into it + * are aligned on a native word boundary. Most important on IA64 + */ + resource = *resource_ptr; + if (ACPI_IS_MISALIGNED(resource)) { + ACPI_WARNING((AE_INFO, + "Misaligned resource pointer %p", resource)); + } + + /* Get the appropriate conversion info table */ + + aml_resource = ACPI_CAST_PTR(union aml_resource, aml); + if (acpi_ut_get_resource_type(aml) == ACPI_RESOURCE_NAME_SERIAL_BUS) { + if (aml_resource->common_serial_bus.type > + AML_RESOURCE_MAX_SERIALBUSTYPE) { + conversion_table = NULL; + } else { + /* This is an I2C, SPI, or UART serial_bus descriptor */ + + conversion_table = + acpi_gbl_convert_resource_serial_bus_dispatch + [aml_resource->common_serial_bus.type]; + } + } else { + conversion_table = + acpi_gbl_get_resource_dispatch[resource_index]; + } + + if (!conversion_table) { + ACPI_ERROR((AE_INFO, + "Invalid/unsupported resource descriptor: Type 0x%2.2X", + resource_index)); + return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE); + } + + /* Convert the AML byte stream resource to a local resource struct */ + + status = + acpi_rs_convert_aml_to_resource(resource, aml_resource, + conversion_table); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not convert AML resource (Type 0x%X)", + *aml)); + return_ACPI_STATUS(status); + } + + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, + "Type %.2X, AmlLength %.2X InternalLength %.2X\n", + acpi_ut_get_resource_type(aml), length, + resource->length)); + + /* Point to the next structure in the output buffer */ + + *resource_ptr = ACPI_NEXT_RESOURCE(resource); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_convert_resources_to_aml + * + * PARAMETERS: resource - Pointer to the resource linked list + * aml_size_needed - Calculated size of the byte stream + * needed from calling acpi_rs_get_aml_length() + * The size of the output_buffer is + * guaranteed to be >= aml_size_needed + * output_buffer - Pointer to the buffer that will + * contain the byte stream + * + * RETURN: Status + * + * DESCRIPTION: Takes the resource linked list and parses it, creating a + * byte stream of resources in the caller's output buffer + * + ******************************************************************************/ + +acpi_status +acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, + acpi_size aml_size_needed, u8 * output_buffer) +{ + u8 *aml = output_buffer; + u8 *end_aml = output_buffer + aml_size_needed; + struct acpi_rsconvert_info *conversion_table; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_convert_resources_to_aml); + + /* Walk the resource descriptor list, convert each descriptor */ + + while (aml < end_aml) { + + /* Validate the (internal) Resource Type */ + + if (resource->type > ACPI_RESOURCE_TYPE_MAX) { + ACPI_ERROR((AE_INFO, + "Invalid descriptor type (0x%X) in resource list", + resource->type)); + return_ACPI_STATUS(AE_BAD_DATA); + } + + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource->length) { + ACPI_ERROR((AE_INFO, + "Invalid zero length descriptor in resource list\n")); + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); + } + + /* Perform the conversion */ + + if (resource->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + if (resource->data.common_serial_bus.type > + AML_RESOURCE_MAX_SERIALBUSTYPE) { + conversion_table = NULL; + } else { + /* This is an I2C, SPI, or UART serial_bus descriptor */ + + conversion_table = + acpi_gbl_convert_resource_serial_bus_dispatch + [resource->data.common_serial_bus.type]; + } + } else { + conversion_table = + acpi_gbl_set_resource_dispatch[resource->type]; + } + + if (!conversion_table) { + ACPI_ERROR((AE_INFO, + "Invalid/unsupported resource descriptor: Type 0x%2.2X", + resource->type)); + return_ACPI_STATUS(AE_AML_INVALID_RESOURCE_TYPE); + } + + status = acpi_rs_convert_resource_to_aml(resource, + ACPI_CAST_PTR(union + aml_resource, + aml), + conversion_table); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not convert resource (type 0x%X) to AML", + resource->type)); + return_ACPI_STATUS(status); + } + + /* Perform final sanity check on the new AML resource descriptor */ + + status = acpi_ut_validate_resource(NULL, + ACPI_CAST_PTR(union + aml_resource, + aml), NULL); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Check for end-of-list, normal exit */ + + if (resource->type == ACPI_RESOURCE_TYPE_END_TAG) { + + /* An End Tag indicates the end of the input Resource Template */ + + return_ACPI_STATUS(AE_OK); + } + + /* + * Extract the total length of the new descriptor and set the + * Aml to point to the next (output) resource descriptor + */ + aml += acpi_ut_get_descriptor_length(aml); + + /* Point to the next input resource descriptor */ + + resource = ACPI_NEXT_RESOURCE(resource); + } + + /* Completed buffer, but did not find an end_tag resource descriptor */ + + return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG); +} diff --git a/kernel/drivers/acpi/acpica/rsmemory.c b/kernel/drivers/acpi/acpica/rsmemory.c new file mode 100644 index 000000000..c6b808620 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsmemory.c @@ -0,0 +1,234 @@ +/******************************************************************************* + * + * Module Name: rsmem24 - Memory resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsmemory") + +/******************************************************************************* + * + * acpi_rs_convert_memory24 + * + ******************************************************************************/ +struct acpi_rsconvert_info acpi_rs_convert_memory24[4] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_MEMORY24, + ACPI_RS_SIZE(struct acpi_resource_memory24), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_memory24)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_MEMORY24, + sizeof(struct aml_resource_memory24), + 0}, + + /* Read/Write bit */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.memory24.write_protect), + AML_OFFSET(memory24.flags), + 0}, + /* + * These fields are contiguous in both the source and destination: + * Minimum Base Address + * Maximum Base Address + * Address Base Alignment + * Range Length + */ + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.memory24.minimum), + AML_OFFSET(memory24.minimum), + 4} +}; + +/******************************************************************************* + * + * acpi_rs_convert_memory32 + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_memory32[4] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_MEMORY32, + ACPI_RS_SIZE(struct acpi_resource_memory32), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_memory32)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_MEMORY32, + sizeof(struct aml_resource_memory32), + 0}, + + /* Read/Write bit */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.memory32.write_protect), + AML_OFFSET(memory32.flags), + 0}, + /* + * These fields are contiguous in both the source and destination: + * Minimum Base Address + * Maximum Base Address + * Address Base Alignment + * Range Length + */ + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.memory32.minimum), + AML_OFFSET(memory32.minimum), + 4} +}; + +/******************************************************************************* + * + * acpi_rs_convert_fixed_memory32 + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_fixed_memory32[4] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_FIXED_MEMORY32, + ACPI_RS_SIZE(struct acpi_resource_fixed_memory32), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_fixed_memory32)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_FIXED_MEMORY32, + sizeof(struct aml_resource_fixed_memory32), + 0}, + + /* Read/Write bit */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.fixed_memory32.write_protect), + AML_OFFSET(fixed_memory32.flags), + 0}, + /* + * These fields are contiguous in both the source and destination: + * Base Address + * Range Length + */ + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.fixed_memory32.address), + AML_OFFSET(fixed_memory32.address), + 2} +}; + +/******************************************************************************* + * + * acpi_rs_get_vendor_small + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_get_vendor_small[3] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_VENDOR, + ACPI_RS_SIZE(struct acpi_resource_vendor), + ACPI_RSC_TABLE_SIZE(acpi_rs_get_vendor_small)}, + + /* Length of the vendor data (byte count) */ + + {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length), + 0, + sizeof(u8)}, + + /* Vendor data */ + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]), + sizeof(struct aml_resource_small_header), + 0} +}; + +/******************************************************************************* + * + * acpi_rs_get_vendor_large + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_get_vendor_large[3] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_VENDOR, + ACPI_RS_SIZE(struct acpi_resource_vendor), + ACPI_RSC_TABLE_SIZE(acpi_rs_get_vendor_large)}, + + /* Length of the vendor data (byte count) */ + + {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length), + 0, + sizeof(u8)}, + + /* Vendor data */ + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]), + sizeof(struct aml_resource_large_header), + 0} +}; + +/******************************************************************************* + * + * acpi_rs_set_vendor + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_set_vendor[7] = { + /* Default is a small vendor descriptor */ + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_VENDOR_SMALL, + sizeof(struct aml_resource_small_header), + ACPI_RSC_TABLE_SIZE(acpi_rs_set_vendor)}, + + /* Get the length and copy the data */ + + {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length), + 0, + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]), + sizeof(struct aml_resource_small_header), + 0}, + + /* + * All done if the Vendor byte length is 7 or less, meaning that it will + * fit within a small descriptor + */ + {ACPI_RSC_EXIT_LE, 0, 0, 7}, + + /* Must create a large vendor descriptor */ + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_VENDOR_LARGE, + sizeof(struct aml_resource_large_header), + 0}, + + {ACPI_RSC_COUNT16, ACPI_RS_OFFSET(data.vendor.byte_length), + 0, + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.vendor.byte_data[0]), + sizeof(struct aml_resource_large_header), + 0} +}; diff --git a/kernel/drivers/acpi/acpica/rsmisc.c b/kernel/drivers/acpi/acpica/rsmisc.c new file mode 100644 index 000000000..1fe49d223 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsmisc.c @@ -0,0 +1,825 @@ +/******************************************************************************* + * + * Module Name: rsmisc - Miscellaneous resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsmisc") +#define INIT_RESOURCE_TYPE(i) i->resource_offset +#define INIT_RESOURCE_LENGTH(i) i->aml_offset +#define INIT_TABLE_LENGTH(i) i->value +#define COMPARE_OPCODE(i) i->resource_offset +#define COMPARE_TARGET(i) i->aml_offset +#define COMPARE_VALUE(i) i->value +/******************************************************************************* + * + * FUNCTION: acpi_rs_convert_aml_to_resource + * + * PARAMETERS: resource - Pointer to the resource descriptor + * aml - Where the AML descriptor is returned + * info - Pointer to appropriate conversion table + * + * RETURN: Status + * + * DESCRIPTION: Convert an external AML resource descriptor to the corresponding + * internal resource descriptor + * + ******************************************************************************/ +acpi_status +acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, + union aml_resource *aml, + struct acpi_rsconvert_info *info) +{ + acpi_rs_length aml_resource_length; + void *source; + void *destination; + char *target; + u8 count; + u8 flags_mode = FALSE; + u16 item_count = 0; + u16 temp16 = 0; + + ACPI_FUNCTION_TRACE(rs_convert_aml_to_resource); + + if (!info) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (((acpi_size) resource) & 0x3) { + + /* Each internal resource struct is expected to be 32-bit aligned */ + + ACPI_WARNING((AE_INFO, + "Misaligned resource pointer (get): %p Type 0x%2.2X Length %u", + resource, resource->type, resource->length)); + } + + /* Extract the resource Length field (does not include header length) */ + + aml_resource_length = acpi_ut_get_resource_length(aml); + + /* + * First table entry must be ACPI_RSC_INITxxx and must contain the + * table length (# of table entries) + */ + count = INIT_TABLE_LENGTH(info); + while (count) { + /* + * Source is the external AML byte stream buffer, + * destination is the internal resource descriptor + */ + source = ACPI_ADD_PTR(void, aml, info->aml_offset); + destination = + ACPI_ADD_PTR(void, resource, info->resource_offset); + + switch (info->opcode) { + case ACPI_RSC_INITGET: + /* + * Get the resource type and the initial (minimum) length + */ + ACPI_MEMSET(resource, 0, INIT_RESOURCE_LENGTH(info)); + resource->type = INIT_RESOURCE_TYPE(info); + resource->length = INIT_RESOURCE_LENGTH(info); + break; + + case ACPI_RSC_INITSET: + break; + + case ACPI_RSC_FLAGINIT: + + flags_mode = TRUE; + break; + + case ACPI_RSC_1BITFLAG: + /* + * Mask and shift the flag bit + */ + ACPI_SET8(destination, + ((ACPI_GET8(source) >> info->value) & 0x01)); + break; + + case ACPI_RSC_2BITFLAG: + /* + * Mask and shift the flag bits + */ + ACPI_SET8(destination, + ((ACPI_GET8(source) >> info->value) & 0x03)); + break; + + case ACPI_RSC_3BITFLAG: + /* + * Mask and shift the flag bits + */ + ACPI_SET8(destination, + ((ACPI_GET8(source) >> info->value) & 0x07)); + break; + + case ACPI_RSC_COUNT: + + item_count = ACPI_GET8(source); + ACPI_SET8(destination, item_count); + + resource->length = resource->length + + (info->value * (item_count - 1)); + break; + + case ACPI_RSC_COUNT16: + + item_count = aml_resource_length; + ACPI_SET16(destination, item_count); + + resource->length = resource->length + + (info->value * (item_count - 1)); + break; + + case ACPI_RSC_COUNT_GPIO_PIN: + + target = ACPI_ADD_PTR(void, aml, info->value); + item_count = ACPI_GET16(target) - ACPI_GET16(source); + + resource->length = resource->length + item_count; + item_count = item_count / 2; + ACPI_SET16(destination, item_count); + break; + + case ACPI_RSC_COUNT_GPIO_VEN: + + item_count = ACPI_GET8(source); + ACPI_SET8(destination, item_count); + + resource->length = resource->length + + (info->value * item_count); + break; + + case ACPI_RSC_COUNT_GPIO_RES: + /* + * Vendor data is optional (length/offset may both be zero) + * Examine vendor data length field first + */ + target = ACPI_ADD_PTR(void, aml, (info->value + 2)); + if (ACPI_GET16(target)) { + + /* Use vendor offset to get resource source length */ + + target = ACPI_ADD_PTR(void, aml, info->value); + item_count = + ACPI_GET16(target) - ACPI_GET16(source); + } else { + /* No vendor data to worry about */ + + item_count = aml->large_header.resource_length + + sizeof(struct aml_resource_large_header) - + ACPI_GET16(source); + } + + resource->length = resource->length + item_count; + ACPI_SET16(destination, item_count); + break; + + case ACPI_RSC_COUNT_SERIAL_VEN: + + item_count = ACPI_GET16(source) - info->value; + + resource->length = resource->length + item_count; + ACPI_SET16(destination, item_count); + break; + + case ACPI_RSC_COUNT_SERIAL_RES: + + item_count = (aml_resource_length + + sizeof(struct aml_resource_large_header)) + - ACPI_GET16(source) - info->value; + + resource->length = resource->length + item_count; + ACPI_SET16(destination, item_count); + break; + + case ACPI_RSC_LENGTH: + + resource->length = resource->length + info->value; + break; + + case ACPI_RSC_MOVE8: + case ACPI_RSC_MOVE16: + case ACPI_RSC_MOVE32: + case ACPI_RSC_MOVE64: + /* + * Raw data move. Use the Info value field unless item_count has + * been previously initialized via a COUNT opcode + */ + if (info->value) { + item_count = info->value; + } + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_GPIO_PIN: + + /* Generate and set the PIN data pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count * 2)); + *(u16 **)destination = ACPI_CAST_PTR(u16, target); + + /* Copy the PIN data */ + + source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_GPIO_RES: + + /* Generate and set the resource_source string pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count)); + *(u8 **)destination = ACPI_CAST_PTR(u8, target); + + /* Copy the resource_source string */ + + source = ACPI_ADD_PTR(void, aml, ACPI_GET16(source)); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_VEN: + + /* Generate and set the Vendor Data pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count)); + *(u8 **)destination = ACPI_CAST_PTR(u8, target); + + /* Copy the Vendor Data */ + + source = ACPI_ADD_PTR(void, aml, info->value); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_RES: + + /* Generate and set the resource_source string pointer */ + + target = (char *)ACPI_ADD_PTR(void, resource, + (resource->length - + item_count)); + *(u8 **)destination = ACPI_CAST_PTR(u8, target); + + /* Copy the resource_source string */ + + source = + ACPI_ADD_PTR(void, aml, + (ACPI_GET16(source) + info->value)); + acpi_rs_move_data(target, source, item_count, + info->opcode); + break; + + case ACPI_RSC_SET8: + + ACPI_MEMSET(destination, info->aml_offset, info->value); + break; + + case ACPI_RSC_DATA8: + + target = ACPI_ADD_PTR(char, resource, info->value); + ACPI_MEMCPY(destination, source, ACPI_GET16(target)); + break; + + case ACPI_RSC_ADDRESS: + /* + * Common handler for address descriptor flags + */ + if (!acpi_rs_get_address_common(resource, aml)) { + return_ACPI_STATUS + (AE_AML_INVALID_RESOURCE_TYPE); + } + break; + + case ACPI_RSC_SOURCE: + /* + * Optional resource_source (Index and String) + */ + resource->length += + acpi_rs_get_resource_source(aml_resource_length, + info->value, + destination, aml, NULL); + break; + + case ACPI_RSC_SOURCEX: + /* + * Optional resource_source (Index and String). This is the more + * complicated case used by the Interrupt() macro + */ + target = ACPI_ADD_PTR(char, resource, + info->aml_offset + + (item_count * 4)); + + resource->length += + acpi_rs_get_resource_source(aml_resource_length, + (acpi_rs_length) + (((item_count - + 1) * sizeof(u32)) + + info->value), + destination, aml, + target); + break; + + case ACPI_RSC_BITMASK: + /* + * 8-bit encoded bitmask (DMA macro) + */ + item_count = + acpi_rs_decode_bitmask(ACPI_GET8(source), + destination); + if (item_count) { + resource->length += (item_count - 1); + } + + target = ACPI_ADD_PTR(char, resource, info->value); + ACPI_SET8(target, item_count); + break; + + case ACPI_RSC_BITMASK16: + /* + * 16-bit encoded bitmask (IRQ macro) + */ + ACPI_MOVE_16_TO_16(&temp16, source); + + item_count = + acpi_rs_decode_bitmask(temp16, destination); + if (item_count) { + resource->length += (item_count - 1); + } + + target = ACPI_ADD_PTR(char, resource, info->value); + ACPI_SET8(target, item_count); + break; + + case ACPI_RSC_EXIT_NE: + /* + * control - Exit conversion if not equal + */ + switch (info->resource_offset) { + case ACPI_RSC_COMPARE_AML_LENGTH: + + if (aml_resource_length != info->value) { + goto exit; + } + break; + + case ACPI_RSC_COMPARE_VALUE: + + if (ACPI_GET8(source) != info->value) { + goto exit; + } + break; + + default: + + ACPI_ERROR((AE_INFO, + "Invalid conversion sub-opcode")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Invalid conversion opcode")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + count--; + info++; + } + +exit: + if (!flags_mode) { + + /* Round the resource struct length up to the next boundary (32 or 64) */ + + resource->length = + (u32) ACPI_ROUND_UP_TO_NATIVE_WORD(resource->length); + } + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_convert_resource_to_aml + * + * PARAMETERS: resource - Pointer to the resource descriptor + * aml - Where the AML descriptor is returned + * info - Pointer to appropriate conversion table + * + * RETURN: Status + * + * DESCRIPTION: Convert an internal resource descriptor to the corresponding + * external AML resource descriptor. + * + ******************************************************************************/ + +acpi_status +acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, + union aml_resource *aml, + struct acpi_rsconvert_info *info) +{ + void *source = NULL; + void *destination; + char *target; + acpi_rsdesc_size aml_length = 0; + u8 count; + u16 temp16 = 0; + u16 item_count = 0; + + ACPI_FUNCTION_TRACE(rs_convert_resource_to_aml); + + if (!info) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * First table entry must be ACPI_RSC_INITxxx and must contain the + * table length (# of table entries) + */ + count = INIT_TABLE_LENGTH(info); + + while (count) { + /* + * Source is the internal resource descriptor, + * destination is the external AML byte stream buffer + */ + source = ACPI_ADD_PTR(void, resource, info->resource_offset); + destination = ACPI_ADD_PTR(void, aml, info->aml_offset); + + switch (info->opcode) { + case ACPI_RSC_INITSET: + + ACPI_MEMSET(aml, 0, INIT_RESOURCE_LENGTH(info)); + aml_length = INIT_RESOURCE_LENGTH(info); + acpi_rs_set_resource_header(INIT_RESOURCE_TYPE(info), + aml_length, aml); + break; + + case ACPI_RSC_INITGET: + break; + + case ACPI_RSC_FLAGINIT: + /* + * Clear the flag byte + */ + ACPI_SET8(destination, 0); + break; + + case ACPI_RSC_1BITFLAG: + /* + * Mask and shift the flag bit + */ + ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) + ((ACPI_GET8(source) & 0x01) << info-> + value)); + break; + + case ACPI_RSC_2BITFLAG: + /* + * Mask and shift the flag bits + */ + ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) + ((ACPI_GET8(source) & 0x03) << info-> + value)); + break; + + case ACPI_RSC_3BITFLAG: + /* + * Mask and shift the flag bits + */ + ACPI_SET_BIT(*ACPI_CAST8(destination), (u8) + ((ACPI_GET8(source) & 0x07) << info-> + value)); + break; + + case ACPI_RSC_COUNT: + + item_count = ACPI_GET8(source); + ACPI_SET8(destination, item_count); + + aml_length = + (u16) (aml_length + + (info->value * (item_count - 1))); + break; + + case ACPI_RSC_COUNT16: + + item_count = ACPI_GET16(source); + aml_length = (u16) (aml_length + item_count); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_GPIO_PIN: + + item_count = ACPI_GET16(source); + ACPI_SET16(destination, aml_length); + + aml_length = (u16)(aml_length + item_count * 2); + target = ACPI_ADD_PTR(void, aml, info->value); + ACPI_SET16(target, aml_length); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_GPIO_VEN: + + item_count = ACPI_GET16(source); + ACPI_SET16(destination, item_count); + + aml_length = + (u16)(aml_length + (info->value * item_count)); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_GPIO_RES: + + /* Set resource source string length */ + + item_count = ACPI_GET16(source); + ACPI_SET16(destination, aml_length); + + /* Compute offset for the Vendor Data */ + + aml_length = (u16)(aml_length + item_count); + target = ACPI_ADD_PTR(void, aml, info->value); + + /* Set vendor offset only if there is vendor data */ + + if (resource->data.gpio.vendor_length) { + ACPI_SET16(target, aml_length); + } + + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_SERIAL_VEN: + + item_count = ACPI_GET16(source); + ACPI_SET16(destination, item_count + info->value); + aml_length = (u16)(aml_length + item_count); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_COUNT_SERIAL_RES: + + item_count = ACPI_GET16(source); + aml_length = (u16)(aml_length + item_count); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_LENGTH: + + acpi_rs_set_resource_length(info->value, aml); + break; + + case ACPI_RSC_MOVE8: + case ACPI_RSC_MOVE16: + case ACPI_RSC_MOVE32: + case ACPI_RSC_MOVE64: + + if (info->value) { + item_count = info->value; + } + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_GPIO_PIN: + + destination = (char *)ACPI_ADD_PTR(void, aml, + ACPI_GET16 + (destination)); + source = *(u16 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_GPIO_RES: + + /* Used for both resource_source string and vendor_data */ + + destination = (char *)ACPI_ADD_PTR(void, aml, + ACPI_GET16 + (destination)); + source = *(u8 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_VEN: + + destination = (char *)ACPI_ADD_PTR(void, aml, + (aml_length - + item_count)); + source = *(u8 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_MOVE_SERIAL_RES: + + destination = (char *)ACPI_ADD_PTR(void, aml, + (aml_length - + item_count)); + source = *(u8 **)source; + acpi_rs_move_data(destination, source, item_count, + info->opcode); + break; + + case ACPI_RSC_ADDRESS: + + /* Set the Resource Type, General Flags, and Type-Specific Flags */ + + acpi_rs_set_address_common(aml, resource); + break; + + case ACPI_RSC_SOURCEX: + /* + * Optional resource_source (Index and String) + */ + aml_length = + acpi_rs_set_resource_source(aml, + (acpi_rs_length) + aml_length, source); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_SOURCE: + /* + * Optional resource_source (Index and String). This is the more + * complicated case used by the Interrupt() macro + */ + aml_length = + acpi_rs_set_resource_source(aml, info->value, + source); + acpi_rs_set_resource_length(aml_length, aml); + break; + + case ACPI_RSC_BITMASK: + /* + * 8-bit encoded bitmask (DMA macro) + */ + ACPI_SET8(destination, + acpi_rs_encode_bitmask(source, + *ACPI_ADD_PTR(u8, + resource, + info-> + value))); + break; + + case ACPI_RSC_BITMASK16: + /* + * 16-bit encoded bitmask (IRQ macro) + */ + temp16 = acpi_rs_encode_bitmask(source, + *ACPI_ADD_PTR(u8, + resource, + info-> + value)); + ACPI_MOVE_16_TO_16(destination, &temp16); + break; + + case ACPI_RSC_EXIT_LE: + /* + * control - Exit conversion if less than or equal + */ + if (item_count <= info->value) { + goto exit; + } + break; + + case ACPI_RSC_EXIT_NE: + /* + * control - Exit conversion if not equal + */ + switch (COMPARE_OPCODE(info)) { + case ACPI_RSC_COMPARE_VALUE: + + if (*ACPI_ADD_PTR(u8, resource, + COMPARE_TARGET(info)) != + COMPARE_VALUE(info)) { + goto exit; + } + break; + + default: + + ACPI_ERROR((AE_INFO, + "Invalid conversion sub-opcode")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + break; + + case ACPI_RSC_EXIT_EQ: + /* + * control - Exit conversion if equal + */ + if (*ACPI_ADD_PTR(u8, resource, + COMPARE_TARGET(info)) == + COMPARE_VALUE(info)) { + goto exit; + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Invalid conversion opcode")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + count--; + info++; + } + +exit: + return_ACPI_STATUS(AE_OK); +} + +#if 0 +/* Previous resource validations */ + +if (aml->ext_address64.revision_ID != AML_RESOURCE_EXTENDED_ADDRESS_REVISION) { + return_ACPI_STATUS(AE_SUPPORT); +} + +if (resource->data.start_dpf.performance_robustness >= 3) { + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_VALUE); +} + +if (((aml->irq.flags & 0x09) == 0x00) || ((aml->irq.flags & 0x09) == 0x09)) { + /* + * Only [active_high, edge_sensitive] or [active_low, level_sensitive] + * polarity/trigger interrupts are allowed (ACPI spec, section + * "IRQ Format"), so 0x00 and 0x09 are illegal. + */ + ACPI_ERROR((AE_INFO, + "Invalid interrupt polarity/trigger in resource list, 0x%X", + aml->irq.flags)); + return_ACPI_STATUS(AE_BAD_DATA); +} + +resource->data.extended_irq.interrupt_count = temp8; +if (temp8 < 1) { + + /* Must have at least one IRQ */ + + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); +} + +if (resource->data.dma.transfer == 0x03) { + ACPI_ERROR((AE_INFO, "Invalid DMA.Transfer preference (3)")); + return_ACPI_STATUS(AE_BAD_DATA); +} +#endif diff --git a/kernel/drivers/acpi/acpica/rsserial.c b/kernel/drivers/acpi/acpica/rsserial.c new file mode 100644 index 000000000..4c8c6fe6e --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsserial.c @@ -0,0 +1,445 @@ +/******************************************************************************* + * + * Module Name: rsserial - GPIO/serial_bus resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsserial") + +/******************************************************************************* + * + * acpi_rs_convert_gpio + * + ******************************************************************************/ +struct acpi_rsconvert_info acpi_rs_convert_gpio[18] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_GPIO, + ACPI_RS_SIZE(struct acpi_resource_gpio), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_gpio)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_GPIO, + sizeof(struct aml_resource_gpio), + 0}, + + /* + * These fields are contiguous in both the source and destination: + * revision_id + * connection_type + */ + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.revision_id), + AML_OFFSET(gpio.revision_id), + 2}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.producer_consumer), + AML_OFFSET(gpio.flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.sharable), + AML_OFFSET(gpio.int_flags), + 3}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.wake_capable), + AML_OFFSET(gpio.int_flags), + 4}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.gpio.io_restriction), + AML_OFFSET(gpio.int_flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.gpio.triggering), + AML_OFFSET(gpio.int_flags), + 0}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.gpio.polarity), + AML_OFFSET(gpio.int_flags), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.pin_config), + AML_OFFSET(gpio.pin_config), + 1}, + + /* + * These fields are contiguous in both the source and destination: + * drive_strength + * debounce_timeout + */ + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.gpio.drive_strength), + AML_OFFSET(gpio.drive_strength), + 2}, + + /* Pin Table */ + + {ACPI_RSC_COUNT_GPIO_PIN, ACPI_RS_OFFSET(data.gpio.pin_table_length), + AML_OFFSET(gpio.pin_table_offset), + AML_OFFSET(gpio.res_source_offset)}, + + {ACPI_RSC_MOVE_GPIO_PIN, ACPI_RS_OFFSET(data.gpio.pin_table), + AML_OFFSET(gpio.pin_table_offset), + 0}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.gpio.resource_source.index), + AML_OFFSET(gpio.res_source_index), + 1}, + + {ACPI_RSC_COUNT_GPIO_RES, + ACPI_RS_OFFSET(data.gpio.resource_source.string_length), + AML_OFFSET(gpio.res_source_offset), + AML_OFFSET(gpio.vendor_offset)}, + + {ACPI_RSC_MOVE_GPIO_RES, + ACPI_RS_OFFSET(data.gpio.resource_source.string_ptr), + AML_OFFSET(gpio.res_source_offset), + 0}, + + /* Vendor Data */ + + {ACPI_RSC_COUNT_GPIO_VEN, ACPI_RS_OFFSET(data.gpio.vendor_length), + AML_OFFSET(gpio.vendor_length), + 1}, + + {ACPI_RSC_MOVE_GPIO_RES, ACPI_RS_OFFSET(data.gpio.vendor_data), + AML_OFFSET(gpio.vendor_offset), + 0}, +}; + +/******************************************************************************* + * + * acpi_rs_convert_i2c_serial_bus + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[16] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, + ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS, + sizeof(struct aml_resource_i2c_serialbus), + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id), + AML_OFFSET(common_serial_bus.revision_id), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type), + AML_OFFSET(common_serial_bus.type), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode), + AML_OFFSET(common_serial_bus.flags), + 0}, + + {ACPI_RSC_1BITFLAG, + ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer), + AML_OFFSET(common_serial_bus.flags), + 1}, + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id), + AML_OFFSET(common_serial_bus.type_revision_id), + 1}, + + {ACPI_RSC_MOVE16, + ACPI_RS_OFFSET(data.common_serial_bus.type_data_length), + AML_OFFSET(common_serial_bus.type_data_length), + 1}, + + /* Vendor data */ + + {ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_length), + AML_OFFSET(common_serial_bus.type_data_length), + AML_RESOURCE_I2C_MIN_DATA_LEN}, + + {ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_data), + 0, + sizeof(struct aml_resource_i2c_serialbus)}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index), + AML_OFFSET(common_serial_bus.res_source_index), + 1}, + + {ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + {ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + /* I2C bus type specific */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.i2c_serial_bus.access_mode), + AML_OFFSET(i2c_serial_bus.type_specific_flags), + 0}, + + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed), + AML_OFFSET(i2c_serial_bus.connection_speed), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.i2c_serial_bus.slave_address), + AML_OFFSET(i2c_serial_bus.slave_address), + 1}, +}; + +/******************************************************************************* + * + * acpi_rs_convert_spi_serial_bus + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_spi_serial_bus[20] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, + ACPI_RS_SIZE(struct acpi_resource_spi_serialbus), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_spi_serial_bus)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS, + sizeof(struct aml_resource_spi_serialbus), + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id), + AML_OFFSET(common_serial_bus.revision_id), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type), + AML_OFFSET(common_serial_bus.type), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode), + AML_OFFSET(common_serial_bus.flags), + 0}, + + {ACPI_RSC_1BITFLAG, + ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer), + AML_OFFSET(common_serial_bus.flags), + 1}, + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id), + AML_OFFSET(common_serial_bus.type_revision_id), + 1}, + + {ACPI_RSC_MOVE16, + ACPI_RS_OFFSET(data.common_serial_bus.type_data_length), + AML_OFFSET(common_serial_bus.type_data_length), + 1}, + + /* Vendor data */ + + {ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_length), + AML_OFFSET(common_serial_bus.type_data_length), + AML_RESOURCE_SPI_MIN_DATA_LEN}, + + {ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_data), + 0, + sizeof(struct aml_resource_spi_serialbus)}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index), + AML_OFFSET(common_serial_bus.res_source_index), + 1}, + + {ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + {ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + /* Spi bus type specific */ + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.spi_serial_bus.wire_mode), + AML_OFFSET(spi_serial_bus.type_specific_flags), + 0}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.spi_serial_bus.device_polarity), + AML_OFFSET(spi_serial_bus.type_specific_flags), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.data_bit_length), + AML_OFFSET(spi_serial_bus.data_bit_length), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.clock_phase), + AML_OFFSET(spi_serial_bus.clock_phase), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.spi_serial_bus.clock_polarity), + AML_OFFSET(spi_serial_bus.clock_polarity), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.spi_serial_bus.device_selection), + AML_OFFSET(spi_serial_bus.device_selection), + 1}, + + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.spi_serial_bus.connection_speed), + AML_OFFSET(spi_serial_bus.connection_speed), + 1}, +}; + +/******************************************************************************* + * + * acpi_rs_convert_uart_serial_bus + * + ******************************************************************************/ + +struct acpi_rsconvert_info acpi_rs_convert_uart_serial_bus[22] = { + {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, + ACPI_RS_SIZE(struct acpi_resource_uart_serialbus), + ACPI_RSC_TABLE_SIZE(acpi_rs_convert_uart_serial_bus)}, + + {ACPI_RSC_INITSET, ACPI_RESOURCE_NAME_SERIAL_BUS, + sizeof(struct aml_resource_uart_serialbus), + 0}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.revision_id), + AML_OFFSET(common_serial_bus.revision_id), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.common_serial_bus.type), + AML_OFFSET(common_serial_bus.type), + 1}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.common_serial_bus.slave_mode), + AML_OFFSET(common_serial_bus.flags), + 0}, + + {ACPI_RSC_1BITFLAG, + ACPI_RS_OFFSET(data.common_serial_bus.producer_consumer), + AML_OFFSET(common_serial_bus.flags), + 1}, + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.type_revision_id), + AML_OFFSET(common_serial_bus.type_revision_id), + 1}, + + {ACPI_RSC_MOVE16, + ACPI_RS_OFFSET(data.common_serial_bus.type_data_length), + AML_OFFSET(common_serial_bus.type_data_length), + 1}, + + /* Vendor data */ + + {ACPI_RSC_COUNT_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_length), + AML_OFFSET(common_serial_bus.type_data_length), + AML_RESOURCE_UART_MIN_DATA_LEN}, + + {ACPI_RSC_MOVE_SERIAL_VEN, + ACPI_RS_OFFSET(data.common_serial_bus.vendor_data), + 0, + sizeof(struct aml_resource_uart_serialbus)}, + + /* Resource Source */ + + {ACPI_RSC_MOVE8, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.index), + AML_OFFSET(common_serial_bus.res_source_index), + 1}, + + {ACPI_RSC_COUNT_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_length), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + {ACPI_RSC_MOVE_SERIAL_RES, + ACPI_RS_OFFSET(data.common_serial_bus.resource_source.string_ptr), + AML_OFFSET(common_serial_bus.type_data_length), + sizeof(struct aml_resource_common_serialbus)}, + + /* Uart bus type specific */ + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.flow_control), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 0}, + + {ACPI_RSC_2BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.stop_bits), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 2}, + + {ACPI_RSC_3BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.data_bits), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 4}, + + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.uart_serial_bus.endian), + AML_OFFSET(uart_serial_bus.type_specific_flags), + 7}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.uart_serial_bus.parity), + AML_OFFSET(uart_serial_bus.parity), + 1}, + + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.uart_serial_bus.lines_enabled), + AML_OFFSET(uart_serial_bus.lines_enabled), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.uart_serial_bus.rx_fifo_size), + AML_OFFSET(uart_serial_bus.rx_fifo_size), + 1}, + + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.uart_serial_bus.tx_fifo_size), + AML_OFFSET(uart_serial_bus.tx_fifo_size), + 1}, + + {ACPI_RSC_MOVE32, + ACPI_RS_OFFSET(data.uart_serial_bus.default_baud_rate), + AML_OFFSET(uart_serial_bus.default_baud_rate), + 1}, +}; diff --git a/kernel/drivers/acpi/acpica/rsutils.c b/kernel/drivers/acpi/acpica/rsutils.c new file mode 100644 index 000000000..ece3cd60c --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsutils.c @@ -0,0 +1,790 @@ +/******************************************************************************* + * + * Module Name: rsutils - Utilities for the resource manager + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsutils") + +/******************************************************************************* + * + * FUNCTION: acpi_rs_decode_bitmask + * + * PARAMETERS: mask - Bitmask to decode + * list - Where the converted list is returned + * + * RETURN: Count of bits set (length of list) + * + * DESCRIPTION: Convert a bit mask into a list of values + * + ******************************************************************************/ +u8 acpi_rs_decode_bitmask(u16 mask, u8 * list) +{ + u8 i; + u8 bit_count; + + ACPI_FUNCTION_ENTRY(); + + /* Decode the mask bits */ + + for (i = 0, bit_count = 0; mask; i++) { + if (mask & 0x0001) { + list[bit_count] = i; + bit_count++; + } + + mask >>= 1; + } + + return (bit_count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_encode_bitmask + * + * PARAMETERS: list - List of values to encode + * count - Length of list + * + * RETURN: Encoded bitmask + * + * DESCRIPTION: Convert a list of values to an encoded bitmask + * + ******************************************************************************/ + +u16 acpi_rs_encode_bitmask(u8 * list, u8 count) +{ + u32 i; + u16 mask; + + ACPI_FUNCTION_ENTRY(); + + /* Encode the list into a single bitmask */ + + for (i = 0, mask = 0; i < count; i++) { + mask |= (0x1 << list[i]); + } + + return (mask); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_move_data + * + * PARAMETERS: destination - Pointer to the destination descriptor + * source - Pointer to the source descriptor + * item_count - How many items to move + * move_type - Byte width + * + * RETURN: None + * + * DESCRIPTION: Move multiple data items from one descriptor to another. Handles + * alignment issues and endian issues if necessary, as configured + * via the ACPI_MOVE_* macros. (This is why a memcpy is not used) + * + ******************************************************************************/ + +void +acpi_rs_move_data(void *destination, void *source, u16 item_count, u8 move_type) +{ + u32 i; + + ACPI_FUNCTION_ENTRY(); + + /* One move per item */ + + for (i = 0; i < item_count; i++) { + switch (move_type) { + /* + * For the 8-bit case, we can perform the move all at once + * since there are no alignment or endian issues + */ + case ACPI_RSC_MOVE8: + case ACPI_RSC_MOVE_GPIO_RES: + case ACPI_RSC_MOVE_SERIAL_VEN: + case ACPI_RSC_MOVE_SERIAL_RES: + + ACPI_MEMCPY(destination, source, item_count); + return; + + /* + * 16-, 32-, and 64-bit cases must use the move macros that perform + * endian conversion and/or accommodate hardware that cannot perform + * misaligned memory transfers + */ + case ACPI_RSC_MOVE16: + case ACPI_RSC_MOVE_GPIO_PIN: + + ACPI_MOVE_16_TO_16(&ACPI_CAST_PTR(u16, destination)[i], + &ACPI_CAST_PTR(u16, source)[i]); + break; + + case ACPI_RSC_MOVE32: + + ACPI_MOVE_32_TO_32(&ACPI_CAST_PTR(u32, destination)[i], + &ACPI_CAST_PTR(u32, source)[i]); + break; + + case ACPI_RSC_MOVE64: + + ACPI_MOVE_64_TO_64(&ACPI_CAST_PTR(u64, destination)[i], + &ACPI_CAST_PTR(u64, source)[i]); + break; + + default: + + return; + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_set_resource_length + * + * PARAMETERS: total_length - Length of the AML descriptor, including + * the header and length fields. + * aml - Pointer to the raw AML descriptor + * + * RETURN: None + * + * DESCRIPTION: Set the resource_length field of an AML + * resource descriptor, both Large and Small descriptors are + * supported automatically. Note: Descriptor Type field must + * be valid. + * + ******************************************************************************/ + +void +acpi_rs_set_resource_length(acpi_rsdesc_size total_length, + union aml_resource *aml) +{ + acpi_rs_length resource_length; + + ACPI_FUNCTION_ENTRY(); + + /* Length is the total descriptor length minus the header length */ + + resource_length = (acpi_rs_length) + (total_length - acpi_ut_get_resource_header_length(aml)); + + /* Length is stored differently for large and small descriptors */ + + if (aml->small_header.descriptor_type & ACPI_RESOURCE_NAME_LARGE) { + + /* Large descriptor -- bytes 1-2 contain the 16-bit length */ + + ACPI_MOVE_16_TO_16(&aml->large_header.resource_length, + &resource_length); + } else { + /* Small descriptor -- bits 2:0 of byte 0 contain the length */ + + aml->small_header.descriptor_type = (u8) + + /* Clear any existing length, preserving descriptor type bits */ + ((aml->small_header. + descriptor_type & ~ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK) + + | resource_length); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_set_resource_header + * + * PARAMETERS: descriptor_type - Byte to be inserted as the type + * total_length - Length of the AML descriptor, including + * the header and length fields. + * aml - Pointer to the raw AML descriptor + * + * RETURN: None + * + * DESCRIPTION: Set the descriptor_type and resource_length fields of an AML + * resource descriptor, both Large and Small descriptors are + * supported automatically + * + ******************************************************************************/ + +void +acpi_rs_set_resource_header(u8 descriptor_type, + acpi_rsdesc_size total_length, + union aml_resource *aml) +{ + ACPI_FUNCTION_ENTRY(); + + /* Set the Resource Type */ + + aml->small_header.descriptor_type = descriptor_type; + + /* Set the Resource Length */ + + acpi_rs_set_resource_length(total_length, aml); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_strcpy + * + * PARAMETERS: destination - Pointer to the destination string + * source - Pointer to the source string + * + * RETURN: String length, including NULL terminator + * + * DESCRIPTION: Local string copy that returns the string length, saving a + * strcpy followed by a strlen. + * + ******************************************************************************/ + +static u16 acpi_rs_strcpy(char *destination, char *source) +{ + u16 i; + + ACPI_FUNCTION_ENTRY(); + + for (i = 0; source[i]; i++) { + destination[i] = source[i]; + } + + destination[i] = 0; + + /* Return string length including the NULL terminator */ + + return ((u16) (i + 1)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_resource_source + * + * PARAMETERS: resource_length - Length field of the descriptor + * minimum_length - Minimum length of the descriptor (minus + * any optional fields) + * resource_source - Where the resource_source is returned + * aml - Pointer to the raw AML descriptor + * string_ptr - (optional) where to store the actual + * resource_source string + * + * RETURN: Length of the string plus NULL terminator, rounded up to native + * word boundary + * + * DESCRIPTION: Copy the optional resource_source data from a raw AML descriptor + * to an internal resource descriptor + * + ******************************************************************************/ + +acpi_rs_length +acpi_rs_get_resource_source(acpi_rs_length resource_length, + acpi_rs_length minimum_length, + struct acpi_resource_source * resource_source, + union aml_resource * aml, char *string_ptr) +{ + acpi_rsdesc_size total_length; + u8 *aml_resource_source; + + ACPI_FUNCTION_ENTRY(); + + total_length = + resource_length + sizeof(struct aml_resource_large_header); + aml_resource_source = ACPI_ADD_PTR(u8, aml, minimum_length); + + /* + * resource_source is present if the length of the descriptor is longer than + * the minimum length. + * + * Note: Some resource descriptors will have an additional null, so + * we add 1 to the minimum length. + */ + if (total_length > (acpi_rsdesc_size) (minimum_length + 1)) { + + /* Get the resource_source_index */ + + resource_source->index = aml_resource_source[0]; + + resource_source->string_ptr = string_ptr; + if (!string_ptr) { + /* + * String destination pointer is not specified; Set the String + * pointer to the end of the current resource_source structure. + */ + resource_source->string_ptr = + ACPI_ADD_PTR(char, resource_source, + sizeof(struct acpi_resource_source)); + } + + /* + * In order for the Resource length to be a multiple of the native + * word, calculate the length of the string (+1 for NULL terminator) + * and expand to the next word multiple. + * + * Zero the entire area of the buffer. + */ + total_length = + (u32) + ACPI_STRLEN(ACPI_CAST_PTR(char, &aml_resource_source[1])) + + 1; + total_length = (u32) ACPI_ROUND_UP_TO_NATIVE_WORD(total_length); + + ACPI_MEMSET(resource_source->string_ptr, 0, total_length); + + /* Copy the resource_source string to the destination */ + + resource_source->string_length = + acpi_rs_strcpy(resource_source->string_ptr, + ACPI_CAST_PTR(char, + &aml_resource_source[1])); + + return ((acpi_rs_length) total_length); + } + + /* resource_source is not present */ + + resource_source->index = 0; + resource_source->string_length = 0; + resource_source->string_ptr = NULL; + return (0); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_set_resource_source + * + * PARAMETERS: aml - Pointer to the raw AML descriptor + * minimum_length - Minimum length of the descriptor (minus + * any optional fields) + * resource_source - Internal resource_source + + * + * RETURN: Total length of the AML descriptor + * + * DESCRIPTION: Convert an optional resource_source from internal format to a + * raw AML resource descriptor + * + ******************************************************************************/ + +acpi_rsdesc_size +acpi_rs_set_resource_source(union aml_resource * aml, + acpi_rs_length minimum_length, + struct acpi_resource_source * resource_source) +{ + u8 *aml_resource_source; + acpi_rsdesc_size descriptor_length; + + ACPI_FUNCTION_ENTRY(); + + descriptor_length = minimum_length; + + /* Non-zero string length indicates presence of a resource_source */ + + if (resource_source->string_length) { + + /* Point to the end of the AML descriptor */ + + aml_resource_source = ACPI_ADD_PTR(u8, aml, minimum_length); + + /* Copy the resource_source_index */ + + aml_resource_source[0] = (u8) resource_source->index; + + /* Copy the resource_source string */ + + ACPI_STRCPY(ACPI_CAST_PTR(char, &aml_resource_source[1]), + resource_source->string_ptr); + + /* + * Add the length of the string (+ 1 for null terminator) to the + * final descriptor length + */ + descriptor_length += + ((acpi_rsdesc_size) resource_source->string_length + 1); + } + + /* Return the new total length of the AML descriptor */ + + return (descriptor_length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_prt_method_data + * + * PARAMETERS: node - Device node + * ret_buffer - Pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _PRT value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_prt_method_data(struct acpi_namespace_node * node, + struct acpi_buffer * ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_get_prt_method_data); + + /* Parameters guaranteed valid by caller */ + + /* Execute the method, no parameters */ + + status = acpi_ut_evaluate_object(node, METHOD_NAME__PRT, + ACPI_BTYPE_PACKAGE, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Create a resource linked list from the byte stream buffer that comes + * back from the _CRS method execution. + */ + status = acpi_rs_create_pci_routing_table(obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluate_object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_crs_method_data + * + * PARAMETERS: node - Device node + * ret_buffer - Pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _CRS value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_crs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_get_crs_method_data); + + /* Parameters guaranteed valid by caller */ + + /* Execute the method, no parameters */ + + status = acpi_ut_evaluate_object(node, METHOD_NAME__CRS, + ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list(obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluateObject */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_prs_method_data + * + * PARAMETERS: node - Device node + * ret_buffer - Pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _PRS value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_get_prs_method_data); + + /* Parameters guaranteed valid by caller */ + + /* Execute the method, no parameters */ + + status = acpi_ut_evaluate_object(node, METHOD_NAME__PRS, + ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list(obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluateObject */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} +#endif /* ACPI_FUTURE_USAGE */ + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_aei_method_data + * + * PARAMETERS: node - Device node + * ret_buffer - Pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _AEI value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_aei_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_get_aei_method_data); + + /* Parameters guaranteed valid by caller */ + + /* Execute the method, no parameters */ + + status = acpi_ut_evaluate_object(node, METHOD_NAME__AEI, + ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list(obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluateObject */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_method_data + * + * PARAMETERS: handle - Handle to the containing object + * path - Path to method, relative to Handle + * ret_buffer - Pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _CRS or _PRS value of an + * object contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_method_data(acpi_handle handle, + char *path, struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(rs_get_method_data); + + /* Parameters guaranteed valid by caller */ + + /* Execute the method, no parameters */ + + status = + acpi_ut_evaluate_object(ACPI_CAST_PTR + (struct acpi_namespace_node, handle), path, + ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the method + * execution. + */ + status = acpi_rs_create_resource_list(obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluate_object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_set_srs_method_data + * + * PARAMETERS: node - Device node + * in_buffer - Pointer to a buffer structure of the + * parameter + * + * RETURN: Status + * + * DESCRIPTION: This function is called to set the _SRS of an object contained + * in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + * Note: Parameters guaranteed valid by caller + * + ******************************************************************************/ + +acpi_status +acpi_rs_set_srs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *in_buffer) +{ + struct acpi_evaluate_info *info; + union acpi_operand_object *args[2]; + acpi_status status; + struct acpi_buffer buffer; + + ACPI_FUNCTION_TRACE(rs_set_srs_method_data); + + /* Allocate and initialize the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + info->prefix_node = node; + info->relative_pathname = METHOD_NAME__SRS; + info->parameters = args; + info->flags = ACPI_IGNORE_RETURN_VALUE; + + /* + * The in_buffer parameter will point to a linked list of + * resource parameters. It needs to be formatted into a + * byte stream to be sent in as an input parameter to _SRS + * + * Convert the linked list into a byte stream + */ + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_rs_create_aml_resources(in_buffer, &buffer); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Create and initialize the method parameter object */ + + args[0] = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER); + if (!args[0]) { + /* + * Must free the buffer allocated above (otherwise it is freed + * later) + */ + ACPI_FREE(buffer.pointer); + status = AE_NO_MEMORY; + goto cleanup; + } + + args[0]->buffer.length = (u32) buffer.length; + args[0]->buffer.pointer = buffer.pointer; + args[0]->common.flags = AOPOBJ_DATA_VALID; + args[1] = NULL; + + /* Execute the method, no return value is expected */ + + status = acpi_ns_evaluate(info); + + /* Clean up and return the status from acpi_ns_evaluate */ + + acpi_ut_remove_reference(args[0]); + +cleanup: + ACPI_FREE(info); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/rsxface.c b/kernel/drivers/acpi/acpica/rsxface.c new file mode 100644 index 000000000..8e6276df0 --- /dev/null +++ b/kernel/drivers/acpi/acpica/rsxface.c @@ -0,0 +1,663 @@ +/******************************************************************************* + * + * Module Name: rsxface - Public interfaces to the resource manager + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acresrc.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("rsxface") + +/* Local macros for 16,32-bit to 64-bit conversion */ +#define ACPI_COPY_FIELD(out, in, field) ((out)->field = (in)->field) +#define ACPI_COPY_ADDRESS(out, in) \ + ACPI_COPY_FIELD(out, in, resource_type); \ + ACPI_COPY_FIELD(out, in, producer_consumer); \ + ACPI_COPY_FIELD(out, in, decode); \ + ACPI_COPY_FIELD(out, in, min_address_fixed); \ + ACPI_COPY_FIELD(out, in, max_address_fixed); \ + ACPI_COPY_FIELD(out, in, info); \ + ACPI_COPY_FIELD(out, in, address.granularity); \ + ACPI_COPY_FIELD(out, in, address.minimum); \ + ACPI_COPY_FIELD(out, in, address.maximum); \ + ACPI_COPY_FIELD(out, in, address.translation_offset); \ + ACPI_COPY_FIELD(out, in, address.address_length); \ + ACPI_COPY_FIELD(out, in, resource_source); +/* Local prototypes */ +static acpi_status +acpi_rs_match_vendor_resource(struct acpi_resource *resource, void *context); + +static acpi_status +acpi_rs_validate_parameters(acpi_handle device_handle, + struct acpi_buffer *buffer, + struct acpi_namespace_node **return_node); + +/******************************************************************************* + * + * FUNCTION: acpi_rs_validate_parameters + * + * PARAMETERS: device_handle - Handle to a device + * buffer - Pointer to a data buffer + * return_node - Pointer to where the device node is returned + * + * RETURN: Status + * + * DESCRIPTION: Common parameter validation for resource interfaces + * + ******************************************************************************/ + +static acpi_status +acpi_rs_validate_parameters(acpi_handle device_handle, + struct acpi_buffer *buffer, + struct acpi_namespace_node **return_node) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(rs_validate_parameters); + + /* + * Must have a valid handle to an ACPI device + */ + if (!device_handle) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + node = acpi_ns_validate_handle(device_handle); + if (!node) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (node->type != ACPI_TYPE_DEVICE) { + return_ACPI_STATUS(AE_TYPE); + } + + /* + * Validate the user buffer object + * + * if there is a non-zero buffer length we also need a valid pointer in + * the buffer. If it's a zero buffer length, we'll be returning the + * needed buffer size (later), so keep going. + */ + status = acpi_ut_validate_buffer(buffer); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + *return_node = node; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_get_irq_routing_table + * + * PARAMETERS: device_handle - Handle to the Bus device we are querying + * ret_buffer - Pointer to a buffer to receive the + * current resources for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the IRQ routing table for a + * specific bus. The caller must first acquire a handle for the + * desired bus. The routine table is placed in the buffer pointed + * to by the ret_buffer variable parameter. + * + * If the function fails an appropriate status will be returned + * and the value of ret_buffer is undefined. + * + * This function attempts to execute the _PRT method contained in + * the object indicated by the passed device_handle. + * + ******************************************************************************/ + +acpi_status +acpi_get_irq_routing_table(acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_get_irq_routing_table); + + /* Validate parameters then dispatch to internal routine */ + + status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_rs_get_prt_method_data(node, ret_buffer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_irq_routing_table) + +/******************************************************************************* + * + * FUNCTION: acpi_get_current_resources + * + * PARAMETERS: device_handle - Handle to the device object for the + * device we are querying + * ret_buffer - Pointer to a buffer to receive the + * current resources for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the current resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is placed in the buffer + * pointed to by the ret_buffer variable parameter. + * + * If the function fails an appropriate status will be returned + * and the value of ret_buffer is undefined. + * + * This function attempts to execute the _CRS method contained in + * the object indicated by the passed device_handle. + * + ******************************************************************************/ +acpi_status +acpi_get_current_resources(acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_get_current_resources); + + /* Validate parameters then dispatch to internal routine */ + + status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_rs_get_crs_method_data(node, ret_buffer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_current_resources) +#ifdef ACPI_FUTURE_USAGE +/******************************************************************************* + * + * FUNCTION: acpi_get_possible_resources + * + * PARAMETERS: device_handle - Handle to the device object for the + * device we are querying + * ret_buffer - Pointer to a buffer to receive the + * resources for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get a list of the possible resources + * for a specific device. The caller must first acquire a handle + * for the desired device. The resource data is placed in the + * buffer pointed to by the ret_buffer variable. + * + * If the function fails an appropriate status will be returned + * and the value of ret_buffer is undefined. + * + ******************************************************************************/ +acpi_status +acpi_get_possible_resources(acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_get_possible_resources); + + /* Validate parameters then dispatch to internal routine */ + + status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_rs_get_prs_method_data(node, ret_buffer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_possible_resources) +#endif /* ACPI_FUTURE_USAGE */ +/******************************************************************************* + * + * FUNCTION: acpi_set_current_resources + * + * PARAMETERS: device_handle - Handle to the device object for the + * device we are setting resources + * in_buffer - Pointer to a buffer containing the + * resources to be set for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to set the current resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is passed to the routine + * the buffer pointed to by the in_buffer variable. + * + ******************************************************************************/ +acpi_status +acpi_set_current_resources(acpi_handle device_handle, + struct acpi_buffer *in_buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_set_current_resources); + + /* Validate the buffer, don't allow zero length */ + + if ((!in_buffer) || (!in_buffer->pointer) || (!in_buffer->length)) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Validate parameters then dispatch to internal routine */ + + status = acpi_rs_validate_parameters(device_handle, in_buffer, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_rs_set_srs_method_data(node, in_buffer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_set_current_resources) + +/******************************************************************************* + * + * FUNCTION: acpi_get_event_resources + * + * PARAMETERS: device_handle - Handle to the device object for the + * device we are getting resources + * in_buffer - Pointer to a buffer containing the + * resources to be set for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the event resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is passed to the routine + * the buffer pointed to by the in_buffer variable. Uses the + * _AEI method. + * + ******************************************************************************/ +acpi_status +acpi_get_event_resources(acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + + ACPI_FUNCTION_TRACE(acpi_get_event_resources); + + /* Validate parameters then dispatch to internal routine */ + + status = acpi_rs_validate_parameters(device_handle, ret_buffer, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_rs_get_aei_method_data(node, ret_buffer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_event_resources) + +/****************************************************************************** + * + * FUNCTION: acpi_resource_to_address64 + * + * PARAMETERS: resource - Pointer to a resource + * out - Pointer to the users's return buffer + * (a struct acpi_resource_address64) + * + * RETURN: Status + * + * DESCRIPTION: If the resource is an address16, address32, or address64, + * copy it to the address64 return buffer. This saves the + * caller from having to duplicate code for different-sized + * addresses. + * + ******************************************************************************/ +acpi_status +acpi_resource_to_address64(struct acpi_resource *resource, + struct acpi_resource_address64 *out) +{ + struct acpi_resource_address16 *address16; + struct acpi_resource_address32 *address32; + + if (!resource || !out) { + return (AE_BAD_PARAMETER); + } + + /* Convert 16 or 32 address descriptor to 64 */ + + switch (resource->type) { + case ACPI_RESOURCE_TYPE_ADDRESS16: + + address16 = + ACPI_CAST_PTR(struct acpi_resource_address16, + &resource->data); + ACPI_COPY_ADDRESS(out, address16); + break; + + case ACPI_RESOURCE_TYPE_ADDRESS32: + + address32 = + ACPI_CAST_PTR(struct acpi_resource_address32, + &resource->data); + ACPI_COPY_ADDRESS(out, address32); + break; + + case ACPI_RESOURCE_TYPE_ADDRESS64: + + /* Simple copy for 64 bit source */ + + ACPI_MEMCPY(out, &resource->data, + sizeof(struct acpi_resource_address64)); + break; + + default: + + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_resource_to_address64) + +/******************************************************************************* + * + * FUNCTION: acpi_get_vendor_resource + * + * PARAMETERS: device_handle - Handle for the parent device object + * name - Method name for the parent resource + * (METHOD_NAME__CRS or METHOD_NAME__PRS) + * uuid - Pointer to the UUID to be matched. + * includes both subtype and 16-byte UUID + * ret_buffer - Where the vendor resource is returned + * + * RETURN: Status + * + * DESCRIPTION: Walk a resource template for the specified device to find a + * vendor-defined resource that matches the supplied UUID and + * UUID subtype. Returns a struct acpi_resource of type Vendor. + * + ******************************************************************************/ +acpi_status +acpi_get_vendor_resource(acpi_handle device_handle, + char *name, + struct acpi_vendor_uuid * uuid, + struct acpi_buffer * ret_buffer) +{ + struct acpi_vendor_walk_info info; + acpi_status status; + + /* Other parameters are validated by acpi_walk_resources */ + + if (!uuid || !ret_buffer) { + return (AE_BAD_PARAMETER); + } + + info.uuid = uuid; + info.buffer = ret_buffer; + info.status = AE_NOT_EXIST; + + /* Walk the _CRS or _PRS resource list for this device */ + + status = + acpi_walk_resources(device_handle, name, + acpi_rs_match_vendor_resource, &info); + if (ACPI_FAILURE(status)) { + return (status); + } + + return (info.status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_vendor_resource) + +/******************************************************************************* + * + * FUNCTION: acpi_rs_match_vendor_resource + * + * PARAMETERS: acpi_walk_resource_callback + * + * RETURN: Status + * + * DESCRIPTION: Match a vendor resource via the ACPI 3.0 UUID + * + ******************************************************************************/ +static acpi_status +acpi_rs_match_vendor_resource(struct acpi_resource *resource, void *context) +{ + struct acpi_vendor_walk_info *info = context; + struct acpi_resource_vendor_typed *vendor; + struct acpi_buffer *buffer; + acpi_status status; + + /* Ignore all descriptors except Vendor */ + + if (resource->type != ACPI_RESOURCE_TYPE_VENDOR) { + return (AE_OK); + } + + vendor = &resource->data.vendor_typed; + + /* + * For a valid match, these conditions must hold: + * + * 1) Length of descriptor data must be at least as long as a UUID struct + * 2) The UUID subtypes must match + * 3) The UUID data must match + */ + if ((vendor->byte_length < (ACPI_UUID_LENGTH + 1)) || + (vendor->uuid_subtype != info->uuid->subtype) || + (ACPI_MEMCMP(vendor->uuid, info->uuid->data, ACPI_UUID_LENGTH))) { + return (AE_OK); + } + + /* Validate/Allocate/Clear caller buffer */ + + buffer = info->buffer; + status = acpi_ut_initialize_buffer(buffer, resource->length); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Found the correct resource, copy and return it */ + + ACPI_MEMCPY(buffer->pointer, resource, resource->length); + buffer->length = resource->length; + + /* Found the desired descriptor, terminate resource walk */ + + info->status = AE_OK; + return (AE_CTRL_TERMINATE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_walk_resource_buffer + * + * PARAMETERS: buffer - Formatted buffer returned by one of the + * various Get*Resource functions + * user_function - Called for each resource + * context - Passed to user_function + * + * RETURN: Status + * + * DESCRIPTION: Walks the input resource template. The user_function is called + * once for each resource in the list. + * + ******************************************************************************/ + +acpi_status +acpi_walk_resource_buffer(struct acpi_buffer * buffer, + acpi_walk_resource_callback user_function, + void *context) +{ + acpi_status status = AE_OK; + struct acpi_resource *resource; + struct acpi_resource *resource_end; + + ACPI_FUNCTION_TRACE(acpi_walk_resource_buffer); + + /* Parameter validation */ + + if (!buffer || !buffer->pointer || !user_function) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Buffer contains the resource list and length */ + + resource = ACPI_CAST_PTR(struct acpi_resource, buffer->pointer); + resource_end = + ACPI_ADD_PTR(struct acpi_resource, buffer->pointer, buffer->length); + + /* Walk the resource list until the end_tag is found (or buffer end) */ + + while (resource < resource_end) { + + /* Sanity check the resource type */ + + if (resource->type > ACPI_RESOURCE_TYPE_MAX) { + status = AE_AML_INVALID_RESOURCE_TYPE; + break; + } + + /* Sanity check the length. It must not be zero, or we loop forever */ + + if (!resource->length) { + return_ACPI_STATUS(AE_AML_BAD_RESOURCE_LENGTH); + } + + /* Invoke the user function, abort on any error returned */ + + status = user_function(resource, context); + if (ACPI_FAILURE(status)) { + if (status == AE_CTRL_TERMINATE) { + + /* This is an OK termination by the user function */ + + status = AE_OK; + } + break; + } + + /* end_tag indicates end-of-list */ + + if (resource->type == ACPI_RESOURCE_TYPE_END_TAG) { + break; + } + + /* Get the next resource descriptor */ + + resource = ACPI_NEXT_RESOURCE(resource); + } + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_walk_resource_buffer) + +/******************************************************************************* + * + * FUNCTION: acpi_walk_resources + * + * PARAMETERS: device_handle - Handle to the device object for the + * device we are querying + * name - Method name of the resources we want. + * (METHOD_NAME__CRS, METHOD_NAME__PRS, or + * METHOD_NAME__AEI) + * user_function - Called for each resource + * context - Passed to user_function + * + * RETURN: Status + * + * DESCRIPTION: Retrieves the current or possible resource list for the + * specified device. The user_function is called once for + * each resource in the list. + * + ******************************************************************************/ +acpi_status +acpi_walk_resources(acpi_handle device_handle, + char *name, + acpi_walk_resource_callback user_function, void *context) +{ + acpi_status status; + struct acpi_buffer buffer; + + ACPI_FUNCTION_TRACE(acpi_walk_resources); + + /* Parameter validation */ + + if (!device_handle || !user_function || !name || + (!ACPI_COMPARE_NAME(name, METHOD_NAME__CRS) && + !ACPI_COMPARE_NAME(name, METHOD_NAME__PRS) && + !ACPI_COMPARE_NAME(name, METHOD_NAME__AEI))) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Get the _CRS/_PRS/_AEI resource list */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_rs_get_method_data(device_handle, name, &buffer); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Walk the resource list and cleanup */ + + status = acpi_walk_resource_buffer(&buffer, user_function, context); + ACPI_FREE(buffer.pointer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_walk_resources) diff --git a/kernel/drivers/acpi/acpica/tbdata.c b/kernel/drivers/acpi/acpica/tbdata.c new file mode 100644 index 000000000..d7f838645 --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbdata.c @@ -0,0 +1,773 @@ +/****************************************************************************** + * + * Module Name: tbdata - Table manager data structure functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbdata") + +/******************************************************************************* + * + * FUNCTION: acpi_tb_init_table_descriptor + * + * PARAMETERS: table_desc - Table descriptor + * address - Physical address of the table + * flags - Allocation flags of the table + * table - Pointer to the table + * + * RETURN: None + * + * DESCRIPTION: Initialize a new table descriptor + * + ******************************************************************************/ +void +acpi_tb_init_table_descriptor(struct acpi_table_desc *table_desc, + acpi_physical_address address, + u8 flags, struct acpi_table_header *table) +{ + + /* + * Initialize the table descriptor. Set the pointer to NULL, since the + * table is not fully mapped at this time. + */ + ACPI_MEMSET(table_desc, 0, sizeof(struct acpi_table_desc)); + table_desc->address = address; + table_desc->length = table->length; + table_desc->flags = flags; + ACPI_MOVE_32_TO_32(table_desc->signature.ascii, table->signature); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_acquire_table + * + * PARAMETERS: table_desc - Table descriptor + * table_ptr - Where table is returned + * table_length - Where table length is returned + * table_flags - Where table allocation flags are returned + * + * RETURN: Status + * + * DESCRIPTION: Acquire an ACPI table. It can be used for tables not + * maintained in the acpi_gbl_root_table_list. + * + ******************************************************************************/ + +acpi_status +acpi_tb_acquire_table(struct acpi_table_desc *table_desc, + struct acpi_table_header **table_ptr, + u32 *table_length, u8 *table_flags) +{ + struct acpi_table_header *table = NULL; + + switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) { + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: + + table = + acpi_os_map_memory(table_desc->address, table_desc->length); + break; + + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: + + table = ACPI_CAST_PTR(struct acpi_table_header, + ACPI_PHYSADDR_TO_PTR(table_desc-> + address)); + break; + + default: + + break; + } + + /* Table is not valid yet */ + + if (!table) { + return (AE_NO_MEMORY); + } + + /* Fill the return values */ + + *table_ptr = table; + *table_length = table_desc->length; + *table_flags = table_desc->flags; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_release_table + * + * PARAMETERS: table - Pointer for the table + * table_length - Length for the table + * table_flags - Allocation flags for the table + * + * RETURN: None + * + * DESCRIPTION: Release a table. The inverse of acpi_tb_acquire_table(). + * + ******************************************************************************/ + +void +acpi_tb_release_table(struct acpi_table_header *table, + u32 table_length, u8 table_flags) +{ + + switch (table_flags & ACPI_TABLE_ORIGIN_MASK) { + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: + + acpi_os_unmap_memory(table, table_length); + break; + + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: + default: + + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_acquire_temp_table + * + * PARAMETERS: table_desc - Table descriptor to be acquired + * address - Address of the table + * flags - Allocation flags of the table + * + * RETURN: Status + * + * DESCRIPTION: This function validates the table header to obtain the length + * of a table and fills the table descriptor to make its state as + * "INSTALLED". Such a table descriptor is only used for verified + * installation. + * + ******************************************************************************/ + +acpi_status +acpi_tb_acquire_temp_table(struct acpi_table_desc *table_desc, + acpi_physical_address address, u8 flags) +{ + struct acpi_table_header *table_header; + + switch (flags & ACPI_TABLE_ORIGIN_MASK) { + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: + + /* Get the length of the full table from the header */ + + table_header = + acpi_os_map_memory(address, + sizeof(struct acpi_table_header)); + if (!table_header) { + return (AE_NO_MEMORY); + } + + acpi_tb_init_table_descriptor(table_desc, address, flags, + table_header); + acpi_os_unmap_memory(table_header, + sizeof(struct acpi_table_header)); + return (AE_OK); + + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: + + table_header = ACPI_CAST_PTR(struct acpi_table_header, + ACPI_PHYSADDR_TO_PTR(address)); + if (!table_header) { + return (AE_NO_MEMORY); + } + + acpi_tb_init_table_descriptor(table_desc, address, flags, + table_header); + return (AE_OK); + + default: + + break; + } + + /* Table is not valid yet */ + + return (AE_NO_MEMORY); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_release_temp_table + * + * PARAMETERS: table_desc - Table descriptor to be released + * + * RETURN: Status + * + * DESCRIPTION: The inverse of acpi_tb_acquire_temp_table(). + * + *****************************************************************************/ + +void acpi_tb_release_temp_table(struct acpi_table_desc *table_desc) +{ + + /* + * Note that the .Address is maintained by the callers of + * acpi_tb_acquire_temp_table(), thus do not invoke acpi_tb_uninstall_table() + * where .Address will be freed. + */ + acpi_tb_invalidate_table(table_desc); +} + +/****************************************************************************** + * + * FUNCTION: acpi_tb_validate_table + * + * PARAMETERS: table_desc - Table descriptor + * + * RETURN: Status + * + * DESCRIPTION: This function is called to validate the table, the returned + * table descriptor is in "VALIDATED" state. + * + *****************************************************************************/ + +acpi_status acpi_tb_validate_table(struct acpi_table_desc *table_desc) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(tb_validate_table); + + /* Validate the table if necessary */ + + if (!table_desc->pointer) { + status = acpi_tb_acquire_table(table_desc, &table_desc->pointer, + &table_desc->length, + &table_desc->flags); + if (!table_desc->pointer) { + status = AE_NO_MEMORY; + } + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_invalidate_table + * + * PARAMETERS: table_desc - Table descriptor + * + * RETURN: None + * + * DESCRIPTION: Invalidate one internal ACPI table, this is the inverse of + * acpi_tb_validate_table(). + * + ******************************************************************************/ + +void acpi_tb_invalidate_table(struct acpi_table_desc *table_desc) +{ + + ACPI_FUNCTION_TRACE(tb_invalidate_table); + + /* Table must be validated */ + + if (!table_desc->pointer) { + return_VOID; + } + + acpi_tb_release_table(table_desc->pointer, table_desc->length, + table_desc->flags); + table_desc->pointer = NULL; + + return_VOID; +} + +/****************************************************************************** + * + * FUNCTION: acpi_tb_validate_temp_table + * + * PARAMETERS: table_desc - Table descriptor + * + * RETURN: Status + * + * DESCRIPTION: This function is called to validate the table, the returned + * table descriptor is in "VALIDATED" state. + * + *****************************************************************************/ + +acpi_status acpi_tb_validate_temp_table(struct acpi_table_desc *table_desc) +{ + + if (!table_desc->pointer && !acpi_gbl_verify_table_checksum) { + /* + * Only validates the header of the table. + * Note that Length contains the size of the mapping after invoking + * this work around, this value is required by + * acpi_tb_release_temp_table(). + * We can do this because in acpi_init_table_descriptor(), the Length + * field of the installed descriptor is filled with the actual + * table length obtaining from the table header. + */ + table_desc->length = sizeof(struct acpi_table_header); + } + + return (acpi_tb_validate_table(table_desc)); +} + +/****************************************************************************** + * + * FUNCTION: acpi_tb_verify_temp_table + * + * PARAMETERS: table_desc - Table descriptor + * signature - Table signature to verify + * + * RETURN: Status + * + * DESCRIPTION: This function is called to validate and verify the table, the + * returned table descriptor is in "VALIDATED" state. + * + *****************************************************************************/ + +acpi_status +acpi_tb_verify_temp_table(struct acpi_table_desc * table_desc, char *signature) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(tb_verify_temp_table); + + /* Validate the table */ + + status = acpi_tb_validate_temp_table(table_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* If a particular signature is expected (DSDT/FACS), it must match */ + + if (signature && !ACPI_COMPARE_NAME(&table_desc->signature, signature)) { + ACPI_BIOS_ERROR((AE_INFO, + "Invalid signature 0x%X for ACPI table, expected [%s]", + table_desc->signature.integer, signature)); + status = AE_BAD_SIGNATURE; + goto invalidate_and_exit; + } + + /* Verify the checksum */ + + if (acpi_gbl_verify_table_checksum) { + status = + acpi_tb_verify_checksum(table_desc->pointer, + table_desc->length); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, + "%4.4s 0x%8.8X%8.8X" + " Attempted table install failed", + acpi_ut_valid_acpi_name(table_desc-> + signature. + ascii) ? + table_desc->signature.ascii : "????", + ACPI_FORMAT_UINT64(table_desc-> + address))); + goto invalidate_and_exit; + } + } + + return_ACPI_STATUS(AE_OK); + +invalidate_and_exit: + acpi_tb_invalidate_table(table_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_resize_root_table_list + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Expand the size of global table array + * + ******************************************************************************/ + +acpi_status acpi_tb_resize_root_table_list(void) +{ + struct acpi_table_desc *tables; + u32 table_count; + + ACPI_FUNCTION_TRACE(tb_resize_root_table_list); + + /* allow_resize flag is a parameter to acpi_initialize_tables */ + + if (!(acpi_gbl_root_table_list.flags & ACPI_ROOT_ALLOW_RESIZE)) { + ACPI_ERROR((AE_INFO, + "Resize of Root Table Array is not allowed")); + return_ACPI_STATUS(AE_SUPPORT); + } + + /* Increase the Table Array size */ + + if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) { + table_count = acpi_gbl_root_table_list.max_table_count; + } else { + table_count = acpi_gbl_root_table_list.current_table_count; + } + + tables = ACPI_ALLOCATE_ZEROED(((acpi_size) table_count + + ACPI_ROOT_TABLE_SIZE_INCREMENT) * + sizeof(struct acpi_table_desc)); + if (!tables) { + ACPI_ERROR((AE_INFO, + "Could not allocate new root table array")); + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Copy and free the previous table array */ + + if (acpi_gbl_root_table_list.tables) { + ACPI_MEMCPY(tables, acpi_gbl_root_table_list.tables, + (acpi_size) table_count * + sizeof(struct acpi_table_desc)); + + if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) { + ACPI_FREE(acpi_gbl_root_table_list.tables); + } + } + + acpi_gbl_root_table_list.tables = tables; + acpi_gbl_root_table_list.max_table_count = + table_count + ACPI_ROOT_TABLE_SIZE_INCREMENT; + acpi_gbl_root_table_list.flags |= ACPI_ROOT_ORIGIN_ALLOCATED; + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_next_table_descriptor + * + * PARAMETERS: table_index - Where table index is returned + * table_desc - Where table descriptor is returned + * + * RETURN: Status and table index/descriptor. + * + * DESCRIPTION: Allocate a new ACPI table entry to the global table list + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_next_table_descriptor(u32 *table_index, + struct acpi_table_desc **table_desc) +{ + acpi_status status; + u32 i; + + /* Ensure that there is room for the table in the Root Table List */ + + if (acpi_gbl_root_table_list.current_table_count >= + acpi_gbl_root_table_list.max_table_count) { + status = acpi_tb_resize_root_table_list(); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + i = acpi_gbl_root_table_list.current_table_count; + acpi_gbl_root_table_list.current_table_count++; + + if (table_index) { + *table_index = i; + } + if (table_desc) { + *table_desc = &acpi_gbl_root_table_list.tables[i]; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_terminate + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Delete all internal ACPI tables + * + ******************************************************************************/ + +void acpi_tb_terminate(void) +{ + u32 i; + + ACPI_FUNCTION_TRACE(tb_terminate); + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + + /* Delete the individual tables */ + + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) { + acpi_tb_uninstall_table(&acpi_gbl_root_table_list.tables[i]); + } + + /* + * Delete the root table array if allocated locally. Array cannot be + * mapped, so we don't need to check for that flag. + */ + if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) { + ACPI_FREE(acpi_gbl_root_table_list.tables); + } + + acpi_gbl_root_table_list.tables = NULL; + acpi_gbl_root_table_list.flags = 0; + acpi_gbl_root_table_list.current_table_count = 0; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ACPI Tables freed\n")); + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_delete_namespace_by_owner + * + * PARAMETERS: table_index - Table index + * + * RETURN: Status + * + * DESCRIPTION: Delete all namespace objects created when this table was loaded. + * + ******************************************************************************/ + +acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index) +{ + acpi_owner_id owner_id; + acpi_status status; + + ACPI_FUNCTION_TRACE(tb_delete_namespace_by_owner); + + status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (table_index >= acpi_gbl_root_table_list.current_table_count) { + + /* The table index does not exist */ + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Get the owner ID for this table, used to delete namespace nodes */ + + owner_id = acpi_gbl_root_table_list.tables[table_index].owner_id; + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + + /* + * Need to acquire the namespace writer lock to prevent interference + * with any concurrent namespace walks. The interpreter must be + * released during the deletion since the acquisition of the deletion + * lock may block, and also since the execution of a namespace walk + * must be allowed to use the interpreter. + */ + (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock); + + acpi_ns_delete_namespace_by_owner(owner_id); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock); + + status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_allocate_owner_id + * + * PARAMETERS: table_index - Table index + * + * RETURN: Status + * + * DESCRIPTION: Allocates owner_id in table_desc + * + ******************************************************************************/ + +acpi_status acpi_tb_allocate_owner_id(u32 table_index) +{ + acpi_status status = AE_BAD_PARAMETER; + + ACPI_FUNCTION_TRACE(tb_allocate_owner_id); + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + if (table_index < acpi_gbl_root_table_list.current_table_count) { + status = + acpi_ut_allocate_owner_id(& + (acpi_gbl_root_table_list. + tables[table_index].owner_id)); + } + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_release_owner_id + * + * PARAMETERS: table_index - Table index + * + * RETURN: Status + * + * DESCRIPTION: Releases owner_id in table_desc + * + ******************************************************************************/ + +acpi_status acpi_tb_release_owner_id(u32 table_index) +{ + acpi_status status = AE_BAD_PARAMETER; + + ACPI_FUNCTION_TRACE(tb_release_owner_id); + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + if (table_index < acpi_gbl_root_table_list.current_table_count) { + acpi_ut_release_owner_id(& + (acpi_gbl_root_table_list. + tables[table_index].owner_id)); + status = AE_OK; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_owner_id + * + * PARAMETERS: table_index - Table index + * owner_id - Where the table owner_id is returned + * + * RETURN: Status + * + * DESCRIPTION: returns owner_id for the ACPI table + * + ******************************************************************************/ + +acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id * owner_id) +{ + acpi_status status = AE_BAD_PARAMETER; + + ACPI_FUNCTION_TRACE(tb_get_owner_id); + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + if (table_index < acpi_gbl_root_table_list.current_table_count) { + *owner_id = + acpi_gbl_root_table_list.tables[table_index].owner_id; + status = AE_OK; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_is_table_loaded + * + * PARAMETERS: table_index - Index into the root table + * + * RETURN: Table Loaded Flag + * + ******************************************************************************/ + +u8 acpi_tb_is_table_loaded(u32 table_index) +{ + u8 is_loaded = FALSE; + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + if (table_index < acpi_gbl_root_table_list.current_table_count) { + is_loaded = (u8) + (acpi_gbl_root_table_list.tables[table_index].flags & + ACPI_TABLE_IS_LOADED); + } + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return (is_loaded); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_set_table_loaded_flag + * + * PARAMETERS: table_index - Table index + * is_loaded - TRUE if table is loaded, FALSE otherwise + * + * RETURN: None + * + * DESCRIPTION: Sets the table loaded flag to either TRUE or FALSE. + * + ******************************************************************************/ + +void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded) +{ + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + if (table_index < acpi_gbl_root_table_list.current_table_count) { + if (is_loaded) { + acpi_gbl_root_table_list.tables[table_index].flags |= + ACPI_TABLE_IS_LOADED; + } else { + acpi_gbl_root_table_list.tables[table_index].flags &= + ~ACPI_TABLE_IS_LOADED; + } + } + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); +} diff --git a/kernel/drivers/acpi/acpica/tbfadt.c b/kernel/drivers/acpi/acpica/tbfadt.c new file mode 100644 index 000000000..7d2486005 --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbfadt.c @@ -0,0 +1,747 @@ +/****************************************************************************** + * + * Module Name: tbfadt - FADT table utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbfadt") + +/* Local prototypes */ +static void +acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, + u8 space_id, + u8 byte_width, + u64 address, char *register_name, u8 flags); + +static void acpi_tb_convert_fadt(void); + +static void acpi_tb_setup_fadt_registers(void); + +static u64 +acpi_tb_select_address(char *register_name, u32 address32, u64 address64); + +/* Table for conversion of FADT to common internal format and FADT validation */ + +typedef struct acpi_fadt_info { + char *name; + u16 address64; + u16 address32; + u16 length; + u8 default_length; + u8 flags; + +} acpi_fadt_info; + +#define ACPI_FADT_OPTIONAL 0 +#define ACPI_FADT_REQUIRED 1 +#define ACPI_FADT_SEPARATE_LENGTH 2 +#define ACPI_FADT_GPE_REGISTER 4 + +static struct acpi_fadt_info fadt_info_table[] = { + {"Pm1aEventBlock", + ACPI_FADT_OFFSET(xpm1a_event_block), + ACPI_FADT_OFFSET(pm1a_event_block), + ACPI_FADT_OFFSET(pm1_event_length), + ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */ + ACPI_FADT_REQUIRED}, + + {"Pm1bEventBlock", + ACPI_FADT_OFFSET(xpm1b_event_block), + ACPI_FADT_OFFSET(pm1b_event_block), + ACPI_FADT_OFFSET(pm1_event_length), + ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */ + ACPI_FADT_OPTIONAL}, + + {"Pm1aControlBlock", + ACPI_FADT_OFFSET(xpm1a_control_block), + ACPI_FADT_OFFSET(pm1a_control_block), + ACPI_FADT_OFFSET(pm1_control_length), + ACPI_PM1_REGISTER_WIDTH, + ACPI_FADT_REQUIRED}, + + {"Pm1bControlBlock", + ACPI_FADT_OFFSET(xpm1b_control_block), + ACPI_FADT_OFFSET(pm1b_control_block), + ACPI_FADT_OFFSET(pm1_control_length), + ACPI_PM1_REGISTER_WIDTH, + ACPI_FADT_OPTIONAL}, + + {"Pm2ControlBlock", + ACPI_FADT_OFFSET(xpm2_control_block), + ACPI_FADT_OFFSET(pm2_control_block), + ACPI_FADT_OFFSET(pm2_control_length), + ACPI_PM2_REGISTER_WIDTH, + ACPI_FADT_SEPARATE_LENGTH}, + + {"PmTimerBlock", + ACPI_FADT_OFFSET(xpm_timer_block), + ACPI_FADT_OFFSET(pm_timer_block), + ACPI_FADT_OFFSET(pm_timer_length), + ACPI_PM_TIMER_WIDTH, + ACPI_FADT_SEPARATE_LENGTH}, /* ACPI 5.0A: Timer is optional */ + + {"Gpe0Block", + ACPI_FADT_OFFSET(xgpe0_block), + ACPI_FADT_OFFSET(gpe0_block), + ACPI_FADT_OFFSET(gpe0_block_length), + 0, + ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER}, + + {"Gpe1Block", + ACPI_FADT_OFFSET(xgpe1_block), + ACPI_FADT_OFFSET(gpe1_block), + ACPI_FADT_OFFSET(gpe1_block_length), + 0, + ACPI_FADT_SEPARATE_LENGTH | ACPI_FADT_GPE_REGISTER} +}; + +#define ACPI_FADT_INFO_ENTRIES \ + (sizeof (fadt_info_table) / sizeof (struct acpi_fadt_info)) + +/* Table used to split Event Blocks into separate status/enable registers */ + +typedef struct acpi_fadt_pm_info { + struct acpi_generic_address *target; + u16 source; + u8 register_num; + +} acpi_fadt_pm_info; + +static struct acpi_fadt_pm_info fadt_pm_info_table[] = { + {&acpi_gbl_xpm1a_status, + ACPI_FADT_OFFSET(xpm1a_event_block), + 0}, + + {&acpi_gbl_xpm1a_enable, + ACPI_FADT_OFFSET(xpm1a_event_block), + 1}, + + {&acpi_gbl_xpm1b_status, + ACPI_FADT_OFFSET(xpm1b_event_block), + 0}, + + {&acpi_gbl_xpm1b_enable, + ACPI_FADT_OFFSET(xpm1b_event_block), + 1} +}; + +#define ACPI_FADT_PM_INFO_ENTRIES \ + (sizeof (fadt_pm_info_table) / sizeof (struct acpi_fadt_pm_info)) + +/******************************************************************************* + * + * FUNCTION: acpi_tb_init_generic_address + * + * PARAMETERS: generic_address - GAS struct to be initialized + * space_id - ACPI Space ID for this register + * byte_width - Width of this register + * address - Address of the register + * register_name - ASCII name of the ACPI register + * + * RETURN: None + * + * DESCRIPTION: Initialize a Generic Address Structure (GAS) + * See the ACPI specification for a full description and + * definition of this structure. + * + ******************************************************************************/ + +static void +acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, + u8 space_id, + u8 byte_width, + u64 address, char *register_name, u8 flags) +{ + u8 bit_width; + + /* + * Bit width field in the GAS is only one byte long, 255 max. + * Check for bit_width overflow in GAS. + */ + bit_width = (u8)(byte_width * 8); + if (byte_width > 31) { /* (31*8)=248, (32*8)=256 */ + /* + * No error for GPE blocks, because we do not use the bit_width + * for GPEs, the legacy length (byte_width) is used instead to + * allow for a large number of GPEs. + */ + if (!(flags & ACPI_FADT_GPE_REGISTER)) { + ACPI_ERROR((AE_INFO, + "%s - 32-bit FADT register is too long (%u bytes, %u bits) " + "to convert to GAS struct - 255 bits max, truncating", + register_name, byte_width, + (byte_width * 8))); + } + + bit_width = 255; + } + + /* + * The 64-bit Address field is non-aligned in the byte packed + * GAS struct. + */ + ACPI_MOVE_64_TO_64(&generic_address->address, &address); + + /* All other fields are byte-wide */ + + generic_address->space_id = space_id; + generic_address->bit_width = bit_width; + generic_address->bit_offset = 0; + generic_address->access_width = 0; /* Access width ANY */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_select_address + * + * PARAMETERS: register_name - ASCII name of the ACPI register + * address32 - 32-bit address of the register + * address64 - 64-bit address of the register + * + * RETURN: The resolved 64-bit address + * + * DESCRIPTION: Select between 32-bit and 64-bit versions of addresses within + * the FADT. Used for the FACS and DSDT addresses. + * + * NOTES: + * + * Check for FACS and DSDT address mismatches. An address mismatch between + * the 32-bit and 64-bit address fields (FIRMWARE_CTRL/X_FIRMWARE_CTRL and + * DSDT/X_DSDT) could be a corrupted address field or it might indicate + * the presence of two FACS or two DSDT tables. + * + * November 2013: + * By default, as per the ACPICA specification, a valid 64-bit address is + * used regardless of the value of the 32-bit address. However, this + * behavior can be overridden via the acpi_gbl_use32_bit_fadt_addresses flag. + * + ******************************************************************************/ + +static u64 +acpi_tb_select_address(char *register_name, u32 address32, u64 address64) +{ + + if (!address64) { + + /* 64-bit address is zero, use 32-bit address */ + + return ((u64)address32); + } + + if (address32 && (address64 != (u64)address32)) { + + /* Address mismatch between 32-bit and 64-bit versions */ + + ACPI_BIOS_WARNING((AE_INFO, + "32/64X %s address mismatch in FADT: " + "0x%8.8X/0x%8.8X%8.8X, using %u-bit address", + register_name, address32, + ACPI_FORMAT_UINT64(address64), + acpi_gbl_use32_bit_fadt_addresses ? 32 : + 64)); + + /* 32-bit address override */ + + if (acpi_gbl_use32_bit_fadt_addresses) { + return ((u64)address32); + } + } + + /* Default is to use the 64-bit address */ + + return (address64); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_parse_fadt + * + * PARAMETERS: table_index - Index for the FADT + * + * RETURN: None + * + * DESCRIPTION: Initialize the FADT, DSDT and FACS tables + * (FADT contains the addresses of the DSDT and FACS) + * + ******************************************************************************/ + +void acpi_tb_parse_fadt(u32 table_index) +{ + u32 length; + struct acpi_table_header *table; + + /* + * The FADT has multiple versions with different lengths, + * and it contains pointers to both the DSDT and FACS tables. + * + * Get a local copy of the FADT and convert it to a common format + * Map entire FADT, assumed to be smaller than one page. + */ + length = acpi_gbl_root_table_list.tables[table_index].length; + + table = + acpi_os_map_memory(acpi_gbl_root_table_list.tables[table_index]. + address, length); + if (!table) { + return; + } + + /* + * Validate the FADT checksum before we copy the table. Ignore + * checksum error as we want to try to get the DSDT and FACS. + */ + (void)acpi_tb_verify_checksum(table, length); + + /* Create a local copy of the FADT in common ACPI 2.0+ format */ + + acpi_tb_create_local_fadt(table, length); + + /* All done with the real FADT, unmap it */ + + acpi_os_unmap_memory(table, length); + + /* Obtain the DSDT and FACS tables via their addresses within the FADT */ + + acpi_tb_install_fixed_table((acpi_physical_address) acpi_gbl_FADT.Xdsdt, + ACPI_SIG_DSDT, ACPI_TABLE_INDEX_DSDT); + + /* If Hardware Reduced flag is set, there is no FACS */ + + if (!acpi_gbl_reduced_hardware) { + acpi_tb_install_fixed_table((acpi_physical_address) + acpi_gbl_FADT.Xfacs, ACPI_SIG_FACS, + ACPI_TABLE_INDEX_FACS); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_create_local_fadt + * + * PARAMETERS: table - Pointer to BIOS FADT + * length - Length of the table + * + * RETURN: None + * + * DESCRIPTION: Get a local copy of the FADT and convert it to a common format. + * Performs validation on some important FADT fields. + * + * NOTE: We create a local copy of the FADT regardless of the version. + * + ******************************************************************************/ + +void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) +{ + /* + * Check if the FADT is larger than the largest table that we expect + * (the ACPI 5.0 version). If so, truncate the table, and issue + * a warning. + */ + if (length > sizeof(struct acpi_table_fadt)) { + ACPI_BIOS_WARNING((AE_INFO, + "FADT (revision %u) is longer than ACPI 5.0 version, " + "truncating length %u to %u", + table->revision, length, + (u32)sizeof(struct acpi_table_fadt))); + } + + /* Clear the entire local FADT */ + + ACPI_MEMSET(&acpi_gbl_FADT, 0, sizeof(struct acpi_table_fadt)); + + /* Copy the original FADT, up to sizeof (struct acpi_table_fadt) */ + + ACPI_MEMCPY(&acpi_gbl_FADT, table, + ACPI_MIN(length, sizeof(struct acpi_table_fadt))); + + /* Take a copy of the Hardware Reduced flag */ + + acpi_gbl_reduced_hardware = FALSE; + if (acpi_gbl_FADT.flags & ACPI_FADT_HW_REDUCED) { + acpi_gbl_reduced_hardware = TRUE; + } + + /* Convert the local copy of the FADT to the common internal format */ + + acpi_tb_convert_fadt(); + + /* Initialize the global ACPI register structures */ + + acpi_tb_setup_fadt_registers(); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_convert_fadt + * + * PARAMETERS: none - acpi_gbl_FADT is used. + * + * RETURN: None + * + * DESCRIPTION: Converts all versions of the FADT to a common internal format. + * Expand 32-bit addresses to 64-bit as necessary. Also validate + * important fields within the FADT. + * + * NOTE: acpi_gbl_FADT must be of size (struct acpi_table_fadt), and must + * contain a copy of the actual BIOS-provided FADT. + * + * Notes on 64-bit register addresses: + * + * After this FADT conversion, later ACPICA code will only use the 64-bit "X" + * fields of the FADT for all ACPI register addresses. + * + * The 64-bit X fields are optional extensions to the original 32-bit FADT + * V1.0 fields. Even if they are present in the FADT, they are optional and + * are unused if the BIOS sets them to zero. Therefore, we must copy/expand + * 32-bit V1.0 fields to the 64-bit X fields if the the 64-bit X field is + * originally zero. + * + * For ACPI 1.0 FADTs (that contain no 64-bit addresses), all 32-bit address + * fields are expanded to the corresponding 64-bit X fields in the internal + * common FADT. + * + * For ACPI 2.0+ FADTs, all valid (non-zero) 32-bit address fields are expanded + * to the corresponding 64-bit X fields, if the 64-bit field is originally + * zero. Adhering to the ACPI specification, we completely ignore the 32-bit + * field if the 64-bit field is valid, regardless of whether the host OS is + * 32-bit or 64-bit. + * + * Possible additional checks: + * (acpi_gbl_FADT.pm1_event_length >= 4) + * (acpi_gbl_FADT.pm1_control_length >= 2) + * (acpi_gbl_FADT.pm_timer_length >= 4) + * Gpe block lengths must be multiple of 2 + * + ******************************************************************************/ + +static void acpi_tb_convert_fadt(void) +{ + char *name; + struct acpi_generic_address *address64; + u32 address32; + u8 length; + u8 flags; + u32 i; + + /* + * For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which + * should be zero are indeed zero. This will workaround BIOSs that + * inadvertently place values in these fields. + * + * The ACPI 1.0 reserved fields that will be zeroed are the bytes located + * at offset 45, 55, 95, and the word located at offset 109, 110. + * + * Note: The FADT revision value is unreliable. Only the length can be + * trusted. + */ + if (acpi_gbl_FADT.header.length <= ACPI_FADT_V2_SIZE) { + acpi_gbl_FADT.preferred_profile = 0; + acpi_gbl_FADT.pstate_control = 0; + acpi_gbl_FADT.cst_control = 0; + acpi_gbl_FADT.boot_flags = 0; + } + + /* + * Now we can update the local FADT length to the length of the + * current FADT version as defined by the ACPI specification. + * Thus, we will have a common FADT internally. + */ + acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt); + + /* + * Expand the 32-bit FACS and DSDT addresses to 64-bit as necessary. + * Later ACPICA code will always use the X 64-bit field. + */ + acpi_gbl_FADT.Xfacs = acpi_tb_select_address("FACS", + acpi_gbl_FADT.facs, + acpi_gbl_FADT.Xfacs); + + acpi_gbl_FADT.Xdsdt = acpi_tb_select_address("DSDT", + acpi_gbl_FADT.dsdt, + acpi_gbl_FADT.Xdsdt); + + /* If Hardware Reduced flag is set, we are all done */ + + if (acpi_gbl_reduced_hardware) { + return; + } + + /* Examine all of the 64-bit extended address fields (X fields) */ + + for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { + /* + * Get the 32-bit and 64-bit addresses, as well as the register + * length and register name. + */ + address32 = *ACPI_ADD_PTR(u32, + &acpi_gbl_FADT, + fadt_info_table[i].address32); + + address64 = ACPI_ADD_PTR(struct acpi_generic_address, + &acpi_gbl_FADT, + fadt_info_table[i].address64); + + length = *ACPI_ADD_PTR(u8, + &acpi_gbl_FADT, + fadt_info_table[i].length); + + name = fadt_info_table[i].name; + flags = fadt_info_table[i].flags; + + /* + * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X" + * generic address structures as necessary. Later code will always use + * the 64-bit address structures. + * + * November 2013: + * Now always use the 64-bit address if it is valid (non-zero), in + * accordance with the ACPI specification which states that a 64-bit + * address supersedes the 32-bit version. This behavior can be + * overridden by the acpi_gbl_use32_bit_fadt_addresses flag. + * + * During 64-bit address construction and verification, + * these cases are handled: + * + * Address32 zero, Address64 [don't care] - Use Address64 + * + * Address32 non-zero, Address64 zero - Copy/use Address32 + * Address32 non-zero == Address64 non-zero - Use Address64 + * Address32 non-zero != Address64 non-zero - Warning, use Address64 + * + * Override: if acpi_gbl_use32_bit_fadt_addresses is TRUE, and: + * Address32 non-zero != Address64 non-zero - Warning, copy/use Address32 + * + * Note: space_id is always I/O for 32-bit legacy address fields + */ + if (address32) { + if (!address64->address) { + + /* 64-bit address is zero, use 32-bit address */ + + acpi_tb_init_generic_address(address64, + ACPI_ADR_SPACE_SYSTEM_IO, + *ACPI_ADD_PTR(u8, + &acpi_gbl_FADT, + fadt_info_table + [i]. + length), + (u64)address32, + name, flags); + } else if (address64->address != (u64)address32) { + + /* Address mismatch */ + + ACPI_BIOS_WARNING((AE_INFO, + "32/64X address mismatch in FADT/%s: " + "0x%8.8X/0x%8.8X%8.8X, using %u-bit address", + name, address32, + ACPI_FORMAT_UINT64 + (address64->address), + acpi_gbl_use32_bit_fadt_addresses + ? 32 : 64)); + + if (acpi_gbl_use32_bit_fadt_addresses) { + + /* 32-bit address override */ + + acpi_tb_init_generic_address(address64, + ACPI_ADR_SPACE_SYSTEM_IO, + *ACPI_ADD_PTR + (u8, + &acpi_gbl_FADT, + fadt_info_table + [i]. + length), + (u64) + address32, + name, + flags); + } + } + } + + /* + * For each extended field, check for length mismatch between the + * legacy length field and the corresponding 64-bit X length field. + * Note: If the legacy length field is > 0xFF bits, ignore this + * check. (GPE registers can be larger than the 64-bit GAS structure + * can accomodate, 0xFF bits). + */ + if (address64->address && + (ACPI_MUL_8(length) <= ACPI_UINT8_MAX) && + (address64->bit_width != ACPI_MUL_8(length))) { + ACPI_BIOS_WARNING((AE_INFO, + "32/64X length mismatch in FADT/%s: %u/%u", + name, ACPI_MUL_8(length), + address64->bit_width)); + } + + if (fadt_info_table[i].flags & ACPI_FADT_REQUIRED) { + /* + * Field is required (Pm1a_event, Pm1a_control). + * Both the address and length must be non-zero. + */ + if (!address64->address || !length) { + ACPI_BIOS_ERROR((AE_INFO, + "Required FADT field %s has zero address and/or length: " + "0x%8.8X%8.8X/0x%X", + name, + ACPI_FORMAT_UINT64(address64-> + address), + length)); + } + } else if (fadt_info_table[i].flags & ACPI_FADT_SEPARATE_LENGTH) { + /* + * Field is optional (Pm2_control, GPE0, GPE1) AND has its own + * length field. If present, both the address and length must + * be valid. + */ + if ((address64->address && !length) || + (!address64->address && length)) { + ACPI_BIOS_WARNING((AE_INFO, + "Optional FADT field %s has zero address or length: " + "0x%8.8X%8.8X/0x%X", + name, + ACPI_FORMAT_UINT64 + (address64->address), + length)); + } + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_setup_fadt_registers + * + * PARAMETERS: None, uses acpi_gbl_FADT. + * + * RETURN: None + * + * DESCRIPTION: Initialize global ACPI PM1 register definitions. Optionally, + * force FADT register definitions to their default lengths. + * + ******************************************************************************/ + +static void acpi_tb_setup_fadt_registers(void) +{ + struct acpi_generic_address *target64; + struct acpi_generic_address *source64; + u8 pm1_register_byte_width; + u32 i; + + /* + * Optionally check all register lengths against the default values and + * update them if they are incorrect. + */ + if (acpi_gbl_use_default_register_widths) { + for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { + target64 = + ACPI_ADD_PTR(struct acpi_generic_address, + &acpi_gbl_FADT, + fadt_info_table[i].address64); + + /* + * If a valid register (Address != 0) and the (default_length > 0) + * (Not a GPE register), then check the width against the default. + */ + if ((target64->address) && + (fadt_info_table[i].default_length > 0) && + (fadt_info_table[i].default_length != + target64->bit_width)) { + ACPI_BIOS_WARNING((AE_INFO, + "Invalid length for FADT/%s: %u, using default %u", + fadt_info_table[i].name, + target64->bit_width, + fadt_info_table[i]. + default_length)); + + /* Incorrect size, set width to the default */ + + target64->bit_width = + fadt_info_table[i].default_length; + } + } + } + + /* + * Get the length of the individual PM1 registers (enable and status). + * Each register is defined to be (event block length / 2). Extra divide + * by 8 converts bits to bytes. + */ + pm1_register_byte_width = (u8) + ACPI_DIV_16(acpi_gbl_FADT.xpm1a_event_block.bit_width); + + /* + * Calculate separate GAS structs for the PM1x (A/B) Status and Enable + * registers. These addresses do not appear (directly) in the FADT, so it + * is useful to pre-calculate them from the PM1 Event Block definitions. + * + * The PM event blocks are split into two register blocks, first is the + * PM Status Register block, followed immediately by the PM Enable + * Register block. Each is of length (pm1_event_length/2) + * + * Note: The PM1A event block is required by the ACPI specification. + * However, the PM1B event block is optional and is rarely, if ever, + * used. + */ + + for (i = 0; i < ACPI_FADT_PM_INFO_ENTRIES; i++) { + source64 = + ACPI_ADD_PTR(struct acpi_generic_address, &acpi_gbl_FADT, + fadt_pm_info_table[i].source); + + if (source64->address) { + acpi_tb_init_generic_address(fadt_pm_info_table[i]. + target, source64->space_id, + pm1_register_byte_width, + source64->address + + (fadt_pm_info_table[i]. + register_num * + pm1_register_byte_width), + "PmRegisters", 0); + } + } +} diff --git a/kernel/drivers/acpi/acpica/tbfind.c b/kernel/drivers/acpi/acpica/tbfind.c new file mode 100644 index 000000000..0b879fcfe --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbfind.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Module Name: tbfind - find table + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbfind") + +/******************************************************************************* + * + * FUNCTION: acpi_tb_find_table + * + * PARAMETERS: signature - String with ACPI table signature + * oem_id - String with the table OEM ID + * oem_table_id - String with the OEM Table ID + * table_index - Where the table index is returned + * + * RETURN: Status and table index + * + * DESCRIPTION: Find an ACPI table (in the RSDT/XSDT) that matches the + * Signature, OEM ID and OEM Table ID. Returns an index that can + * be used to get the table header or entire table. + * + ******************************************************************************/ +acpi_status +acpi_tb_find_table(char *signature, + char *oem_id, char *oem_table_id, u32 *table_index) +{ + u32 i; + acpi_status status; + struct acpi_table_header header; + + ACPI_FUNCTION_TRACE(tb_find_table); + + /* Normalize the input strings */ + + ACPI_MEMSET(&header, 0, sizeof(struct acpi_table_header)); + ACPI_MOVE_NAME(header.signature, signature); + ACPI_STRNCPY(header.oem_id, oem_id, ACPI_OEM_ID_SIZE); + ACPI_STRNCPY(header.oem_table_id, oem_table_id, ACPI_OEM_TABLE_ID_SIZE); + + /* Search for the table */ + + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { + if (ACPI_MEMCMP(&(acpi_gbl_root_table_list.tables[i].signature), + header.signature, ACPI_NAME_SIZE)) { + + /* Not the requested table */ + + continue; + } + + /* Table with matching signature has been found */ + + if (!acpi_gbl_root_table_list.tables[i].pointer) { + + /* Table is not currently mapped, map it */ + + status = + acpi_tb_validate_table(&acpi_gbl_root_table_list. + tables[i]); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + if (!acpi_gbl_root_table_list.tables[i].pointer) { + continue; + } + } + + /* Check for table match on all IDs */ + + if (!ACPI_MEMCMP + (acpi_gbl_root_table_list.tables[i].pointer->signature, + header.signature, ACPI_NAME_SIZE) && (!oem_id[0] + || + !ACPI_MEMCMP + (acpi_gbl_root_table_list. + tables[i].pointer-> + oem_id, + header.oem_id, + ACPI_OEM_ID_SIZE)) + && (!oem_table_id[0] + || !ACPI_MEMCMP(acpi_gbl_root_table_list.tables[i]. + pointer->oem_table_id, + header.oem_table_id, + ACPI_OEM_TABLE_ID_SIZE))) { + *table_index = i; + + ACPI_DEBUG_PRINT((ACPI_DB_TABLES, + "Found table [%4.4s]\n", + header.signature)); + return_ACPI_STATUS(AE_OK); + } + } + + return_ACPI_STATUS(AE_NOT_FOUND); +} diff --git a/kernel/drivers/acpi/acpica/tbinstal.c b/kernel/drivers/acpi/acpica/tbinstal.c new file mode 100644 index 000000000..008a25178 --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbinstal.c @@ -0,0 +1,490 @@ +/****************************************************************************** + * + * Module Name: tbinstal - ACPI table installation and removal + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbinstal") + +/* Local prototypes */ +static u8 +acpi_tb_compare_tables(struct acpi_table_desc *table_desc, u32 table_index); + +/******************************************************************************* + * + * FUNCTION: acpi_tb_compare_tables + * + * PARAMETERS: table_desc - Table 1 descriptor to be compared + * table_index - Index of table 2 to be compared + * + * RETURN: TRUE if both tables are identical. + * + * DESCRIPTION: This function compares a table with another table that has + * already been installed in the root table list. + * + ******************************************************************************/ + +static u8 +acpi_tb_compare_tables(struct acpi_table_desc *table_desc, u32 table_index) +{ + acpi_status status = AE_OK; + u8 is_identical; + struct acpi_table_header *table; + u32 table_length; + u8 table_flags; + + status = + acpi_tb_acquire_table(&acpi_gbl_root_table_list.tables[table_index], + &table, &table_length, &table_flags); + if (ACPI_FAILURE(status)) { + return (FALSE); + } + + /* + * Check for a table match on the entire table length, + * not just the header. + */ + is_identical = (u8)((table_desc->length != table_length || + ACPI_MEMCMP(table_desc->pointer, table, + table_length)) ? FALSE : TRUE); + + /* Release the acquired table */ + + acpi_tb_release_table(table, table_length, table_flags); + return (is_identical); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_install_table_with_override + * + * PARAMETERS: table_index - Index into root table array + * new_table_desc - New table descriptor to install + * override - Whether override should be performed + * + * RETURN: None + * + * DESCRIPTION: Install an ACPI table into the global data structure. The + * table override mechanism is called to allow the host + * OS to replace any table before it is installed in the root + * table array. + * + ******************************************************************************/ + +void +acpi_tb_install_table_with_override(u32 table_index, + struct acpi_table_desc *new_table_desc, + u8 override) +{ + + if (table_index >= acpi_gbl_root_table_list.current_table_count) { + return; + } + + /* + * ACPI Table Override: + * + * Before we install the table, let the host OS override it with a new + * one if desired. Any table within the RSDT/XSDT can be replaced, + * including the DSDT which is pointed to by the FADT. + */ + if (override) { + acpi_tb_override_table(new_table_desc); + } + + acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list. + tables[table_index], + new_table_desc->address, + new_table_desc->flags, + new_table_desc->pointer); + + acpi_tb_print_table_header(new_table_desc->address, + new_table_desc->pointer); + + /* Set the global integer width (based upon revision of the DSDT) */ + + if (table_index == ACPI_TABLE_INDEX_DSDT) { + acpi_ut_set_integer_width(new_table_desc->pointer->revision); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_install_fixed_table + * + * PARAMETERS: address - Physical address of DSDT or FACS + * signature - Table signature, NULL if no need to + * match + * table_index - Index into root table array + * + * RETURN: Status + * + * DESCRIPTION: Install a fixed ACPI table (DSDT/FACS) into the global data + * structure. + * + ******************************************************************************/ + +acpi_status +acpi_tb_install_fixed_table(acpi_physical_address address, + char *signature, u32 table_index) +{ + struct acpi_table_desc new_table_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(tb_install_fixed_table); + + if (!address) { + ACPI_ERROR((AE_INFO, + "Null physical address for ACPI table [%s]", + signature)); + return (AE_NO_MEMORY); + } + + /* Fill a table descriptor for validation */ + + status = acpi_tb_acquire_temp_table(&new_table_desc, address, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not acquire table length at %8.8X%8.8X", + ACPI_FORMAT_UINT64(address))); + return_ACPI_STATUS(status); + } + + /* Validate and verify a table before installation */ + + status = acpi_tb_verify_temp_table(&new_table_desc, signature); + if (ACPI_FAILURE(status)) { + goto release_and_exit; + } + + acpi_tb_install_table_with_override(table_index, &new_table_desc, TRUE); + +release_and_exit: + + /* Release the temporary table descriptor */ + + acpi_tb_release_temp_table(&new_table_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_install_standard_table + * + * PARAMETERS: address - Address of the table (might be a virtual + * address depending on the table_flags) + * flags - Flags for the table + * reload - Whether reload should be performed + * override - Whether override should be performed + * table_index - Where the table index is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to install an ACPI table that is + * neither DSDT nor FACS (a "standard" table.) + * When this function is called by "Load" or "LoadTable" opcodes, + * or by acpi_load_table() API, the "Reload" parameter is set. + * After sucessfully returning from this function, table is + * "INSTALLED" but not "VALIDATED". + * + ******************************************************************************/ + +acpi_status +acpi_tb_install_standard_table(acpi_physical_address address, + u8 flags, + u8 reload, u8 override, u32 *table_index) +{ + u32 i; + acpi_status status = AE_OK; + struct acpi_table_desc new_table_desc; + + ACPI_FUNCTION_TRACE(tb_install_standard_table); + + /* Acquire a temporary table descriptor for validation */ + + status = acpi_tb_acquire_temp_table(&new_table_desc, address, flags); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, + "Could not acquire table length at %8.8X%8.8X", + ACPI_FORMAT_UINT64(address))); + return_ACPI_STATUS(status); + } + + /* + * Optionally do not load any SSDTs from the RSDT/XSDT. This can + * be useful for debugging ACPI problems on some machines. + */ + if (!reload && + acpi_gbl_disable_ssdt_table_install && + ACPI_COMPARE_NAME(&new_table_desc.signature, ACPI_SIG_SSDT)) { + ACPI_INFO((AE_INFO, + "Ignoring installation of %4.4s at %8.8X%8.8X", + new_table_desc.signature.ascii, + ACPI_FORMAT_UINT64(address))); + goto release_and_exit; + } + + /* Validate and verify a table before installation */ + + status = acpi_tb_verify_temp_table(&new_table_desc, NULL); + if (ACPI_FAILURE(status)) { + goto release_and_exit; + } + + if (reload) { + /* + * Validate the incoming table signature. + * + * 1) Originally, we checked the table signature for "SSDT" or "PSDT". + * 2) We added support for OEMx tables, signature "OEM". + * 3) Valid tables were encountered with a null signature, so we just + * gave up on validating the signature, (05/2008). + * 4) We encountered non-AML tables such as the MADT, which caused + * interpreter errors and kernel faults. So now, we once again allow + * only "SSDT", "OEMx", and now, also a null signature. (05/2011). + */ + if ((new_table_desc.signature.ascii[0] != 0x00) && + (!ACPI_COMPARE_NAME + (&new_table_desc.signature, ACPI_SIG_SSDT)) + && (ACPI_STRNCMP(new_table_desc.signature.ascii, "OEM", 3))) + { + ACPI_BIOS_ERROR((AE_INFO, + "Table has invalid signature [%4.4s] (0x%8.8X), " + "must be SSDT or OEMx", + acpi_ut_valid_acpi_name(new_table_desc. + signature. + ascii) ? + new_table_desc.signature. + ascii : "????", + new_table_desc.signature.integer)); + + status = AE_BAD_SIGNATURE; + goto release_and_exit; + } + + /* Check if table is already registered */ + + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; + ++i) { + /* + * Check for a table match on the entire table length, + * not just the header. + */ + if (!acpi_tb_compare_tables(&new_table_desc, i)) { + continue; + } + + /* + * Note: the current mechanism does not unregister a table if it is + * dynamically unloaded. The related namespace entries are deleted, + * but the table remains in the root table list. + * + * The assumption here is that the number of different tables that + * will be loaded is actually small, and there is minimal overhead + * in just keeping the table in case it is needed again. + * + * If this assumption changes in the future (perhaps on large + * machines with many table load/unload operations), tables will + * need to be unregistered when they are unloaded, and slots in the + * root table list should be reused when empty. + */ + if (acpi_gbl_root_table_list.tables[i]. + flags & ACPI_TABLE_IS_LOADED) { + + /* Table is still loaded, this is an error */ + + status = AE_ALREADY_EXISTS; + goto release_and_exit; + } else { + /* + * Table was unloaded, allow it to be reloaded. + * As we are going to return AE_OK to the caller, we should + * take the responsibility of freeing the input descriptor. + * Refill the input descriptor to ensure + * acpi_tb_install_table_with_override() can be called again to + * indicate the re-installation. + */ + acpi_tb_uninstall_table(&new_table_desc); + *table_index = i; + return_ACPI_STATUS(AE_OK); + } + } + } + + /* Add the table to the global root table list */ + + status = acpi_tb_get_next_table_descriptor(&i, NULL); + if (ACPI_FAILURE(status)) { + goto release_and_exit; + } + + *table_index = i; + acpi_tb_install_table_with_override(i, &new_table_desc, override); + +release_and_exit: + + /* Release the temporary table descriptor */ + + acpi_tb_release_temp_table(&new_table_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_override_table + * + * PARAMETERS: old_table_desc - Validated table descriptor to be + * overridden + * + * RETURN: None + * + * DESCRIPTION: Attempt table override by calling the OSL override functions. + * Note: If the table is overridden, then the entire new table + * is acquired and returned by this function. + * Before/after invocation, the table descriptor is in a state + * that is "VALIDATED". + * + ******************************************************************************/ + +void acpi_tb_override_table(struct acpi_table_desc *old_table_desc) +{ + acpi_status status; + char *override_type; + struct acpi_table_desc new_table_desc; + struct acpi_table_header *table; + acpi_physical_address address; + u32 length; + + /* (1) Attempt logical override (returns a logical address) */ + + status = acpi_os_table_override(old_table_desc->pointer, &table); + if (ACPI_SUCCESS(status) && table) { + acpi_tb_acquire_temp_table(&new_table_desc, + ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL); + override_type = "Logical"; + goto finish_override; + } + + /* (2) Attempt physical override (returns a physical address) */ + + status = acpi_os_physical_table_override(old_table_desc->pointer, + &address, &length); + if (ACPI_SUCCESS(status) && address && length) { + acpi_tb_acquire_temp_table(&new_table_desc, address, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL); + override_type = "Physical"; + goto finish_override; + } + + return; /* There was no override */ + +finish_override: + + /* Validate and verify a table before overriding */ + + status = acpi_tb_verify_temp_table(&new_table_desc, NULL); + if (ACPI_FAILURE(status)) { + return; + } + + ACPI_INFO((AE_INFO, "%4.4s 0x%8.8X%8.8X" + " %s table override, new table: 0x%8.8X%8.8X", + old_table_desc->signature.ascii, + ACPI_FORMAT_UINT64(old_table_desc->address), + override_type, ACPI_FORMAT_UINT64(new_table_desc.address))); + + /* We can now uninstall the original table */ + + acpi_tb_uninstall_table(old_table_desc); + + /* + * Replace the original table descriptor and keep its state as + * "VALIDATED". + */ + acpi_tb_init_table_descriptor(old_table_desc, new_table_desc.address, + new_table_desc.flags, + new_table_desc.pointer); + acpi_tb_validate_temp_table(old_table_desc); + + /* Release the temporary table descriptor */ + + acpi_tb_release_temp_table(&new_table_desc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_uninstall_table + * + * PARAMETERS: table_desc - Table descriptor + * + * RETURN: None + * + * DESCRIPTION: Delete one internal ACPI table + * + ******************************************************************************/ + +void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc) +{ + + ACPI_FUNCTION_TRACE(tb_uninstall_table); + + /* Table must be installed */ + + if (!table_desc->address) { + return_VOID; + } + + acpi_tb_invalidate_table(table_desc); + + if ((table_desc->flags & ACPI_TABLE_ORIGIN_MASK) == + ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL) { + ACPI_FREE(ACPI_PHYSADDR_TO_PTR(table_desc->address)); + } + + table_desc->address = ACPI_PTR_TO_PHYSADDR(NULL); + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/tbprint.c b/kernel/drivers/acpi/acpica/tbprint.c new file mode 100644 index 000000000..77ba5c71c --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbprint.c @@ -0,0 +1,244 @@ +/****************************************************************************** + * + * Module Name: tbprint - Table output utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbprint") + +/* Local prototypes */ +static void acpi_tb_fix_string(char *string, acpi_size length); + +static void +acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, + struct acpi_table_header *header); + +/******************************************************************************* + * + * FUNCTION: acpi_tb_fix_string + * + * PARAMETERS: string - String to be repaired + * length - Maximum length + * + * RETURN: None + * + * DESCRIPTION: Replace every non-printable or non-ascii byte in the string + * with a question mark '?'. + * + ******************************************************************************/ + +static void acpi_tb_fix_string(char *string, acpi_size length) +{ + + while (length && *string) { + if (!ACPI_IS_PRINT(*string)) { + *string = '?'; + } + string++; + length--; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_cleanup_table_header + * + * PARAMETERS: out_header - Where the cleaned header is returned + * header - Input ACPI table header + * + * RETURN: Returns the cleaned header in out_header + * + * DESCRIPTION: Copy the table header and ensure that all "string" fields in + * the header consist of printable characters. + * + ******************************************************************************/ + +static void +acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, + struct acpi_table_header *header) +{ + + ACPI_MEMCPY(out_header, header, sizeof(struct acpi_table_header)); + + acpi_tb_fix_string(out_header->signature, ACPI_NAME_SIZE); + acpi_tb_fix_string(out_header->oem_id, ACPI_OEM_ID_SIZE); + acpi_tb_fix_string(out_header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); + acpi_tb_fix_string(out_header->asl_compiler_id, ACPI_NAME_SIZE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_print_table_header + * + * PARAMETERS: address - Table physical address + * header - Table header + * + * RETURN: None + * + * DESCRIPTION: Print an ACPI table header. Special cases for FACS and RSDP. + * + ******************************************************************************/ + +void +acpi_tb_print_table_header(acpi_physical_address address, + struct acpi_table_header *header) +{ + struct acpi_table_header local_header; + + if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_FACS)) { + + /* FACS only has signature and length fields */ + + ACPI_INFO((AE_INFO, "%-4.4s 0x%8.8X%8.8X %06X", + header->signature, ACPI_FORMAT_UINT64(address), + header->length)); + } else if (ACPI_VALIDATE_RSDP_SIG(header->signature)) { + + /* RSDP has no common fields */ + + ACPI_MEMCPY(local_header.oem_id, + ACPI_CAST_PTR(struct acpi_table_rsdp, + header)->oem_id, ACPI_OEM_ID_SIZE); + acpi_tb_fix_string(local_header.oem_id, ACPI_OEM_ID_SIZE); + + ACPI_INFO((AE_INFO, "RSDP 0x%8.8X%8.8X %06X (v%.2d %-6.6s)", + ACPI_FORMAT_UINT64(address), + (ACPI_CAST_PTR(struct acpi_table_rsdp, header)-> + revision > + 0) ? ACPI_CAST_PTR(struct acpi_table_rsdp, + header)->length : 20, + ACPI_CAST_PTR(struct acpi_table_rsdp, + header)->revision, + local_header.oem_id)); + } else { + /* Standard ACPI table with full common header */ + + acpi_tb_cleanup_table_header(&local_header, header); + + ACPI_INFO((AE_INFO, + "%-4.4s 0x%8.8X%8.8X" + " %06X (v%.2d %-6.6s %-8.8s %08X %-4.4s %08X)", + local_header.signature, ACPI_FORMAT_UINT64(address), + local_header.length, local_header.revision, + local_header.oem_id, local_header.oem_table_id, + local_header.oem_revision, + local_header.asl_compiler_id, + local_header.asl_compiler_revision)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_validate_checksum + * + * PARAMETERS: table - ACPI table to verify + * length - Length of entire table + * + * RETURN: Status + * + * DESCRIPTION: Verifies that the table checksums to zero. Optionally returns + * exception on bad checksum. + * + ******************************************************************************/ + +acpi_status acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length) +{ + u8 checksum; + + /* + * FACS/S3PT: + * They are the odd tables, have no standard ACPI header and no checksum + */ + + if (ACPI_COMPARE_NAME(table->signature, ACPI_SIG_S3PT) || + ACPI_COMPARE_NAME(table->signature, ACPI_SIG_FACS)) { + return (AE_OK); + } + + /* Compute the checksum on the table */ + + checksum = acpi_tb_checksum(ACPI_CAST_PTR(u8, table), length); + + /* Checksum ok? (should be zero) */ + + if (checksum) { + ACPI_BIOS_WARNING((AE_INFO, + "Incorrect checksum in table [%4.4s] - 0x%2.2X, " + "should be 0x%2.2X", + table->signature, table->checksum, + (u8)(table->checksum - checksum))); + +#if (ACPI_CHECKSUM_ABORT) + return (AE_BAD_CHECKSUM); +#endif + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_checksum + * + * PARAMETERS: buffer - Pointer to memory region to be checked + * length - Length of this memory region + * + * RETURN: Checksum (u8) + * + * DESCRIPTION: Calculates circular checksum of memory region. + * + ******************************************************************************/ + +u8 acpi_tb_checksum(u8 *buffer, u32 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) { + sum = (u8)(sum + *(buffer++)); + } + + return (sum); +} diff --git a/kernel/drivers/acpi/acpica/tbutils.c b/kernel/drivers/acpi/acpica/tbutils.c new file mode 100644 index 000000000..6559a5843 --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbutils.c @@ -0,0 +1,401 @@ +/****************************************************************************** + * + * Module Name: tbutils - ACPI Table utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbutils") + +/* Local prototypes */ +static acpi_physical_address +acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size); + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_tb_initialize_facs + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create a permanent mapping for the FADT and save it in a global + * for accessing the Global Lock and Firmware Waking Vector + * + ******************************************************************************/ + +acpi_status acpi_tb_initialize_facs(void) +{ + acpi_status status; + + /* If Hardware Reduced flag is set, there is no FACS */ + + if (acpi_gbl_reduced_hardware) { + acpi_gbl_FACS = NULL; + return (AE_OK); + } + + status = acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, + ACPI_CAST_INDIRECT_PTR(struct + acpi_table_header, + &acpi_gbl_FACS)); + return (status); +} +#endif /* !ACPI_REDUCED_HARDWARE */ + +/******************************************************************************* + * + * FUNCTION: acpi_tb_tables_loaded + * + * PARAMETERS: None + * + * RETURN: TRUE if required ACPI tables are loaded + * + * DESCRIPTION: Determine if the minimum required ACPI tables are present + * (FADT, FACS, DSDT) + * + ******************************************************************************/ + +u8 acpi_tb_tables_loaded(void) +{ + + if (acpi_gbl_root_table_list.current_table_count >= 3) { + return (TRUE); + } + + return (FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_check_dsdt_header + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Quick compare to check validity of the DSDT. This will detect + * if the DSDT has been replaced from outside the OS and/or if + * the DSDT header has been corrupted. + * + ******************************************************************************/ + +void acpi_tb_check_dsdt_header(void) +{ + + /* Compare original length and checksum to current values */ + + if (acpi_gbl_original_dsdt_header.length != acpi_gbl_DSDT->length || + acpi_gbl_original_dsdt_header.checksum != acpi_gbl_DSDT->checksum) { + ACPI_BIOS_ERROR((AE_INFO, + "The DSDT has been corrupted or replaced - " + "old, new headers below")); + acpi_tb_print_table_header(0, &acpi_gbl_original_dsdt_header); + acpi_tb_print_table_header(0, acpi_gbl_DSDT); + + ACPI_ERROR((AE_INFO, + "Please send DMI info to linux-acpi@vger.kernel.org\n" + "If system does not work as expected, please boot with acpi=copy_dsdt")); + + /* Disable further error messages */ + + acpi_gbl_original_dsdt_header.length = acpi_gbl_DSDT->length; + acpi_gbl_original_dsdt_header.checksum = + acpi_gbl_DSDT->checksum; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_copy_dsdt + * + * PARAMETERS: table_desc - Installed table to copy + * + * RETURN: None + * + * DESCRIPTION: Implements a subsystem option to copy the DSDT to local memory. + * Some very bad BIOSs are known to either corrupt the DSDT or + * install a new, bad DSDT. This copy works around the problem. + * + ******************************************************************************/ + +struct acpi_table_header *acpi_tb_copy_dsdt(u32 table_index) +{ + struct acpi_table_header *new_table; + struct acpi_table_desc *table_desc; + + table_desc = &acpi_gbl_root_table_list.tables[table_index]; + + new_table = ACPI_ALLOCATE(table_desc->length); + if (!new_table) { + ACPI_ERROR((AE_INFO, "Could not copy DSDT of length 0x%X", + table_desc->length)); + return (NULL); + } + + ACPI_MEMCPY(new_table, table_desc->pointer, table_desc->length); + acpi_tb_uninstall_table(table_desc); + + acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list. + tables[ACPI_TABLE_INDEX_DSDT], + ACPI_PTR_TO_PHYSADDR(new_table), + ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, + new_table); + + ACPI_INFO((AE_INFO, + "Forced DSDT copy: length 0x%05X copied locally, original unmapped", + new_table->length)); + + return (new_table); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_root_table_entry + * + * PARAMETERS: table_entry - Pointer to the RSDT/XSDT table entry + * table_entry_size - sizeof 32 or 64 (RSDT or XSDT) + * + * RETURN: Physical address extracted from the root table + * + * DESCRIPTION: Get one root table entry. Handles 32-bit and 64-bit cases on + * both 32-bit and 64-bit platforms + * + * NOTE: acpi_physical_address is 32-bit on 32-bit platforms, 64-bit on + * 64-bit platforms. + * + ******************************************************************************/ + +static acpi_physical_address +acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size) +{ + u64 address64; + + /* + * Get the table physical address (32-bit for RSDT, 64-bit for XSDT): + * Note: Addresses are 32-bit aligned (not 64) in both RSDT and XSDT + */ + if (table_entry_size == ACPI_RSDT_ENTRY_SIZE) { + /* + * 32-bit platform, RSDT: Return 32-bit table entry + * 64-bit platform, RSDT: Expand 32-bit to 64-bit and return + */ + return ((acpi_physical_address) + (*ACPI_CAST_PTR(u32, table_entry))); + } else { + /* + * 32-bit platform, XSDT: Truncate 64-bit to 32-bit and return + * 64-bit platform, XSDT: Move (unaligned) 64-bit to local, + * return 64-bit + */ + ACPI_MOVE_64_TO_64(&address64, table_entry); + +#if ACPI_MACHINE_WIDTH == 32 + if (address64 > ACPI_UINT32_MAX) { + + /* Will truncate 64-bit address to 32 bits, issue warning */ + + ACPI_BIOS_WARNING((AE_INFO, + "64-bit Physical Address in XSDT is too large (0x%8.8X%8.8X)," + " truncating", + ACPI_FORMAT_UINT64(address64))); + } +#endif + return ((acpi_physical_address) (address64)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_parse_root_table + * + * PARAMETERS: rsdp - Pointer to the RSDP + * + * RETURN: Status + * + * DESCRIPTION: This function is called to parse the Root System Description + * Table (RSDT or XSDT) + * + * NOTE: Tables are mapped (not copied) for efficiency. The FACS must + * be mapped and cannot be copied because it contains the actual + * memory location of the ACPI Global Lock. + * + ******************************************************************************/ + +acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) +{ + struct acpi_table_rsdp *rsdp; + u32 table_entry_size; + u32 i; + u32 table_count; + struct acpi_table_header *table; + acpi_physical_address address; + u32 length; + u8 *table_entry; + acpi_status status; + u32 table_index; + + ACPI_FUNCTION_TRACE(tb_parse_root_table); + + /* Map the entire RSDP and extract the address of the RSDT or XSDT */ + + rsdp = acpi_os_map_memory(rsdp_address, sizeof(struct acpi_table_rsdp)); + if (!rsdp) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + acpi_tb_print_table_header(rsdp_address, + ACPI_CAST_PTR(struct acpi_table_header, + rsdp)); + + /* Use XSDT if present and not overridden. Otherwise, use RSDT */ + + if ((rsdp->revision > 1) && + rsdp->xsdt_physical_address && !acpi_gbl_do_not_use_xsdt) { + /* + * RSDP contains an XSDT (64-bit physical addresses). We must use + * the XSDT if the revision is > 1 and the XSDT pointer is present, + * as per the ACPI specification. + */ + address = (acpi_physical_address) rsdp->xsdt_physical_address; + table_entry_size = ACPI_XSDT_ENTRY_SIZE; + } else { + /* Root table is an RSDT (32-bit physical addresses) */ + + address = (acpi_physical_address) rsdp->rsdt_physical_address; + table_entry_size = ACPI_RSDT_ENTRY_SIZE; + } + + /* + * It is not possible to map more than one entry in some environments, + * so unmap the RSDP here before mapping other tables + */ + acpi_os_unmap_memory(rsdp, sizeof(struct acpi_table_rsdp)); + + /* Map the RSDT/XSDT table header to get the full table length */ + + table = acpi_os_map_memory(address, sizeof(struct acpi_table_header)); + if (!table) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + acpi_tb_print_table_header(address, table); + + /* + * Validate length of the table, and map entire table. + * Minimum length table must contain at least one entry. + */ + length = table->length; + acpi_os_unmap_memory(table, sizeof(struct acpi_table_header)); + + if (length < (sizeof(struct acpi_table_header) + table_entry_size)) { + ACPI_BIOS_ERROR((AE_INFO, + "Invalid table length 0x%X in RSDT/XSDT", + length)); + return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); + } + + table = acpi_os_map_memory(address, length); + if (!table) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Validate the root table checksum */ + + status = acpi_tb_verify_checksum(table, length); + if (ACPI_FAILURE(status)) { + acpi_os_unmap_memory(table, length); + return_ACPI_STATUS(status); + } + + /* Get the number of entries and pointer to first entry */ + + table_count = (u32)((table->length - sizeof(struct acpi_table_header)) / + table_entry_size); + table_entry = ACPI_ADD_PTR(u8, table, sizeof(struct acpi_table_header)); + + /* + * First two entries in the table array are reserved for the DSDT + * and FACS, which are not actually present in the RSDT/XSDT - they + * come from the FADT + */ + acpi_gbl_root_table_list.current_table_count = 2; + + /* Initialize the root table array from the RSDT/XSDT */ + + for (i = 0; i < table_count; i++) { + + /* Get the table physical address (32-bit for RSDT, 64-bit for XSDT) */ + + address = + acpi_tb_get_root_table_entry(table_entry, table_entry_size); + + /* Skip NULL entries in RSDT/XSDT */ + + if (!address) { + goto next_table; + } + + status = acpi_tb_install_standard_table(address, + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL, + FALSE, TRUE, + &table_index); + + if (ACPI_SUCCESS(status) && + ACPI_COMPARE_NAME(&acpi_gbl_root_table_list. + tables[table_index].signature, + ACPI_SIG_FADT)) { + acpi_tb_parse_fadt(table_index); + } + +next_table: + + table_entry += table_entry_size; + } + + acpi_os_unmap_memory(table, length); + + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/tbxface.c b/kernel/drivers/acpi/acpica/tbxface.c new file mode 100644 index 000000000..60e94f87f --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbxface.c @@ -0,0 +1,481 @@ +/****************************************************************************** + * + * Module Name: tbxface - ACPI table-oriented external interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbxface") + +/******************************************************************************* + * + * FUNCTION: acpi_allocate_root_table + * + * PARAMETERS: initial_table_count - Size of initial_table_array, in number of + * struct acpi_table_desc structures + * + * RETURN: Status + * + * DESCRIPTION: Allocate a root table array. Used by iASL compiler and + * acpi_initialize_tables. + * + ******************************************************************************/ +acpi_status acpi_allocate_root_table(u32 initial_table_count) +{ + + acpi_gbl_root_table_list.max_table_count = initial_table_count; + acpi_gbl_root_table_list.flags = ACPI_ROOT_ALLOW_RESIZE; + + return (acpi_tb_resize_root_table_list()); +} + +/******************************************************************************* + * + * FUNCTION: acpi_initialize_tables + * + * PARAMETERS: initial_table_array - Pointer to an array of pre-allocated + * struct acpi_table_desc structures. If NULL, the + * array is dynamically allocated. + * initial_table_count - Size of initial_table_array, in number of + * struct acpi_table_desc structures + * allow_resize - Flag to tell Table Manager if resize of + * pre-allocated array is allowed. Ignored + * if initial_table_array is NULL. + * + * RETURN: Status + * + * DESCRIPTION: Initialize the table manager, get the RSDP and RSDT/XSDT. + * + * NOTE: Allows static allocation of the initial table array in order + * to avoid the use of dynamic memory in confined environments + * such as the kernel boot sequence where it may not be available. + * + * If the host OS memory managers are initialized, use NULL for + * initial_table_array, and the table will be dynamically allocated. + * + ******************************************************************************/ + +acpi_status __init +acpi_initialize_tables(struct acpi_table_desc * initial_table_array, + u32 initial_table_count, u8 allow_resize) +{ + acpi_physical_address rsdp_address; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_initialize_tables); + + /* + * Setup the Root Table Array and allocate the table array + * if requested + */ + if (!initial_table_array) { + status = acpi_allocate_root_table(initial_table_count); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } else { + /* Root Table Array has been statically allocated by the host */ + + ACPI_MEMSET(initial_table_array, 0, + (acpi_size) initial_table_count * + sizeof(struct acpi_table_desc)); + + acpi_gbl_root_table_list.tables = initial_table_array; + acpi_gbl_root_table_list.max_table_count = initial_table_count; + acpi_gbl_root_table_list.flags = ACPI_ROOT_ORIGIN_UNKNOWN; + if (allow_resize) { + acpi_gbl_root_table_list.flags |= + ACPI_ROOT_ALLOW_RESIZE; + } + } + + /* Get the address of the RSDP */ + + rsdp_address = acpi_os_get_root_pointer(); + if (!rsdp_address) { + return_ACPI_STATUS(AE_NOT_FOUND); + } + + /* + * Get the root table (RSDT or XSDT) and extract all entries to the local + * Root Table Array. This array contains the information of the RSDT/XSDT + * in a common, more useable format. + */ + status = acpi_tb_parse_root_table(rsdp_address); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_tables) + +/******************************************************************************* + * + * FUNCTION: acpi_reallocate_root_table + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Reallocate Root Table List into dynamic memory. Copies the + * root list from the previously provided scratch area. Should + * be called once dynamic memory allocation is available in the + * kernel. + * + ******************************************************************************/ +acpi_status __init acpi_reallocate_root_table(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_reallocate_root_table); + + /* + * Only reallocate the root table if the host provided a static buffer + * for the table array in the call to acpi_initialize_tables. + */ + if (acpi_gbl_root_table_list.flags & ACPI_ROOT_ORIGIN_ALLOCATED) { + return_ACPI_STATUS(AE_SUPPORT); + } + + acpi_gbl_root_table_list.flags |= ACPI_ROOT_ALLOW_RESIZE; + + status = acpi_tb_resize_root_table_list(); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_reallocate_root_table) + +/******************************************************************************* + * + * FUNCTION: acpi_get_table_header + * + * PARAMETERS: signature - ACPI signature of needed table + * instance - Which instance (for SSDTs) + * out_table_header - The pointer to the table header to fill + * + * RETURN: Status and pointer to mapped table header + * + * DESCRIPTION: Finds an ACPI table header. + * + * NOTE: Caller is responsible in unmapping the header with + * acpi_os_unmap_memory + * + ******************************************************************************/ +acpi_status +acpi_get_table_header(char *signature, + u32 instance, struct acpi_table_header *out_table_header) +{ + u32 i; + u32 j; + struct acpi_table_header *header; + + /* Parameter validation */ + + if (!signature || !out_table_header) { + return (AE_BAD_PARAMETER); + } + + /* Walk the root table list */ + + for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count; + i++) { + if (!ACPI_COMPARE_NAME + (&(acpi_gbl_root_table_list.tables[i].signature), + signature)) { + continue; + } + + if (++j < instance) { + continue; + } + + if (!acpi_gbl_root_table_list.tables[i].pointer) { + if ((acpi_gbl_root_table_list.tables[i].flags & + ACPI_TABLE_ORIGIN_MASK) == + ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL) { + header = + acpi_os_map_memory(acpi_gbl_root_table_list. + tables[i].address, + sizeof(struct + acpi_table_header)); + if (!header) { + return (AE_NO_MEMORY); + } + ACPI_MEMCPY(out_table_header, header, + sizeof(struct acpi_table_header)); + acpi_os_unmap_memory(header, + sizeof(struct + acpi_table_header)); + } else { + return (AE_NOT_FOUND); + } + } else { + ACPI_MEMCPY(out_table_header, + acpi_gbl_root_table_list.tables[i].pointer, + sizeof(struct acpi_table_header)); + } + return (AE_OK); + } + + return (AE_NOT_FOUND); +} + +ACPI_EXPORT_SYMBOL(acpi_get_table_header) + +/******************************************************************************* + * + * FUNCTION: acpi_get_table_with_size + * + * PARAMETERS: signature - ACPI signature of needed table + * instance - Which instance (for SSDTs) + * out_table - Where the pointer to the table is returned + * + * RETURN: Status and pointer to the requested table + * + * DESCRIPTION: Finds and verifies an ACPI table. Table must be in the + * RSDT/XSDT. + * + ******************************************************************************/ +acpi_status +acpi_get_table_with_size(char *signature, + u32 instance, struct acpi_table_header **out_table, + acpi_size *tbl_size) +{ + u32 i; + u32 j; + acpi_status status; + + /* Parameter validation */ + + if (!signature || !out_table) { + return (AE_BAD_PARAMETER); + } + + /* Walk the root table list */ + + for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count; + i++) { + if (!ACPI_COMPARE_NAME + (&(acpi_gbl_root_table_list.tables[i].signature), + signature)) { + continue; + } + + if (++j < instance) { + continue; + } + + status = + acpi_tb_validate_table(&acpi_gbl_root_table_list.tables[i]); + if (ACPI_SUCCESS(status)) { + *out_table = acpi_gbl_root_table_list.tables[i].pointer; + *tbl_size = acpi_gbl_root_table_list.tables[i].length; + } + + if (!acpi_gbl_permanent_mmap) { + acpi_gbl_root_table_list.tables[i].pointer = NULL; + } + + return (status); + } + + return (AE_NOT_FOUND); +} + +ACPI_EXPORT_SYMBOL(acpi_get_table_with_size) + +acpi_status +acpi_get_table(char *signature, + u32 instance, struct acpi_table_header **out_table) +{ + acpi_size tbl_size; + + return acpi_get_table_with_size(signature, + instance, out_table, &tbl_size); +} + +ACPI_EXPORT_SYMBOL(acpi_get_table) + +/******************************************************************************* + * + * FUNCTION: acpi_get_table_by_index + * + * PARAMETERS: table_index - Table index + * table - Where the pointer to the table is returned + * + * RETURN: Status and pointer to the requested table + * + * DESCRIPTION: Obtain a table by an index into the global table list. Used + * internally also. + * + ******************************************************************************/ +acpi_status +acpi_get_table_by_index(u32 table_index, struct acpi_table_header ** table) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_get_table_by_index); + + /* Parameter validation */ + + if (!table) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + + /* Validate index */ + + if (table_index >= acpi_gbl_root_table_list.current_table_count) { + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (!acpi_gbl_root_table_list.tables[table_index].pointer) { + + /* Table is not mapped, map it */ + + status = + acpi_tb_validate_table(&acpi_gbl_root_table_list. + tables[table_index]); + if (ACPI_FAILURE(status)) { + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(status); + } + } + + *table = acpi_gbl_root_table_list.tables[table_index].pointer; + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_get_table_by_index) + +/******************************************************************************* + * + * FUNCTION: acpi_install_table_handler + * + * PARAMETERS: handler - Table event handler + * context - Value passed to the handler on each event + * + * RETURN: Status + * + * DESCRIPTION: Install a global table event handler. + * + ******************************************************************************/ +acpi_status +acpi_install_table_handler(acpi_table_handler handler, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_table_handler); + + if (!handler) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Don't allow more than one handler */ + + if (acpi_gbl_table_handler) { + status = AE_ALREADY_EXISTS; + goto cleanup; + } + + /* Install the handler */ + + acpi_gbl_table_handler = handler; + acpi_gbl_table_handler_context = context; + +cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_table_handler) + +/******************************************************************************* + * + * FUNCTION: acpi_remove_table_handler + * + * PARAMETERS: handler - Table event handler that was installed + * previously. + * + * RETURN: Status + * + * DESCRIPTION: Remove a table event handler + * + ******************************************************************************/ +acpi_status acpi_remove_table_handler(acpi_table_handler handler) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_remove_table_handler); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Make sure that the installed handler is the same */ + + if (!handler || handler != acpi_gbl_table_handler) { + status = AE_BAD_PARAMETER; + goto cleanup; + } + + /* Remove the handler */ + + acpi_gbl_table_handler = NULL; + +cleanup: + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_table_handler) diff --git a/kernel/drivers/acpi/acpica/tbxfload.c b/kernel/drivers/acpi/acpica/tbxfload.c new file mode 100644 index 000000000..aadb3002a --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbxfload.c @@ -0,0 +1,418 @@ +/****************************************************************************** + * + * Module Name: tbxfload - Table load/unload external interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbxfload") + +/* Local prototypes */ +static acpi_status acpi_tb_load_namespace(void); + +/******************************************************************************* + * + * FUNCTION: acpi_load_tables + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Load the ACPI tables from the RSDT/XSDT + * + ******************************************************************************/ + +acpi_status __init acpi_load_tables(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_load_tables); + + /* Load the namespace from the tables */ + + status = acpi_tb_load_namespace(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While loading namespace from ACPI tables")); + } + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_load_tables) + +/******************************************************************************* + * + * FUNCTION: acpi_tb_load_namespace + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Load the namespace from the DSDT and all SSDTs/PSDTs found in + * the RSDT/XSDT. + * + ******************************************************************************/ +static acpi_status acpi_tb_load_namespace(void) +{ + acpi_status status; + u32 i; + struct acpi_table_header *new_dsdt; + + ACPI_FUNCTION_TRACE(tb_load_namespace); + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + + /* + * Load the namespace. The DSDT is required, but any SSDT and + * PSDT tables are optional. Verify the DSDT. + */ + if (!acpi_gbl_root_table_list.current_table_count || + !ACPI_COMPARE_NAME(& + (acpi_gbl_root_table_list. + tables[ACPI_TABLE_INDEX_DSDT].signature), + ACPI_SIG_DSDT) + || + ACPI_FAILURE(acpi_tb_validate_table + (&acpi_gbl_root_table_list. + tables[ACPI_TABLE_INDEX_DSDT]))) { + status = AE_NO_ACPI_TABLES; + goto unlock_and_exit; + } + + /* + * Save the DSDT pointer for simple access. This is the mapped memory + * address. We must take care here because the address of the .Tables + * array can change dynamically as tables are loaded at run-time. Note: + * .Pointer field is not validated until after call to acpi_tb_validate_table. + */ + acpi_gbl_DSDT = + acpi_gbl_root_table_list.tables[ACPI_TABLE_INDEX_DSDT].pointer; + + /* + * Optionally copy the entire DSDT to local memory (instead of simply + * mapping it.) There are some BIOSs that corrupt or replace the original + * DSDT, creating the need for this option. Default is FALSE, do not copy + * the DSDT. + */ + if (acpi_gbl_copy_dsdt_locally) { + new_dsdt = acpi_tb_copy_dsdt(ACPI_TABLE_INDEX_DSDT); + if (new_dsdt) { + acpi_gbl_DSDT = new_dsdt; + } + } + + /* + * Save the original DSDT header for detection of table corruption + * and/or replacement of the DSDT from outside the OS. + */ + ACPI_MEMCPY(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT, + sizeof(struct acpi_table_header)); + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + + /* Load and parse tables */ + + status = acpi_ns_load_table(ACPI_TABLE_INDEX_DSDT, acpi_gbl_root_node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */ + + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { + if ((!ACPI_COMPARE_NAME + (&(acpi_gbl_root_table_list.tables[i].signature), + ACPI_SIG_SSDT) + && + !ACPI_COMPARE_NAME(& + (acpi_gbl_root_table_list.tables[i]. + signature), ACPI_SIG_PSDT)) + || + ACPI_FAILURE(acpi_tb_validate_table + (&acpi_gbl_root_table_list.tables[i]))) { + continue; + } + + /* Ignore errors while loading tables, get as many as possible */ + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + (void)acpi_ns_load_table(i, acpi_gbl_root_node); + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + } + + ACPI_INFO((AE_INFO, "All ACPI Tables successfully acquired")); + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_install_table + * + * PARAMETERS: address - Address of the ACPI table to be installed. + * physical - Whether the address is a physical table + * address or not + * + * RETURN: Status + * + * DESCRIPTION: Dynamically install an ACPI table. + * Note: This function should only be invoked after + * acpi_initialize_tables() and before acpi_load_tables(). + * + ******************************************************************************/ + +acpi_status __init +acpi_install_table(acpi_physical_address address, u8 physical) +{ + acpi_status status; + u8 flags; + u32 table_index; + + ACPI_FUNCTION_TRACE(acpi_install_table); + + if (physical) { + flags = ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL; + } else { + flags = ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL; + } + + status = acpi_tb_install_standard_table(address, flags, + FALSE, FALSE, &table_index); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_install_table) + +/******************************************************************************* + * + * FUNCTION: acpi_load_table + * + * PARAMETERS: table - Pointer to a buffer containing the ACPI + * table to be loaded. + * + * RETURN: Status + * + * DESCRIPTION: Dynamically load an ACPI table from the caller's buffer. Must + * be a valid ACPI table with a valid ACPI table header. + * Note1: Mainly intended to support hotplug addition of SSDTs. + * Note2: Does not copy the incoming table. User is responsible + * to ensure that the table is not deleted or unmapped. + * + ******************************************************************************/ +acpi_status acpi_load_table(struct acpi_table_header *table) +{ + acpi_status status; + u32 table_index; + + ACPI_FUNCTION_TRACE(acpi_load_table); + + /* Parameter validation */ + + if (!table) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Must acquire the interpreter lock during this operation */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Install the table and load it into the namespace */ + + ACPI_INFO((AE_INFO, "Host-directed Dynamic ACPI Table Load:")); + (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + + status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table), + ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL, + TRUE, FALSE, &table_index); + + (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* + * Note: Now table is "INSTALLED", it must be validated before + * using. + */ + status = + acpi_tb_validate_table(&acpi_gbl_root_table_list. + tables[table_index]); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + status = acpi_ns_load_table(table_index, acpi_gbl_root_node); + + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table, + acpi_gbl_table_handler_context); + } + +unlock_and_exit: + (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_load_table) + +/******************************************************************************* + * + * FUNCTION: acpi_unload_parent_table + * + * PARAMETERS: object - Handle to any namespace object owned by + * the table to be unloaded + * + * RETURN: Status + * + * DESCRIPTION: Via any namespace object within an SSDT or OEMx table, unloads + * the table and deletes all namespace objects associated with + * that table. Unloading of the DSDT is not allowed. + * Note: Mainly intended to support hotplug removal of SSDTs. + * + ******************************************************************************/ +acpi_status acpi_unload_parent_table(acpi_handle object) +{ + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, object); + acpi_status status = AE_NOT_EXIST; + acpi_owner_id owner_id; + u32 i; + + ACPI_FUNCTION_TRACE(acpi_unload_parent_table); + + /* Parameter validation */ + + if (!object) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* + * The node owner_id is currently the same as the parent table ID. + * However, this could change in the future. + */ + owner_id = node->owner_id; + if (!owner_id) { + + /* owner_id==0 means DSDT is the owner. DSDT cannot be unloaded */ + + return_ACPI_STATUS(AE_TYPE); + } + + /* Must acquire the interpreter lock during this operation */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Find the table in the global table list */ + + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) { + if (owner_id != acpi_gbl_root_table_list.tables[i].owner_id) { + continue; + } + + /* + * Allow unload of SSDT and OEMx tables only. Do not allow unload + * of the DSDT. No other types of tables should get here, since + * only these types can contain AML and thus are the only types + * that can create namespace objects. + */ + if (ACPI_COMPARE_NAME + (acpi_gbl_root_table_list.tables[i].signature.ascii, + ACPI_SIG_DSDT)) { + status = AE_TYPE; + break; + } + + /* Ensure the table is actually loaded */ + + if (!acpi_tb_is_table_loaded(i)) { + status = AE_NOT_EXIST; + break; + } + + /* Invoke table handler if present */ + + if (acpi_gbl_table_handler) { + (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_UNLOAD, + acpi_gbl_root_table_list. + tables[i].pointer, + acpi_gbl_table_handler_context); + } + + /* + * Delete all namespace objects owned by this table. Note that + * these objects can appear anywhere in the namespace by virtue + * of the AML "Scope" operator. Thus, we need to track ownership + * by an ID, not simply a position within the hierarchy. + */ + status = acpi_tb_delete_namespace_by_owner(i); + if (ACPI_FAILURE(status)) { + break; + } + + status = acpi_tb_release_owner_id(i); + acpi_tb_set_table_loaded_flag(i, FALSE); + break; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_unload_parent_table) diff --git a/kernel/drivers/acpi/acpica/tbxfroot.c b/kernel/drivers/acpi/acpica/tbxfroot.c new file mode 100644 index 000000000..fa76a3603 --- /dev/null +++ b/kernel/drivers/acpi/acpica/tbxfroot.c @@ -0,0 +1,298 @@ +/****************************************************************************** + * + * Module Name: tbxfroot - Find the root ACPI table (RSDT) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbxfroot") + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_rsdp_length + * + * PARAMETERS: rsdp - Pointer to RSDP + * + * RETURN: Table length + * + * DESCRIPTION: Get the length of the RSDP + * + ******************************************************************************/ +u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp) +{ + + if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) { + + /* BAD Signature */ + + return (0); + } + + /* "Length" field is available if table version >= 2 */ + + if (rsdp->revision >= 2) { + return (rsdp->length); + } else { + return (ACPI_RSDP_CHECKSUM_LENGTH); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_validate_rsdp + * + * PARAMETERS: rsdp - Pointer to unvalidated RSDP + * + * RETURN: Status + * + * DESCRIPTION: Validate the RSDP (ptr) + * + ******************************************************************************/ + +acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp * rsdp) +{ + + /* + * The signature and checksum must both be correct + * + * Note: Sometimes there exists more than one RSDP in memory; the valid + * RSDP has a valid checksum, all others have an invalid checksum. + */ + if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) { + + /* Nope, BAD Signature */ + + return (AE_BAD_SIGNATURE); + } + + /* Check the standard checksum */ + + if (acpi_tb_checksum((u8 *) rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) { + return (AE_BAD_CHECKSUM); + } + + /* Check extended checksum if table version >= 2 */ + + if ((rsdp->revision >= 2) && + (acpi_tb_checksum((u8 *) rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0)) { + return (AE_BAD_CHECKSUM); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_find_root_pointer + * + * PARAMETERS: table_address - Where the table pointer is returned + * + * RETURN: Status, RSDP physical address + * + * DESCRIPTION: Search lower 1Mbyte of memory for the root system descriptor + * pointer structure. If it is found, set *RSDP to point to it. + * + * NOTE1: The RSDP must be either in the first 1K of the Extended + * BIOS Data Area or between E0000 and FFFFF (From ACPI Spec.) + * Only a 32-bit physical address is necessary. + * + * NOTE2: This function is always available, regardless of the + * initialization state of the rest of ACPI. + * + ******************************************************************************/ + +acpi_status __init acpi_find_root_pointer(acpi_physical_address * table_address) +{ + u8 *table_ptr; + u8 *mem_rover; + u32 physical_address; + + ACPI_FUNCTION_TRACE(acpi_find_root_pointer); + + /* 1a) Get the location of the Extended BIOS Data Area (EBDA) */ + + table_ptr = acpi_os_map_memory((acpi_physical_address) + ACPI_EBDA_PTR_LOCATION, + ACPI_EBDA_PTR_LENGTH); + if (!table_ptr) { + ACPI_ERROR((AE_INFO, + "Could not map memory at 0x%8.8X for length %u", + ACPI_EBDA_PTR_LOCATION, ACPI_EBDA_PTR_LENGTH)); + + return_ACPI_STATUS(AE_NO_MEMORY); + } + + ACPI_MOVE_16_TO_32(&physical_address, table_ptr); + + /* Convert segment part to physical address */ + + physical_address <<= 4; + acpi_os_unmap_memory(table_ptr, ACPI_EBDA_PTR_LENGTH); + + /* EBDA present? */ + + if (physical_address > 0x400) { + /* + * 1b) Search EBDA paragraphs (EBDA is required to be a + * minimum of 1K length) + */ + table_ptr = acpi_os_map_memory((acpi_physical_address) + physical_address, + ACPI_EBDA_WINDOW_SIZE); + if (!table_ptr) { + ACPI_ERROR((AE_INFO, + "Could not map memory at 0x%8.8X for length %u", + physical_address, ACPI_EBDA_WINDOW_SIZE)); + + return_ACPI_STATUS(AE_NO_MEMORY); + } + + mem_rover = + acpi_tb_scan_memory_for_rsdp(table_ptr, + ACPI_EBDA_WINDOW_SIZE); + acpi_os_unmap_memory(table_ptr, ACPI_EBDA_WINDOW_SIZE); + + if (mem_rover) { + + /* Return the physical address */ + + physical_address += + (u32) ACPI_PTR_DIFF(mem_rover, table_ptr); + + *table_address = + (acpi_physical_address) physical_address; + return_ACPI_STATUS(AE_OK); + } + } + + /* + * 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh + */ + table_ptr = acpi_os_map_memory((acpi_physical_address) + ACPI_HI_RSDP_WINDOW_BASE, + ACPI_HI_RSDP_WINDOW_SIZE); + + if (!table_ptr) { + ACPI_ERROR((AE_INFO, + "Could not map memory at 0x%8.8X for length %u", + ACPI_HI_RSDP_WINDOW_BASE, + ACPI_HI_RSDP_WINDOW_SIZE)); + + return_ACPI_STATUS(AE_NO_MEMORY); + } + + mem_rover = + acpi_tb_scan_memory_for_rsdp(table_ptr, ACPI_HI_RSDP_WINDOW_SIZE); + acpi_os_unmap_memory(table_ptr, ACPI_HI_RSDP_WINDOW_SIZE); + + if (mem_rover) { + + /* Return the physical address */ + + physical_address = (u32) + (ACPI_HI_RSDP_WINDOW_BASE + + ACPI_PTR_DIFF(mem_rover, table_ptr)); + + *table_address = (acpi_physical_address) physical_address; + return_ACPI_STATUS(AE_OK); + } + + /* A valid RSDP was not found */ + + ACPI_BIOS_ERROR((AE_INFO, "A valid RSDP was not found")); + return_ACPI_STATUS(AE_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_scan_memory_for_rsdp + * + * PARAMETERS: start_address - Starting pointer for search + * length - Maximum length to search + * + * RETURN: Pointer to the RSDP if found, otherwise NULL. + * + * DESCRIPTION: Search a block of memory for the RSDP signature + * + ******************************************************************************/ +u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length) +{ + acpi_status status; + u8 *mem_rover; + u8 *end_address; + + ACPI_FUNCTION_TRACE(tb_scan_memory_for_rsdp); + + end_address = start_address + length; + + /* Search from given start address for the requested length */ + + for (mem_rover = start_address; mem_rover < end_address; + mem_rover += ACPI_RSDP_SCAN_STEP) { + + /* The RSDP signature and checksum must both be correct */ + + status = + acpi_tb_validate_rsdp(ACPI_CAST_PTR + (struct acpi_table_rsdp, mem_rover)); + if (ACPI_SUCCESS(status)) { + + /* Sig and checksum valid, we have found a real RSDP */ + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "RSDP located at physical address %p\n", + mem_rover)); + return_PTR(mem_rover); + } + + /* No sig match or bad checksum, keep searching */ + } + + /* Searched entire block, no RSDP was found */ + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Searched entire block from %p, valid RSDP was not found\n", + start_address)); + return_PTR(NULL); +} diff --git a/kernel/drivers/acpi/acpica/utaddress.c b/kernel/drivers/acpi/acpica/utaddress.c new file mode 100644 index 000000000..911ea8e7f --- /dev/null +++ b/kernel/drivers/acpi/acpica/utaddress.c @@ -0,0 +1,297 @@ +/****************************************************************************** + * + * Module Name: utaddress - op_region address range check + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utaddress") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_add_address_range + * + * PARAMETERS: space_id - Address space ID + * address - op_region start address + * length - op_region length + * region_node - op_region namespace node + * + * RETURN: Status + * + * DESCRIPTION: Add the Operation Region address range to the global list. + * The only supported Space IDs are Memory and I/O. Called when + * the op_region address/length operands are fully evaluated. + * + * MUTEX: Locks the namespace + * + * NOTE: Because this interface is only called when an op_region argument + * list is evaluated, there cannot be any duplicate region_nodes. + * Duplicate Address/Length values are allowed, however, so that multiple + * address conflicts can be detected. + * + ******************************************************************************/ +acpi_status +acpi_ut_add_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, + u32 length, struct acpi_namespace_node *region_node) +{ + struct acpi_address_range *range_info; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_add_address_range); + + if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + return_ACPI_STATUS(AE_OK); + } + + /* Allocate/init a new info block, add it to the appropriate list */ + + range_info = ACPI_ALLOCATE(sizeof(struct acpi_address_range)); + if (!range_info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + range_info->start_address = address; + range_info->end_address = (address + length - 1); + range_info->region_node = region_node; + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + ACPI_FREE(range_info); + return_ACPI_STATUS(status); + } + + range_info->next = acpi_gbl_address_range_list[space_id]; + acpi_gbl_address_range_list[space_id] = range_info; + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "\nAdded [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n", + acpi_ut_get_node_name(range_info->region_node), + ACPI_FORMAT_UINT64(address), + ACPI_FORMAT_UINT64(range_info->end_address))); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_address_range + * + * PARAMETERS: space_id - Address space ID + * region_node - op_region namespace node + * + * RETURN: None + * + * DESCRIPTION: Remove the Operation Region from the global list. The only + * supported Space IDs are Memory and I/O. Called when an + * op_region is deleted. + * + * MUTEX: Assumes the namespace is locked + * + ******************************************************************************/ + +void +acpi_ut_remove_address_range(acpi_adr_space_type space_id, + struct acpi_namespace_node *region_node) +{ + struct acpi_address_range *range_info; + struct acpi_address_range *prev; + + ACPI_FUNCTION_TRACE(ut_remove_address_range); + + if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + return_VOID; + } + + /* Get the appropriate list head and check the list */ + + range_info = prev = acpi_gbl_address_range_list[space_id]; + while (range_info) { + if (range_info->region_node == region_node) { + if (range_info == prev) { /* Found at list head */ + acpi_gbl_address_range_list[space_id] = + range_info->next; + } else { + prev->next = range_info->next; + } + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, + "\nRemoved [%4.4s] address range: 0x%8.8X%8.8X-0x%8.8X%8.8X\n", + acpi_ut_get_node_name(range_info-> + region_node), + ACPI_FORMAT_UINT64(range_info-> + start_address), + ACPI_FORMAT_UINT64(range_info-> + end_address))); + + ACPI_FREE(range_info); + return_VOID; + } + + prev = range_info; + range_info = range_info->next; + } + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_check_address_range + * + * PARAMETERS: space_id - Address space ID + * address - Start address + * length - Length of address range + * warn - TRUE if warning on overlap desired + * + * RETURN: Count of the number of conflicts detected. Zero is always + * returned for Space IDs other than Memory or I/O. + * + * DESCRIPTION: Check if the input address range overlaps any of the + * ASL operation region address ranges. The only supported + * Space IDs are Memory and I/O. + * + * MUTEX: Assumes the namespace is locked. + * + ******************************************************************************/ + +u32 +acpi_ut_check_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, u32 length, u8 warn) +{ + struct acpi_address_range *range_info; + acpi_physical_address end_address; + char *pathname; + u32 overlap_count = 0; + + ACPI_FUNCTION_TRACE(ut_check_address_range); + + if ((space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) && + (space_id != ACPI_ADR_SPACE_SYSTEM_IO)) { + return_UINT32(0); + } + + range_info = acpi_gbl_address_range_list[space_id]; + end_address = address + length - 1; + + /* Check entire list for all possible conflicts */ + + while (range_info) { + /* + * Check if the requested address/length overlaps this + * address range. There are four cases to consider: + * + * 1) Input address/length is contained completely in the + * address range + * 2) Input address/length overlaps range at the range start + * 3) Input address/length overlaps range at the range end + * 4) Input address/length completely encompasses the range + */ + if ((address <= range_info->end_address) && + (end_address >= range_info->start_address)) { + + /* Found an address range overlap */ + + overlap_count++; + if (warn) { /* Optional warning message */ + pathname = + acpi_ns_get_external_pathname(range_info-> + region_node); + + ACPI_WARNING((AE_INFO, + "%s range 0x%8.8X%8.8X-0x%8.8X%8.8X conflicts with OpRegion 0x%8.8X%8.8X-0x%8.8X%8.8X (%s)", + acpi_ut_get_region_name(space_id), + ACPI_FORMAT_UINT64(address), + ACPI_FORMAT_UINT64(end_address), + ACPI_FORMAT_UINT64(range_info-> + start_address), + ACPI_FORMAT_UINT64(range_info-> + end_address), + pathname)); + ACPI_FREE(pathname); + } + } + + range_info = range_info->next; + } + + return_UINT32(overlap_count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_address_lists + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Delete all global address range lists (called during + * subsystem shutdown). + * + ******************************************************************************/ + +void acpi_ut_delete_address_lists(void) +{ + struct acpi_address_range *next; + struct acpi_address_range *range_info; + int i; + + /* Delete all elements in all address range lists */ + + for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) { + next = acpi_gbl_address_range_list[i]; + + while (next) { + range_info = next; + next = range_info->next; + ACPI_FREE(range_info); + } + + acpi_gbl_address_range_list[i] = NULL; + } +} diff --git a/kernel/drivers/acpi/acpica/utalloc.c b/kernel/drivers/acpi/acpica/utalloc.c new file mode 100644 index 000000000..61d8f6d18 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utalloc.c @@ -0,0 +1,342 @@ +/****************************************************************************** + * + * Module Name: utalloc - local memory allocation routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utalloc") + +#if !defined (USE_NATIVE_ALLOCATE_ZEROED) +/******************************************************************************* + * + * FUNCTION: acpi_os_allocate_zeroed + * + * PARAMETERS: size - Size of the allocation + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: Subsystem equivalent of calloc. Allocate and zero memory. + * This is the default implementation. Can be overridden via the + * USE_NATIVE_ALLOCATE_ZEROED flag. + * + ******************************************************************************/ +void *acpi_os_allocate_zeroed(acpi_size size) +{ + void *allocation; + + ACPI_FUNCTION_ENTRY(); + + allocation = acpi_os_allocate(size); + if (allocation) { + + /* Clear the memory block */ + + ACPI_MEMSET(allocation, 0, size); + } + + return (allocation); +} + +#endif /* !USE_NATIVE_ALLOCATE_ZEROED */ + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_caches + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create all local caches + * + ******************************************************************************/ + +acpi_status acpi_ut_create_caches(void) +{ + acpi_status status; + + /* Object Caches, for frequently used objects */ + + status = + acpi_os_create_cache("Acpi-Namespace", + sizeof(struct acpi_namespace_node), + ACPI_MAX_NAMESPACE_CACHE_DEPTH, + &acpi_gbl_namespace_cache); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = + acpi_os_create_cache("Acpi-State", sizeof(union acpi_generic_state), + ACPI_MAX_STATE_CACHE_DEPTH, + &acpi_gbl_state_cache); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = + acpi_os_create_cache("Acpi-Parse", + sizeof(struct acpi_parse_obj_common), + ACPI_MAX_PARSE_CACHE_DEPTH, + &acpi_gbl_ps_node_cache); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = + acpi_os_create_cache("Acpi-ParseExt", + sizeof(struct acpi_parse_obj_named), + ACPI_MAX_EXTPARSE_CACHE_DEPTH, + &acpi_gbl_ps_node_ext_cache); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = + acpi_os_create_cache("Acpi-Operand", + sizeof(union acpi_operand_object), + ACPI_MAX_OBJECT_CACHE_DEPTH, + &acpi_gbl_operand_cache); + if (ACPI_FAILURE(status)) { + return (status); + } +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + + /* Memory allocation lists */ + + status = acpi_ut_create_list("Acpi-Global", 0, &acpi_gbl_global_list); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = + acpi_ut_create_list("Acpi-Namespace", + sizeof(struct acpi_namespace_node), + &acpi_gbl_ns_node_list); + if (ACPI_FAILURE(status)) { + return (status); + } +#endif + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_caches + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Purge and delete all local caches + * + ******************************************************************************/ + +acpi_status acpi_ut_delete_caches(void) +{ +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + char buffer[7]; + + if (acpi_gbl_display_final_mem_stats) { + ACPI_STRCPY(buffer, "MEMORY"); + (void)acpi_db_display_statistics(buffer); + } +#endif + + (void)acpi_os_delete_cache(acpi_gbl_namespace_cache); + acpi_gbl_namespace_cache = NULL; + + (void)acpi_os_delete_cache(acpi_gbl_state_cache); + acpi_gbl_state_cache = NULL; + + (void)acpi_os_delete_cache(acpi_gbl_operand_cache); + acpi_gbl_operand_cache = NULL; + + (void)acpi_os_delete_cache(acpi_gbl_ps_node_cache); + acpi_gbl_ps_node_cache = NULL; + + (void)acpi_os_delete_cache(acpi_gbl_ps_node_ext_cache); + acpi_gbl_ps_node_ext_cache = NULL; + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + + /* Debug only - display leftover memory allocation, if any */ + + acpi_ut_dump_allocations(ACPI_UINT32_MAX, NULL); + + /* Free memory lists */ + + acpi_os_free(acpi_gbl_global_list); + acpi_gbl_global_list = NULL; + + acpi_os_free(acpi_gbl_ns_node_list); + acpi_gbl_ns_node_list = NULL; +#endif + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_validate_buffer + * + * PARAMETERS: buffer - Buffer descriptor to be validated + * + * RETURN: Status + * + * DESCRIPTION: Perform parameter validation checks on an struct acpi_buffer + * + ******************************************************************************/ + +acpi_status acpi_ut_validate_buffer(struct acpi_buffer * buffer) +{ + + /* Obviously, the structure pointer must be valid */ + + if (!buffer) { + return (AE_BAD_PARAMETER); + } + + /* Special semantics for the length */ + + if ((buffer->length == ACPI_NO_BUFFER) || + (buffer->length == ACPI_ALLOCATE_BUFFER) || + (buffer->length == ACPI_ALLOCATE_LOCAL_BUFFER)) { + return (AE_OK); + } + + /* Length is valid, the buffer pointer must be also */ + + if (!buffer->pointer) { + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_initialize_buffer + * + * PARAMETERS: buffer - Buffer to be validated + * required_length - Length needed + * + * RETURN: Status + * + * DESCRIPTION: Validate that the buffer is of the required length or + * allocate a new buffer. Returned buffer is always zeroed. + * + ******************************************************************************/ + +acpi_status +acpi_ut_initialize_buffer(struct acpi_buffer * buffer, + acpi_size required_length) +{ + acpi_size input_buffer_length; + + /* Parameter validation */ + + if (!buffer || !required_length) { + return (AE_BAD_PARAMETER); + } + + /* + * Buffer->Length is used as both an input and output parameter. Get the + * input actual length and set the output required buffer length. + */ + input_buffer_length = buffer->length; + buffer->length = required_length; + + /* + * The input buffer length contains the actual buffer length, or the type + * of buffer to be allocated by this routine. + */ + switch (input_buffer_length) { + case ACPI_NO_BUFFER: + + /* Return the exception (and the required buffer length) */ + + return (AE_BUFFER_OVERFLOW); + + case ACPI_ALLOCATE_BUFFER: + /* + * Allocate a new buffer. We directectly call acpi_os_allocate here to + * purposefully bypass the (optionally enabled) internal allocation + * tracking mechanism since we only want to track internal + * allocations. Note: The caller should use acpi_os_free to free this + * buffer created via ACPI_ALLOCATE_BUFFER. + */ + buffer->pointer = acpi_os_allocate(required_length); + break; + + case ACPI_ALLOCATE_LOCAL_BUFFER: + + /* Allocate a new buffer with local interface to allow tracking */ + + buffer->pointer = ACPI_ALLOCATE(required_length); + break; + + default: + + /* Existing buffer: Validate the size of the buffer */ + + if (input_buffer_length < required_length) { + return (AE_BUFFER_OVERFLOW); + } + break; + } + + /* Validate allocation from above or input buffer pointer */ + + if (!buffer->pointer) { + return (AE_NO_MEMORY); + } + + /* Have a valid buffer, clear it */ + + ACPI_MEMSET(buffer->pointer, 0, required_length); + return (AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/utbuffer.c b/kernel/drivers/acpi/acpica/utbuffer.c new file mode 100644 index 000000000..a8c39643e --- /dev/null +++ b/kernel/drivers/acpi/acpica/utbuffer.c @@ -0,0 +1,337 @@ +/****************************************************************************** + * + * Module Name: utbuffer - Buffer dump routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utbuffer") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dump_buffer + * + * PARAMETERS: buffer - Buffer to dump + * count - Amount to dump, in bytes + * display - BYTE, WORD, DWORD, or QWORD display: + * DB_BYTE_DISPLAY + * DB_WORD_DISPLAY + * DB_DWORD_DISPLAY + * DB_QWORD_DISPLAY + * base_offset - Beginning buffer offset (display only) + * + * RETURN: None + * + * DESCRIPTION: Generic dump buffer in both hex and ascii. + * + ******************************************************************************/ +void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 base_offset) +{ + u32 i = 0; + u32 j; + u32 temp32; + u8 buf_char; + + if (!buffer) { + acpi_os_printf("Null Buffer Pointer in DumpBuffer!\n"); + return; + } + + if ((count < 4) || (count & 0x01)) { + display = DB_BYTE_DISPLAY; + } + + /* Nasty little dump buffer routine! */ + + while (i < count) { + + /* Print current offset */ + + acpi_os_printf("%6.4X: ", (base_offset + i)); + + /* Print 16 hex chars */ + + for (j = 0; j < 16;) { + if (i + j >= count) { + + /* Dump fill spaces */ + + acpi_os_printf("%*s", ((display * 2) + 1), " "); + j += display; + continue; + } + + switch (display) { + case DB_BYTE_DISPLAY: + default: /* Default is BYTE display */ + + acpi_os_printf("%02X ", + buffer[(acpi_size) i + j]); + break; + + case DB_WORD_DISPLAY: + + ACPI_MOVE_16_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_os_printf("%04X ", temp32); + break; + + case DB_DWORD_DISPLAY: + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_os_printf("%08X ", temp32); + break; + + case DB_QWORD_DISPLAY: + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_os_printf("%08X", temp32); + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j + + 4]); + acpi_os_printf("%08X ", temp32); + break; + } + + j += display; + } + + /* + * Print the ASCII equivalent characters but watch out for the bad + * unprintable ones (printable chars are 0x20 through 0x7E) + */ + acpi_os_printf(" "); + for (j = 0; j < 16; j++) { + if (i + j >= count) { + acpi_os_printf("\n"); + return; + } + + /* + * Add comment characters so rest of line is ignored when + * compiled + */ + if (j == 0) { + acpi_os_printf("// "); + } + + buf_char = buffer[(acpi_size) i + j]; + if (ACPI_IS_PRINT(buf_char)) { + acpi_os_printf("%c", buf_char); + } else { + acpi_os_printf("."); + } + } + + /* Done with that line. */ + + acpi_os_printf("\n"); + i += 16; + } + + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_debug_dump_buffer + * + * PARAMETERS: buffer - Buffer to dump + * count - Amount to dump, in bytes + * display - BYTE, WORD, DWORD, or QWORD display: + * DB_BYTE_DISPLAY + * DB_WORD_DISPLAY + * DB_DWORD_DISPLAY + * DB_QWORD_DISPLAY + * component_ID - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Generic dump buffer in both hex and ascii. + * + ******************************************************************************/ + +void +acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id) +{ + + /* Only dump the buffer if tracing is enabled */ + + if (!((ACPI_LV_TABLES & acpi_dbg_level) && + (component_id & acpi_dbg_layer))) { + return; + } + + acpi_ut_dump_buffer(buffer, count, display, 0); +} + +#ifdef ACPI_APPLICATION +/******************************************************************************* + * + * FUNCTION: acpi_ut_dump_buffer_to_file + * + * PARAMETERS: file - File descriptor + * buffer - Buffer to dump + * count - Amount to dump, in bytes + * display - BYTE, WORD, DWORD, or QWORD display: + * DB_BYTE_DISPLAY + * DB_WORD_DISPLAY + * DB_DWORD_DISPLAY + * DB_QWORD_DISPLAY + * base_offset - Beginning buffer offset (display only) + * + * RETURN: None + * + * DESCRIPTION: Generic dump buffer in both hex and ascii to a file. + * + ******************************************************************************/ + +void +acpi_ut_dump_buffer_to_file(ACPI_FILE file, + u8 *buffer, u32 count, u32 display, u32 base_offset) +{ + u32 i = 0; + u32 j; + u32 temp32; + u8 buf_char; + + if (!buffer) { + acpi_ut_file_printf(file, + "Null Buffer Pointer in DumpBuffer!\n"); + return; + } + + if ((count < 4) || (count & 0x01)) { + display = DB_BYTE_DISPLAY; + } + + /* Nasty little dump buffer routine! */ + + while (i < count) { + + /* Print current offset */ + + acpi_ut_file_printf(file, "%6.4X: ", (base_offset + i)); + + /* Print 16 hex chars */ + + for (j = 0; j < 16;) { + if (i + j >= count) { + + /* Dump fill spaces */ + + acpi_ut_file_printf(file, "%*s", + ((display * 2) + 1), " "); + j += display; + continue; + } + + switch (display) { + case DB_BYTE_DISPLAY: + default: /* Default is BYTE display */ + + acpi_ut_file_printf(file, "%02X ", + buffer[(acpi_size) i + j]); + break; + + case DB_WORD_DISPLAY: + + ACPI_MOVE_16_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_ut_file_printf(file, "%04X ", temp32); + break; + + case DB_DWORD_DISPLAY: + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_ut_file_printf(file, "%08X ", temp32); + break; + + case DB_QWORD_DISPLAY: + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_ut_file_printf(file, "%08X", temp32); + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j + + 4]); + acpi_ut_file_printf(file, "%08X ", temp32); + break; + } + + j += display; + } + + /* + * Print the ASCII equivalent characters but watch out for the bad + * unprintable ones (printable chars are 0x20 through 0x7E) + */ + acpi_ut_file_printf(file, " "); + for (j = 0; j < 16; j++) { + if (i + j >= count) { + acpi_ut_file_printf(file, "\n"); + return; + } + + buf_char = buffer[(acpi_size) i + j]; + if (ACPI_IS_PRINT(buf_char)) { + acpi_ut_file_printf(file, "%c", buf_char); + } else { + acpi_ut_file_printf(file, "."); + } + } + + /* Done with that line. */ + + acpi_ut_file_printf(file, "\n"); + i += 16; + } + + return; +} +#endif diff --git a/kernel/drivers/acpi/acpica/utcache.c b/kernel/drivers/acpi/acpica/utcache.c new file mode 100644 index 000000000..eacc5eee3 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utcache.c @@ -0,0 +1,313 @@ +/****************************************************************************** + * + * Module Name: utcache - local cache allocation routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utcache") + +#ifdef ACPI_USE_LOCAL_CACHE +/******************************************************************************* + * + * FUNCTION: acpi_os_create_cache + * + * PARAMETERS: cache_name - Ascii name for the cache + * object_size - Size of each cached object + * max_depth - Maximum depth of the cache (in objects) + * return_cache - Where the new cache object is returned + * + * RETURN: Status + * + * DESCRIPTION: Create a cache object + * + ******************************************************************************/ +acpi_status +acpi_os_create_cache(char *cache_name, + u16 object_size, + u16 max_depth, struct acpi_memory_list **return_cache) +{ + struct acpi_memory_list *cache; + + ACPI_FUNCTION_ENTRY(); + + if (!cache_name || !return_cache || (object_size < 16)) { + return (AE_BAD_PARAMETER); + } + + /* Create the cache object */ + + cache = acpi_os_allocate(sizeof(struct acpi_memory_list)); + if (!cache) { + return (AE_NO_MEMORY); + } + + /* Populate the cache object and return it */ + + ACPI_MEMSET(cache, 0, sizeof(struct acpi_memory_list)); + cache->list_name = cache_name; + cache->object_size = object_size; + cache->max_depth = max_depth; + + *return_cache = cache; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_purge_cache + * + * PARAMETERS: cache - Handle to cache object + * + * RETURN: Status + * + * DESCRIPTION: Free all objects within the requested cache. + * + ******************************************************************************/ + +acpi_status acpi_os_purge_cache(struct acpi_memory_list * cache) +{ + void *next; + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + if (!cache) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Walk the list of objects in this cache */ + + while (cache->list_head) { + + /* Delete and unlink one cached state object */ + + next = ACPI_GET_DESCRIPTOR_PTR(cache->list_head); + ACPI_FREE(cache->list_head); + + cache->list_head = next; + cache->current_depth--; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_CACHES); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_delete_cache + * + * PARAMETERS: cache - Handle to cache object + * + * RETURN: Status + * + * DESCRIPTION: Free all objects within the requested cache and delete the + * cache object. + * + ******************************************************************************/ + +acpi_status acpi_os_delete_cache(struct acpi_memory_list * cache) +{ + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + /* Purge all objects in the cache */ + + status = acpi_os_purge_cache(cache); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Now we can delete the cache object */ + + acpi_os_free(cache); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_release_object + * + * PARAMETERS: cache - Handle to cache object + * object - The object to be released + * + * RETURN: None + * + * DESCRIPTION: Release an object to the specified cache. If cache is full, + * the object is deleted. + * + ******************************************************************************/ + +acpi_status +acpi_os_release_object(struct acpi_memory_list * cache, void *object) +{ + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + if (!cache || !object) { + return (AE_BAD_PARAMETER); + } + + /* If cache is full, just free this object */ + + if (cache->current_depth >= cache->max_depth) { + ACPI_FREE(object); + ACPI_MEM_TRACKING(cache->total_freed++); + } + + /* Otherwise put this object back into the cache */ + + else { + status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Mark the object as cached */ + + ACPI_MEMSET(object, 0xCA, cache->object_size); + ACPI_SET_DESCRIPTOR_TYPE(object, ACPI_DESC_TYPE_CACHED); + + /* Put the object at the head of the cache list */ + + ACPI_SET_DESCRIPTOR_PTR(object, cache->list_head); + cache->list_head = object; + cache->current_depth++; + + (void)acpi_ut_release_mutex(ACPI_MTX_CACHES); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_acquire_object + * + * PARAMETERS: cache - Handle to cache object + * + * RETURN: the acquired object. NULL on error + * + * DESCRIPTION: Get an object from the specified cache. If cache is empty, + * the object is allocated. + * + ******************************************************************************/ + +void *acpi_os_acquire_object(struct acpi_memory_list *cache) +{ + acpi_status status; + void *object; + + ACPI_FUNCTION_NAME(os_acquire_object); + + if (!cache) { + return_PTR(NULL); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES); + if (ACPI_FAILURE(status)) { + return_PTR(NULL); + } + + ACPI_MEM_TRACKING(cache->requests++); + + /* Check the cache first */ + + if (cache->list_head) { + + /* There is an object available, use it */ + + object = cache->list_head; + cache->list_head = ACPI_GET_DESCRIPTOR_PTR(object); + + cache->current_depth--; + + ACPI_MEM_TRACKING(cache->hits++); + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Object %p from %s cache\n", object, + cache->list_name)); + + status = acpi_ut_release_mutex(ACPI_MTX_CACHES); + if (ACPI_FAILURE(status)) { + return_PTR(NULL); + } + + /* Clear (zero) the previously used Object */ + + ACPI_MEMSET(object, 0, cache->object_size); + } else { + /* The cache is empty, create a new object */ + + ACPI_MEM_TRACKING(cache->total_allocated++); + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + if ((cache->total_allocated - cache->total_freed) > + cache->max_occupied) { + cache->max_occupied = + cache->total_allocated - cache->total_freed; + } +#endif + + /* Avoid deadlock with ACPI_ALLOCATE_ZEROED */ + + status = acpi_ut_release_mutex(ACPI_MTX_CACHES); + if (ACPI_FAILURE(status)) { + return_PTR(NULL); + } + + object = ACPI_ALLOCATE_ZEROED(cache->object_size); + if (!object) { + return_PTR(NULL); + } + } + + return_PTR(object); +} +#endif /* ACPI_USE_LOCAL_CACHE */ diff --git a/kernel/drivers/acpi/acpica/utcopy.c b/kernel/drivers/acpi/acpica/utcopy.c new file mode 100644 index 000000000..c37ec5035 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utcopy.c @@ -0,0 +1,1011 @@ +/****************************************************************************** + * + * Module Name: utcopy - Internal to external object translation utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utcopy") + +/* Local prototypes */ +static acpi_status +acpi_ut_copy_isimple_to_esimple(union acpi_operand_object *internal_object, + union acpi_object *external_object, + u8 * data_space, acpi_size * buffer_space_used); + +static acpi_status +acpi_ut_copy_ielement_to_ielement(u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, + void *context); + +static acpi_status +acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object, + u8 * buffer, acpi_size * space_used); + +static acpi_status +acpi_ut_copy_esimple_to_isimple(union acpi_object *user_obj, + union acpi_operand_object **return_obj); + +static acpi_status +acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object, + union acpi_operand_object **internal_object); + +static acpi_status +acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc); + +static acpi_status +acpi_ut_copy_ielement_to_eelement(u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, + void *context); + +static acpi_status +acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj, + union acpi_operand_object *dest_obj, + struct acpi_walk_state *walk_state); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_isimple_to_esimple + * + * PARAMETERS: internal_object - Source object to be copied + * external_object - Where to return the copied object + * data_space - Where object data is returned (such as + * buffer and string data) + * buffer_space_used - Length of data_space that was used + * + * RETURN: Status + * + * DESCRIPTION: This function is called to copy a simple internal object to + * an external object. + * + * The data_space buffer is assumed to have sufficient space for + * the object. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_isimple_to_esimple(union acpi_operand_object *internal_object, + union acpi_object *external_object, + u8 * data_space, acpi_size * buffer_space_used) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ut_copy_isimple_to_esimple); + + *buffer_space_used = 0; + + /* + * Check for NULL object case (could be an uninitialized + * package element) + */ + if (!internal_object) { + return_ACPI_STATUS(AE_OK); + } + + /* Always clear the external object */ + + ACPI_MEMSET(external_object, 0, sizeof(union acpi_object)); + + /* + * In general, the external object will be the same type as + * the internal object + */ + external_object->type = internal_object->common.type; + + /* However, only a limited number of external types are supported */ + + switch (internal_object->common.type) { + case ACPI_TYPE_STRING: + + external_object->string.pointer = (char *)data_space; + external_object->string.length = internal_object->string.length; + *buffer_space_used = ACPI_ROUND_UP_TO_NATIVE_WORD((acpi_size) + internal_object-> + string. + length + 1); + + ACPI_MEMCPY((void *)data_space, + (void *)internal_object->string.pointer, + (acpi_size) internal_object->string.length + 1); + break; + + case ACPI_TYPE_BUFFER: + + external_object->buffer.pointer = data_space; + external_object->buffer.length = internal_object->buffer.length; + *buffer_space_used = + ACPI_ROUND_UP_TO_NATIVE_WORD(internal_object->string. + length); + + ACPI_MEMCPY((void *)data_space, + (void *)internal_object->buffer.pointer, + internal_object->buffer.length); + break; + + case ACPI_TYPE_INTEGER: + + external_object->integer.value = internal_object->integer.value; + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + /* This is an object reference. */ + + switch (internal_object->reference.class) { + case ACPI_REFCLASS_NAME: + /* + * For namepath, return the object handle ("reference") + * We are referring to the namespace node + */ + external_object->reference.handle = + internal_object->reference.node; + external_object->reference.actual_type = + acpi_ns_get_type(internal_object->reference.node); + break; + + default: + + /* All other reference types are unsupported */ + + return_ACPI_STATUS(AE_TYPE); + } + break; + + case ACPI_TYPE_PROCESSOR: + + external_object->processor.proc_id = + internal_object->processor.proc_id; + external_object->processor.pblk_address = + internal_object->processor.address; + external_object->processor.pblk_length = + internal_object->processor.length; + break; + + case ACPI_TYPE_POWER: + + external_object->power_resource.system_level = + internal_object->power_resource.system_level; + + external_object->power_resource.resource_order = + internal_object->power_resource.resource_order; + break; + + default: + /* + * There is no corresponding external object type + */ + ACPI_ERROR((AE_INFO, + "Unsupported object type, cannot convert to external object: %s", + acpi_ut_get_type_name(internal_object->common. + type))); + + return_ACPI_STATUS(AE_SUPPORT); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ielement_to_eelement + * + * PARAMETERS: acpi_pkg_callback + * + * RETURN: Status + * + * DESCRIPTION: Copy one package element to another package element + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_ielement_to_eelement(u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, + void *context) +{ + acpi_status status = AE_OK; + struct acpi_pkg_info *info = (struct acpi_pkg_info *)context; + acpi_size object_space; + u32 this_index; + union acpi_object *target_object; + + ACPI_FUNCTION_ENTRY(); + + this_index = state->pkg.index; + target_object = (union acpi_object *) + &((union acpi_object *)(state->pkg.dest_object))->package. + elements[this_index]; + + switch (object_type) { + case ACPI_COPY_TYPE_SIMPLE: + /* + * This is a simple or null object + */ + status = acpi_ut_copy_isimple_to_esimple(source_object, + target_object, + info->free_space, + &object_space); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + case ACPI_COPY_TYPE_PACKAGE: + /* + * Build the package object + */ + target_object->type = ACPI_TYPE_PACKAGE; + target_object->package.count = source_object->package.count; + target_object->package.elements = + ACPI_CAST_PTR(union acpi_object, info->free_space); + + /* + * Pass the new package object back to the package walk routine + */ + state->pkg.this_target_obj = target_object; + + /* + * Save space for the array of objects (Package elements) + * update the buffer length counter + */ + object_space = ACPI_ROUND_UP_TO_NATIVE_WORD((acpi_size) + target_object-> + package.count * + sizeof(union + acpi_object)); + break; + + default: + + return (AE_BAD_PARAMETER); + } + + info->free_space += object_space; + info->length += object_space; + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ipackage_to_epackage + * + * PARAMETERS: internal_object - Pointer to the object we are returning + * buffer - Where the object is returned + * space_used - Where the object length is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to place a package object in a user + * buffer. A package object by definition contains other objects. + * + * The buffer is assumed to have sufficient space for the object. + * The caller must have verified the buffer length needed using + * the acpi_ut_get_object_size function before calling this function. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object, + u8 * buffer, acpi_size * space_used) +{ + union acpi_object *external_object; + acpi_status status; + struct acpi_pkg_info info; + + ACPI_FUNCTION_TRACE(ut_copy_ipackage_to_epackage); + + /* + * First package at head of the buffer + */ + external_object = ACPI_CAST_PTR(union acpi_object, buffer); + + /* + * Free space begins right after the first package + */ + info.length = ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)); + info.free_space = + buffer + ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)); + info.object_space = 0; + info.num_packages = 1; + + external_object->type = internal_object->common.type; + external_object->package.count = internal_object->package.count; + external_object->package.elements = ACPI_CAST_PTR(union acpi_object, + info.free_space); + + /* + * Leave room for an array of ACPI_OBJECTS in the buffer + * and move the free space past it + */ + info.length += (acpi_size) external_object->package.count * + ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)); + info.free_space += external_object->package.count * + ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)); + + status = acpi_ut_walk_package_tree(internal_object, external_object, + acpi_ut_copy_ielement_to_eelement, + &info); + + *space_used = info.length; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_iobject_to_eobject + * + * PARAMETERS: internal_object - The internal object to be converted + * ret_buffer - Where the object is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to build an API object to be returned + * to the caller. + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_iobject_to_eobject(union acpi_operand_object *internal_object, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_copy_iobject_to_eobject); + + if (internal_object->common.type == ACPI_TYPE_PACKAGE) { + /* + * Package object: Copy all subobjects (including + * nested packages) + */ + status = acpi_ut_copy_ipackage_to_epackage(internal_object, + ret_buffer->pointer, + &ret_buffer->length); + } else { + /* + * Build a simple object (no nested objects) + */ + status = acpi_ut_copy_isimple_to_esimple(internal_object, + ACPI_CAST_PTR(union + acpi_object, + ret_buffer-> + pointer), + ACPI_ADD_PTR(u8, + ret_buffer-> + pointer, + ACPI_ROUND_UP_TO_NATIVE_WORD + (sizeof + (union + acpi_object))), + &ret_buffer->length); + /* + * build simple does not include the object size in the length + * so we add it in here + */ + ret_buffer->length += sizeof(union acpi_object); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_esimple_to_isimple + * + * PARAMETERS: external_object - The external object to be converted + * ret_internal_object - Where the internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: This function copies an external object to an internal one. + * NOTE: Pointers can be copied, we don't need to copy data. + * (The pointers have to be valid in our address space no matter + * what we do with them!) + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_esimple_to_isimple(union acpi_object *external_object, + union acpi_operand_object **ret_internal_object) +{ + union acpi_operand_object *internal_object; + + ACPI_FUNCTION_TRACE(ut_copy_esimple_to_isimple); + + /* + * Simple types supported are: String, Buffer, Integer + */ + switch (external_object->type) { + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_LOCAL_REFERENCE: + + internal_object = acpi_ut_create_internal_object((u8) + external_object-> + type); + if (!internal_object) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + break; + + case ACPI_TYPE_ANY: /* This is the case for a NULL object */ + + *ret_internal_object = NULL; + return_ACPI_STATUS(AE_OK); + + default: + + /* All other types are not supported */ + + ACPI_ERROR((AE_INFO, + "Unsupported object type, cannot convert to internal object: %s", + acpi_ut_get_type_name(external_object->type))); + + return_ACPI_STATUS(AE_SUPPORT); + } + + /* Must COPY string and buffer contents */ + + switch (external_object->type) { + case ACPI_TYPE_STRING: + + internal_object->string.pointer = + ACPI_ALLOCATE_ZEROED((acpi_size) + external_object->string.length + 1); + + if (!internal_object->string.pointer) { + goto error_exit; + } + + ACPI_MEMCPY(internal_object->string.pointer, + external_object->string.pointer, + external_object->string.length); + + internal_object->string.length = external_object->string.length; + break; + + case ACPI_TYPE_BUFFER: + + internal_object->buffer.pointer = + ACPI_ALLOCATE_ZEROED(external_object->buffer.length); + if (!internal_object->buffer.pointer) { + goto error_exit; + } + + ACPI_MEMCPY(internal_object->buffer.pointer, + external_object->buffer.pointer, + external_object->buffer.length); + + internal_object->buffer.length = external_object->buffer.length; + + /* Mark buffer data valid */ + + internal_object->buffer.flags |= AOPOBJ_DATA_VALID; + break; + + case ACPI_TYPE_INTEGER: + + internal_object->integer.value = external_object->integer.value; + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + /* An incoming reference is defined to be a namespace node */ + + internal_object->reference.class = ACPI_REFCLASS_REFOF; + internal_object->reference.object = + external_object->reference.handle; + break; + + default: + + /* Other types can't get here */ + + break; + } + + *ret_internal_object = internal_object; + return_ACPI_STATUS(AE_OK); + +error_exit: + acpi_ut_remove_reference(internal_object); + return_ACPI_STATUS(AE_NO_MEMORY); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_epackage_to_ipackage + * + * PARAMETERS: external_object - The external object to be converted + * internal_object - Where the internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Copy an external package object to an internal package. + * Handles nested packages. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object, + union acpi_operand_object **internal_object) +{ + acpi_status status = AE_OK; + union acpi_operand_object *package_object; + union acpi_operand_object **package_elements; + u32 i; + + ACPI_FUNCTION_TRACE(ut_copy_epackage_to_ipackage); + + /* Create the package object */ + + package_object = + acpi_ut_create_package_object(external_object->package.count); + if (!package_object) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + package_elements = package_object->package.elements; + + /* + * Recursive implementation. Probably ok, since nested external packages + * as parameters should be very rare. + */ + for (i = 0; i < external_object->package.count; i++) { + status = + acpi_ut_copy_eobject_to_iobject(&external_object->package. + elements[i], + &package_elements[i]); + if (ACPI_FAILURE(status)) { + + /* Truncate package and delete it */ + + package_object->package.count = i; + package_elements[i] = NULL; + acpi_ut_remove_reference(package_object); + return_ACPI_STATUS(status); + } + } + + /* Mark package data valid */ + + package_object->package.flags |= AOPOBJ_DATA_VALID; + + *internal_object = package_object; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_eobject_to_iobject + * + * PARAMETERS: external_object - The external object to be converted + * internal_object - Where the internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Converts an external object to an internal object. + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_eobject_to_iobject(union acpi_object *external_object, + union acpi_operand_object **internal_object) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_copy_eobject_to_iobject); + + if (external_object->type == ACPI_TYPE_PACKAGE) { + status = + acpi_ut_copy_epackage_to_ipackage(external_object, + internal_object); + } else { + /* + * Build a simple object (no nested objects) + */ + status = + acpi_ut_copy_esimple_to_isimple(external_object, + internal_object); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_simple_object + * + * PARAMETERS: source_desc - The internal object to be copied + * dest_desc - New target object + * + * RETURN: Status + * + * DESCRIPTION: Simple copy of one internal object to another. Reference count + * of the destination object is preserved. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc) +{ + u16 reference_count; + union acpi_operand_object *next_object; + acpi_status status; + acpi_size copy_size; + + /* Save fields from destination that we don't want to overwrite */ + + reference_count = dest_desc->common.reference_count; + next_object = dest_desc->common.next_object; + + /* + * Copy the entire source object over the destination object. + * Note: Source can be either an operand object or namespace node. + */ + copy_size = sizeof(union acpi_operand_object); + if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_NAMED) { + copy_size = sizeof(struct acpi_namespace_node); + } + + ACPI_MEMCPY(ACPI_CAST_PTR(char, dest_desc), + ACPI_CAST_PTR(char, source_desc), copy_size); + + /* Restore the saved fields */ + + dest_desc->common.reference_count = reference_count; + dest_desc->common.next_object = next_object; + + /* New object is not static, regardless of source */ + + dest_desc->common.flags &= ~AOPOBJ_STATIC_POINTER; + + /* Handle the objects with extra data */ + + switch (dest_desc->common.type) { + case ACPI_TYPE_BUFFER: + /* + * Allocate and copy the actual buffer if and only if: + * 1) There is a valid buffer pointer + * 2) The buffer has a length > 0 + */ + if ((source_desc->buffer.pointer) && + (source_desc->buffer.length)) { + dest_desc->buffer.pointer = + ACPI_ALLOCATE(source_desc->buffer.length); + if (!dest_desc->buffer.pointer) { + return (AE_NO_MEMORY); + } + + /* Copy the actual buffer data */ + + ACPI_MEMCPY(dest_desc->buffer.pointer, + source_desc->buffer.pointer, + source_desc->buffer.length); + } + break; + + case ACPI_TYPE_STRING: + /* + * Allocate and copy the actual string if and only if: + * 1) There is a valid string pointer + * (Pointer to a NULL string is allowed) + */ + if (source_desc->string.pointer) { + dest_desc->string.pointer = + ACPI_ALLOCATE((acpi_size) source_desc->string. + length + 1); + if (!dest_desc->string.pointer) { + return (AE_NO_MEMORY); + } + + /* Copy the actual string data */ + + ACPI_MEMCPY(dest_desc->string.pointer, + source_desc->string.pointer, + (acpi_size) source_desc->string.length + 1); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + /* + * We copied the reference object, so we now must add a reference + * to the object pointed to by the reference + * + * DDBHandle reference (from Load/load_table) is a special reference, + * it does not have a Reference.Object, so does not need to + * increase the reference count + */ + if (source_desc->reference.class == ACPI_REFCLASS_TABLE) { + break; + } + + acpi_ut_add_reference(source_desc->reference.object); + break; + + case ACPI_TYPE_REGION: + /* + * We copied the Region Handler, so we now must add a reference + */ + if (dest_desc->region.handler) { + acpi_ut_add_reference(dest_desc->region.handler); + } + break; + + /* + * For Mutex and Event objects, we cannot simply copy the underlying + * OS object. We must create a new one. + */ + case ACPI_TYPE_MUTEX: + + status = acpi_os_create_mutex(&dest_desc->mutex.os_mutex); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + case ACPI_TYPE_EVENT: + + status = acpi_os_create_semaphore(ACPI_NO_UNIT_LIMIT, 0, + &dest_desc->event. + os_semaphore); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + + default: + + /* Nothing to do for other simple objects */ + + break; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ielement_to_ielement + * + * PARAMETERS: acpi_pkg_callback + * + * RETURN: Status + * + * DESCRIPTION: Copy one package element to another package element + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_ielement_to_ielement(u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, + void *context) +{ + acpi_status status = AE_OK; + u32 this_index; + union acpi_operand_object **this_target_ptr; + union acpi_operand_object *target_object; + + ACPI_FUNCTION_ENTRY(); + + this_index = state->pkg.index; + this_target_ptr = (union acpi_operand_object **) + &state->pkg.dest_object->package.elements[this_index]; + + switch (object_type) { + case ACPI_COPY_TYPE_SIMPLE: + + /* A null source object indicates a (legal) null package element */ + + if (source_object) { + /* + * This is a simple object, just copy it + */ + target_object = + acpi_ut_create_internal_object(source_object-> + common.type); + if (!target_object) { + return (AE_NO_MEMORY); + } + + status = + acpi_ut_copy_simple_object(source_object, + target_object); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + *this_target_ptr = target_object; + } else { + /* Pass through a null element */ + + *this_target_ptr = NULL; + } + break; + + case ACPI_COPY_TYPE_PACKAGE: + /* + * This object is a package - go down another nesting level + * Create and build the package object + */ + target_object = + acpi_ut_create_package_object(source_object->package.count); + if (!target_object) { + return (AE_NO_MEMORY); + } + + target_object->common.flags = source_object->common.flags; + + /* Pass the new package object back to the package walk routine */ + + state->pkg.this_target_obj = target_object; + + /* Store the object pointer in the parent package object */ + + *this_target_ptr = target_object; + break; + + default: + + return (AE_BAD_PARAMETER); + } + + return (status); + +error_exit: + acpi_ut_remove_reference(target_object); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ipackage_to_ipackage + * + * PARAMETERS: source_obj - Pointer to the source package object + * dest_obj - Where the internal object is returned + * walk_state - Current Walk state descriptor + * + * RETURN: Status + * + * DESCRIPTION: This function is called to copy an internal package object + * into another internal package object. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj, + union acpi_operand_object *dest_obj, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ut_copy_ipackage_to_ipackage); + + dest_obj->common.type = source_obj->common.type; + dest_obj->common.flags = source_obj->common.flags; + dest_obj->package.count = source_obj->package.count; + + /* + * Create the object array and walk the source package tree + */ + dest_obj->package.elements = ACPI_ALLOCATE_ZEROED(((acpi_size) + source_obj->package. + count + + 1) * sizeof(void *)); + if (!dest_obj->package.elements) { + ACPI_ERROR((AE_INFO, "Package allocation failure")); + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* + * Copy the package element-by-element by walking the package "tree". + * This handles nested packages of arbitrary depth. + */ + status = acpi_ut_walk_package_tree(source_obj, dest_obj, + acpi_ut_copy_ielement_to_ielement, + walk_state); + if (ACPI_FAILURE(status)) { + + /* On failure, delete the destination package object */ + + acpi_ut_remove_reference(dest_obj); + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_iobject_to_iobject + * + * PARAMETERS: source_desc - The internal object to be copied + * dest_desc - Where the copied object is returned + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Copy an internal object to a new internal object + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_iobject_to_iobject(union acpi_operand_object *source_desc, + union acpi_operand_object **dest_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ut_copy_iobject_to_iobject); + + /* Create the top level object */ + + *dest_desc = acpi_ut_create_internal_object(source_desc->common.type); + if (!*dest_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Copy the object and possible subobjects */ + + if (source_desc->common.type == ACPI_TYPE_PACKAGE) { + status = + acpi_ut_copy_ipackage_to_ipackage(source_desc, *dest_desc, + walk_state); + } else { + status = acpi_ut_copy_simple_object(source_desc, *dest_desc); + } + + /* Delete the allocated object if copy failed */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(*dest_desc); + } + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/utdebug.c b/kernel/drivers/acpi/acpica/utdebug.c new file mode 100644 index 000000000..4f3f888d3 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utdebug.c @@ -0,0 +1,589 @@ +/****************************************************************************** + * + * Module Name: utdebug - Debug print/trace routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utdebug") + +#ifdef ACPI_DEBUG_OUTPUT +static acpi_thread_id acpi_gbl_prev_thread_id = (acpi_thread_id) 0xFFFFFFFF; +static char *acpi_gbl_fn_entry_str = "----Entry"; +static char *acpi_gbl_fn_exit_str = "----Exit-"; + +/* Local prototypes */ + +static const char *acpi_ut_trim_function_name(const char *function_name); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_init_stack_ptr_trace + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Save the current CPU stack pointer at subsystem startup + * + ******************************************************************************/ + +void acpi_ut_init_stack_ptr_trace(void) +{ + acpi_size current_sp; + + acpi_gbl_entry_stack_pointer = ¤t_sp; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_track_stack_ptr + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Save the current CPU stack pointer + * + ******************************************************************************/ + +void acpi_ut_track_stack_ptr(void) +{ + acpi_size current_sp; + + if (¤t_sp < acpi_gbl_lowest_stack_pointer) { + acpi_gbl_lowest_stack_pointer = ¤t_sp; + } + + if (acpi_gbl_nesting_level > acpi_gbl_deepest_nesting) { + acpi_gbl_deepest_nesting = acpi_gbl_nesting_level; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_trim_function_name + * + * PARAMETERS: function_name - Ascii string containing a procedure name + * + * RETURN: Updated pointer to the function name + * + * DESCRIPTION: Remove the "Acpi" prefix from the function name, if present. + * This allows compiler macros such as __func__ to be used with no + * change to the debug output. + * + ******************************************************************************/ + +static const char *acpi_ut_trim_function_name(const char *function_name) +{ + + /* All Function names are longer than 4 chars, check is safe */ + + if (*(ACPI_CAST_PTR(u32, function_name)) == ACPI_PREFIX_MIXED) { + + /* This is the case where the original source has not been modified */ + + return (function_name + 4); + } + + if (*(ACPI_CAST_PTR(u32, function_name)) == ACPI_PREFIX_LOWER) { + + /* This is the case where the source has been 'linuxized' */ + + return (function_name + 5); + } + + return (function_name); +} + +/******************************************************************************* + * + * FUNCTION: acpi_debug_print + * + * PARAMETERS: requested_debug_level - Requested debug print level + * line_number - Caller's line number (for error output) + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print error message with prefix consisting of the module name, + * line number, and component ID. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_debug_print(u32 requested_debug_level, + u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, const char *format, ...) +{ + acpi_thread_id thread_id; + va_list args; + + /* Check if debug output enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(requested_debug_level, component_id)) { + return; + } + + /* + * Thread tracking and context switch notification + */ + thread_id = acpi_os_get_thread_id(); + if (thread_id != acpi_gbl_prev_thread_id) { + if (ACPI_LV_THREADS & acpi_dbg_level) { + acpi_os_printf + ("\n**** Context Switch from TID %u to TID %u ****\n\n", + (u32)acpi_gbl_prev_thread_id, (u32)thread_id); + } + + acpi_gbl_prev_thread_id = thread_id; + acpi_gbl_nesting_level = 0; + } + + /* + * Display the module name, current line number, thread ID (if requested), + * current procedure nesting level, and the current procedure name + */ + acpi_os_printf("%9s-%04ld ", module_name, line_number); + +#ifdef ACPI_APPLICATION + /* + * For acpi_exec/iASL only, emit the thread ID and nesting level. + * Note: nesting level is really only useful during a single-thread + * execution. Otherwise, multiple threads will keep resetting the + * level. + */ + if (ACPI_LV_THREADS & acpi_dbg_level) { + acpi_os_printf("[%u] ", (u32)thread_id); + } + + acpi_os_printf("[%02ld] ", acpi_gbl_nesting_level); +#endif + + acpi_os_printf("%-22.22s: ", acpi_ut_trim_function_name(function_name)); + + va_start(args, format); + acpi_os_vprintf(format, args); + va_end(args); +} + +ACPI_EXPORT_SYMBOL(acpi_debug_print) + +/******************************************************************************* + * + * FUNCTION: acpi_debug_print_raw + * + * PARAMETERS: requested_debug_level - Requested debug print level + * line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print message with no headers. Has same interface as + * debug_print so that the same macros can be used. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_debug_print_raw(u32 requested_debug_level, + u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, const char *format, ...) +{ + va_list args; + + /* Check if debug output enabled */ + + if (!ACPI_IS_DEBUG_ENABLED(requested_debug_level, component_id)) { + return; + } + + va_start(args, format); + acpi_os_vprintf(format, args); + va_end(args); +} + +ACPI_EXPORT_SYMBOL(acpi_debug_print_raw) + +/******************************************************************************* + * + * FUNCTION: acpi_ut_trace + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ******************************************************************************/ +void +acpi_ut_trace(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id) +{ + + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr(); + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s\n", acpi_gbl_fn_entry_str); + } +} + +ACPI_EXPORT_SYMBOL(acpi_ut_trace) + +/******************************************************************************* + * + * FUNCTION: acpi_ut_trace_ptr + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * pointer - Pointer to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ******************************************************************************/ +void +acpi_ut_trace_ptr(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, void *pointer) +{ + + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr(); + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s %p\n", acpi_gbl_fn_entry_str, + pointer); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_trace_str + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * string - Additional string to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ******************************************************************************/ + +void +acpi_ut_trace_str(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, char *string) +{ + + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr(); + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s %s\n", acpi_gbl_fn_entry_str, + string); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_trace_u32 + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * integer - Integer to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ******************************************************************************/ + +void +acpi_ut_trace_u32(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u32 integer) +{ + + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr(); + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s %08X\n", + acpi_gbl_fn_entry_str, integer); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_exit + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ******************************************************************************/ + +void +acpi_ut_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id) +{ + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s\n", acpi_gbl_fn_exit_str); + } + + if (acpi_gbl_nesting_level) { + acpi_gbl_nesting_level--; + } +} + +ACPI_EXPORT_SYMBOL(acpi_ut_exit) + +/******************************************************************************* + * + * FUNCTION: acpi_ut_status_exit + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * status - Exit status code + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level. Prints exit status also. + * + ******************************************************************************/ +void +acpi_ut_status_exit(u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, acpi_status status) +{ + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + if (ACPI_SUCCESS(status)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, + module_name, component_id, "%s %s\n", + acpi_gbl_fn_exit_str, + acpi_format_exception(status)); + } else { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, + module_name, component_id, + "%s ****Exception****: %s\n", + acpi_gbl_fn_exit_str, + acpi_format_exception(status)); + } + } + + if (acpi_gbl_nesting_level) { + acpi_gbl_nesting_level--; + } +} + +ACPI_EXPORT_SYMBOL(acpi_ut_status_exit) + +/******************************************************************************* + * + * FUNCTION: acpi_ut_value_exit + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * value - Value to be printed with exit msg + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level. Prints exit value also. + * + ******************************************************************************/ +void +acpi_ut_value_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u64 value) +{ + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s %8.8X%8.8X\n", + acpi_gbl_fn_exit_str, + ACPI_FORMAT_UINT64(value)); + } + + if (acpi_gbl_nesting_level) { + acpi_gbl_nesting_level--; + } +} + +ACPI_EXPORT_SYMBOL(acpi_ut_value_exit) + +/******************************************************************************* + * + * FUNCTION: acpi_ut_ptr_exit + * + * PARAMETERS: line_number - Caller's line number + * function_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * ptr - Pointer to display + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level. Prints exit value also. + * + ******************************************************************************/ +void +acpi_ut_ptr_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u8 *ptr) +{ + + /* Check if enabled up-front for performance */ + + if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) { + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s %p\n", acpi_gbl_fn_exit_str, + ptr); + } + + if (acpi_gbl_nesting_level) { + acpi_gbl_nesting_level--; + } +} + +#endif + +#ifdef ACPI_APPLICATION +/******************************************************************************* + * + * FUNCTION: acpi_log_error + * + * PARAMETERS: format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print error message to the console, used by applications. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE acpi_log_error(const char *format, ...) +{ + va_list args; + + va_start(args, format); + (void)acpi_ut_file_vprintf(ACPI_FILE_ERR, format, args); + va_end(args); +} + +ACPI_EXPORT_SYMBOL(acpi_log_error) +#endif diff --git a/kernel/drivers/acpi/acpica/utdecode.c b/kernel/drivers/acpi/acpica/utdecode.c new file mode 100644 index 000000000..988e23b77 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utdecode.c @@ -0,0 +1,545 @@ +/****************************************************************************** + * + * Module Name: utdecode - Utility decoding routines (value-to-string) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utdecode") + +/* + * Properties of the ACPI Object Types, both internal and external. + * The table is indexed by values of acpi_object_type + */ +const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES] = { + ACPI_NS_NORMAL, /* 00 Any */ + ACPI_NS_NORMAL, /* 01 Number */ + ACPI_NS_NORMAL, /* 02 String */ + ACPI_NS_NORMAL, /* 03 Buffer */ + ACPI_NS_NORMAL, /* 04 Package */ + ACPI_NS_NORMAL, /* 05 field_unit */ + ACPI_NS_NEWSCOPE, /* 06 Device */ + ACPI_NS_NORMAL, /* 07 Event */ + ACPI_NS_NEWSCOPE, /* 08 Method */ + ACPI_NS_NORMAL, /* 09 Mutex */ + ACPI_NS_NORMAL, /* 10 Region */ + ACPI_NS_NEWSCOPE, /* 11 Power */ + ACPI_NS_NEWSCOPE, /* 12 Processor */ + ACPI_NS_NEWSCOPE, /* 13 Thermal */ + ACPI_NS_NORMAL, /* 14 buffer_field */ + ACPI_NS_NORMAL, /* 15 ddb_handle */ + ACPI_NS_NORMAL, /* 16 Debug Object */ + ACPI_NS_NORMAL, /* 17 def_field */ + ACPI_NS_NORMAL, /* 18 bank_field */ + ACPI_NS_NORMAL, /* 19 index_field */ + ACPI_NS_NORMAL, /* 20 Reference */ + ACPI_NS_NORMAL, /* 21 Alias */ + ACPI_NS_NORMAL, /* 22 method_alias */ + ACPI_NS_NORMAL, /* 23 Notify */ + ACPI_NS_NORMAL, /* 24 Address Handler */ + ACPI_NS_NEWSCOPE | ACPI_NS_LOCAL, /* 25 Resource Desc */ + ACPI_NS_NEWSCOPE | ACPI_NS_LOCAL, /* 26 Resource Field */ + ACPI_NS_NEWSCOPE, /* 27 Scope */ + ACPI_NS_NORMAL, /* 28 Extra */ + ACPI_NS_NORMAL, /* 29 Data */ + ACPI_NS_NORMAL /* 30 Invalid */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_region_name + * + * PARAMETERS: Space ID - ID for the region + * + * RETURN: Decoded region space_id name + * + * DESCRIPTION: Translate a Space ID into a name string (Debug only) + * + ******************************************************************************/ + +/* Region type decoding */ + +const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS] = { + "SystemMemory", /* 0x00 */ + "SystemIO", /* 0x01 */ + "PCI_Config", /* 0x02 */ + "EmbeddedControl", /* 0x03 */ + "SMBus", /* 0x04 */ + "SystemCMOS", /* 0x05 */ + "PCIBARTarget", /* 0x06 */ + "IPMI", /* 0x07 */ + "GeneralPurposeIo", /* 0x08 */ + "GenericSerialBus", /* 0x09 */ + "PCC" /* 0x0A */ +}; + +char *acpi_ut_get_region_name(u8 space_id) +{ + + if (space_id >= ACPI_USER_REGION_BEGIN) { + return ("UserDefinedRegion"); + } else if (space_id == ACPI_ADR_SPACE_DATA_TABLE) { + return ("DataTable"); + } else if (space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { + return ("FunctionalFixedHW"); + } else if (space_id >= ACPI_NUM_PREDEFINED_REGIONS) { + return ("InvalidSpaceId"); + } + + return (ACPI_CAST_PTR(char, acpi_gbl_region_types[space_id])); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_event_name + * + * PARAMETERS: event_id - Fixed event ID + * + * RETURN: Decoded event ID name + * + * DESCRIPTION: Translate a Event ID into a name string (Debug only) + * + ******************************************************************************/ + +/* Event type decoding */ + +static const char *acpi_gbl_event_types[ACPI_NUM_FIXED_EVENTS] = { + "PM_Timer", + "GlobalLock", + "PowerButton", + "SleepButton", + "RealTimeClock", +}; + +char *acpi_ut_get_event_name(u32 event_id) +{ + + if (event_id > ACPI_EVENT_MAX) { + return ("InvalidEventID"); + } + + return (ACPI_CAST_PTR(char, acpi_gbl_event_types[event_id])); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_type_name + * + * PARAMETERS: type - An ACPI object type + * + * RETURN: Decoded ACPI object type name + * + * DESCRIPTION: Translate a Type ID into a name string (Debug only) + * + ******************************************************************************/ + +/* + * Elements of acpi_gbl_ns_type_names below must match + * one-to-one with values of acpi_object_type + * + * The type ACPI_TYPE_ANY (Untyped) is used as a "don't care" when searching; + * when stored in a table it really means that we have thus far seen no + * evidence to indicate what type is actually going to be stored for this entry. + */ +static const char acpi_gbl_bad_type[] = "UNDEFINED"; + +/* Printable names of the ACPI object types */ + +static const char *acpi_gbl_ns_type_names[] = { + /* 00 */ "Untyped", + /* 01 */ "Integer", + /* 02 */ "String", + /* 03 */ "Buffer", + /* 04 */ "Package", + /* 05 */ "FieldUnit", + /* 06 */ "Device", + /* 07 */ "Event", + /* 08 */ "Method", + /* 09 */ "Mutex", + /* 10 */ "Region", + /* 11 */ "Power", + /* 12 */ "Processor", + /* 13 */ "Thermal", + /* 14 */ "BufferField", + /* 15 */ "DdbHandle", + /* 16 */ "DebugObject", + /* 17 */ "RegionField", + /* 18 */ "BankField", + /* 19 */ "IndexField", + /* 20 */ "Reference", + /* 21 */ "Alias", + /* 22 */ "MethodAlias", + /* 23 */ "Notify", + /* 24 */ "AddrHandler", + /* 25 */ "ResourceDesc", + /* 26 */ "ResourceFld", + /* 27 */ "Scope", + /* 28 */ "Extra", + /* 29 */ "Data", + /* 30 */ "Invalid" +}; + +char *acpi_ut_get_type_name(acpi_object_type type) +{ + + if (type > ACPI_TYPE_INVALID) { + return (ACPI_CAST_PTR(char, acpi_gbl_bad_type)); + } + + return (ACPI_CAST_PTR(char, acpi_gbl_ns_type_names[type])); +} + +char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc) +{ + + if (!obj_desc) { + return ("[NULL Object Descriptor]"); + } + + return (acpi_ut_get_type_name(obj_desc->common.type)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_node_name + * + * PARAMETERS: object - A namespace node + * + * RETURN: ASCII name of the node + * + * DESCRIPTION: Validate the node and return the node's ACPI name. + * + ******************************************************************************/ + +char *acpi_ut_get_node_name(void *object) +{ + struct acpi_namespace_node *node = (struct acpi_namespace_node *)object; + + /* Must return a string of exactly 4 characters == ACPI_NAME_SIZE */ + + if (!object) { + return ("NULL"); + } + + /* Check for Root node */ + + if ((object == ACPI_ROOT_OBJECT) || (object == acpi_gbl_root_node)) { + return ("\"\\\" "); + } + + /* Descriptor must be a namespace node */ + + if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { + return ("####"); + } + + /* + * Ensure name is valid. The name was validated/repaired when the node + * was created, but make sure it has not been corrupted. + */ + acpi_ut_repair_name(node->name.ascii); + + /* Return the name */ + + return (node->name.ascii); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_descriptor_name + * + * PARAMETERS: object - An ACPI object + * + * RETURN: Decoded name of the descriptor type + * + * DESCRIPTION: Validate object and return the descriptor type + * + ******************************************************************************/ + +/* Printable names of object descriptor types */ + +static const char *acpi_gbl_desc_type_names[] = { + /* 00 */ "Not a Descriptor", + /* 01 */ "Cached", + /* 02 */ "State-Generic", + /* 03 */ "State-Update", + /* 04 */ "State-Package", + /* 05 */ "State-Control", + /* 06 */ "State-RootParseScope", + /* 07 */ "State-ParseScope", + /* 08 */ "State-WalkScope", + /* 09 */ "State-Result", + /* 10 */ "State-Notify", + /* 11 */ "State-Thread", + /* 12 */ "Walk", + /* 13 */ "Parser", + /* 14 */ "Operand", + /* 15 */ "Node" +}; + +char *acpi_ut_get_descriptor_name(void *object) +{ + + if (!object) { + return ("NULL OBJECT"); + } + + if (ACPI_GET_DESCRIPTOR_TYPE(object) > ACPI_DESC_TYPE_MAX) { + return ("Not a Descriptor"); + } + + return (ACPI_CAST_PTR(char, + acpi_gbl_desc_type_names[ACPI_GET_DESCRIPTOR_TYPE + (object)])); + +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_reference_name + * + * PARAMETERS: object - An ACPI reference object + * + * RETURN: Decoded name of the type of reference + * + * DESCRIPTION: Decode a reference object sub-type to a string. + * + ******************************************************************************/ + +/* Printable names of reference object sub-types */ + +static const char *acpi_gbl_ref_class_names[] = { + /* 00 */ "Local", + /* 01 */ "Argument", + /* 02 */ "RefOf", + /* 03 */ "Index", + /* 04 */ "DdbHandle", + /* 05 */ "Named Object", + /* 06 */ "Debug" +}; + +const char *acpi_ut_get_reference_name(union acpi_operand_object *object) +{ + + if (!object) { + return ("NULL Object"); + } + + if (ACPI_GET_DESCRIPTOR_TYPE(object) != ACPI_DESC_TYPE_OPERAND) { + return ("Not an Operand object"); + } + + if (object->common.type != ACPI_TYPE_LOCAL_REFERENCE) { + return ("Not a Reference object"); + } + + if (object->reference.class > ACPI_REFCLASS_MAX) { + return ("Unknown Reference class"); + } + + return (acpi_gbl_ref_class_names[object->reference.class]); +} + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +/* + * Strings and procedures used for debug only + */ + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_mutex_name + * + * PARAMETERS: mutex_id - The predefined ID for this mutex. + * + * RETURN: Decoded name of the internal mutex + * + * DESCRIPTION: Translate a mutex ID into a name string (Debug only) + * + ******************************************************************************/ + +/* Names for internal mutex objects, used for debug output */ + +static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = { + "ACPI_MTX_Interpreter", + "ACPI_MTX_Namespace", + "ACPI_MTX_Tables", + "ACPI_MTX_Events", + "ACPI_MTX_Caches", + "ACPI_MTX_Memory", + "ACPI_MTX_CommandComplete", + "ACPI_MTX_CommandReady" +}; + +char *acpi_ut_get_mutex_name(u32 mutex_id) +{ + + if (mutex_id > ACPI_MAX_MUTEX) { + return ("Invalid Mutex ID"); + } + + return (acpi_gbl_mutex_names[mutex_id]); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_notify_name + * + * PARAMETERS: notify_value - Value from the Notify() request + * + * RETURN: Decoded name for the notify value + * + * DESCRIPTION: Translate a Notify Value to a notify namestring. + * + ******************************************************************************/ + +/* Names for Notify() values, used for debug output */ + +static const char *acpi_gbl_generic_notify[ACPI_NOTIFY_MAX + 1] = { + /* 00 */ "Bus Check", + /* 01 */ "Device Check", + /* 02 */ "Device Wake", + /* 03 */ "Eject Request", + /* 04 */ "Device Check Light", + /* 05 */ "Frequency Mismatch", + /* 06 */ "Bus Mode Mismatch", + /* 07 */ "Power Fault", + /* 08 */ "Capabilities Check", + /* 09 */ "Device PLD Check", + /* 0A */ "Reserved", + /* 0B */ "System Locality Update", + /* 0C */ "Shutdown Request", + /* 0D */ "System Resource Affinity Update" +}; + +static const char *acpi_gbl_device_notify[4] = { + /* 80 */ "Status Change", + /* 81 */ "Information Change", + /* 82 */ "Device-Specific Change", + /* 83 */ "Device-Specific Change" +}; + +static const char *acpi_gbl_processor_notify[4] = { + /* 80 */ "Performance Capability Change", + /* 81 */ "C-State Change", + /* 82 */ "Throttling Capability Change", + /* 83 */ "Device-Specific Change" +}; + +static const char *acpi_gbl_thermal_notify[4] = { + /* 80 */ "Thermal Status Change", + /* 81 */ "Thermal Trip Point Change", + /* 82 */ "Thermal Device List Change", + /* 83 */ "Thermal Relationship Change" +}; + +const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type) +{ + + /* 00 - 0D are common to all object types */ + + if (notify_value <= ACPI_NOTIFY_MAX) { + return (acpi_gbl_generic_notify[notify_value]); + } + + /* 0D - 7F are reserved */ + + if (notify_value <= ACPI_MAX_SYS_NOTIFY) { + return ("Reserved"); + } + + /* 80 - 83 are per-object-type */ + + if (notify_value <= 0x83) { + switch (type) { + case ACPI_TYPE_ANY: + case ACPI_TYPE_DEVICE: + return (acpi_gbl_device_notify[notify_value - 0x80]); + + case ACPI_TYPE_PROCESSOR: + return (acpi_gbl_processor_notify[notify_value - 0x80]); + + case ACPI_TYPE_THERMAL: + return (acpi_gbl_thermal_notify[notify_value - 0x80]); + + default: + return ("Target object type does not support notifies"); + } + } + + /* 84 - BF are device-specific */ + + if (notify_value <= ACPI_MAX_DEVICE_SPECIFIC_NOTIFY) { + return ("Device-Specific"); + } + + /* C0 and above are hardware-specific */ + + return ("Hardware-Specific"); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ut_valid_object_type + * + * PARAMETERS: type - Object type to be validated + * + * RETURN: TRUE if valid object type, FALSE otherwise + * + * DESCRIPTION: Validate an object type + * + ******************************************************************************/ + +u8 acpi_ut_valid_object_type(acpi_object_type type) +{ + + if (type > ACPI_TYPE_LOCAL_MAX) { + + /* Note: Assumes all TYPEs are contiguous (external/local) */ + + return (FALSE); + } + + return (TRUE); +} diff --git a/kernel/drivers/acpi/acpica/utdelete.c b/kernel/drivers/acpi/acpica/utdelete.c new file mode 100644 index 000000000..71fce389f --- /dev/null +++ b/kernel/drivers/acpi/acpica/utdelete.c @@ -0,0 +1,755 @@ +/******************************************************************************* + * + * Module Name: utdelete - object deletion and reference count utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acevents.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utdelete") + +/* Local prototypes */ +static void acpi_ut_delete_internal_obj(union acpi_operand_object *object); + +static void +acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_internal_obj + * + * PARAMETERS: object - Object to be deleted + * + * RETURN: None + * + * DESCRIPTION: Low level object deletion, after reference counts have been + * updated (All reference counts, including sub-objects!) + * + ******************************************************************************/ + +static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) +{ + void *obj_pointer = NULL; + union acpi_operand_object *handler_desc; + union acpi_operand_object *second_desc; + union acpi_operand_object *next_desc; + union acpi_operand_object *start_desc; + union acpi_operand_object **last_obj_ptr; + + ACPI_FUNCTION_TRACE_PTR(ut_delete_internal_obj, object); + + if (!object) { + return_VOID; + } + + /* + * Must delete or free any pointers within the object that are not + * actual ACPI objects (for example, a raw buffer pointer). + */ + switch (object->common.type) { + case ACPI_TYPE_STRING: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "**** String %p, ptr %p\n", object, + object->string.pointer)); + + /* Free the actual string buffer */ + + if (!(object->common.flags & AOPOBJ_STATIC_POINTER)) { + + /* But only if it is NOT a pointer into an ACPI table */ + + obj_pointer = object->string.pointer; + } + break; + + case ACPI_TYPE_BUFFER: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "**** Buffer %p, ptr %p\n", object, + object->buffer.pointer)); + + /* Free the actual buffer */ + + if (!(object->common.flags & AOPOBJ_STATIC_POINTER)) { + + /* But only if it is NOT a pointer into an ACPI table */ + + obj_pointer = object->buffer.pointer; + } + break; + + case ACPI_TYPE_PACKAGE: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + " **** Package of count %X\n", + object->package.count)); + + /* + * Elements of the package are not handled here, they are deleted + * separately + */ + + /* Free the (variable length) element pointer array */ + + obj_pointer = object->package.elements; + break; + + /* + * These objects have a possible list of notify handlers. + * Device object also may have a GPE block. + */ + case ACPI_TYPE_DEVICE: + + if (object->device.gpe_block) { + (void)acpi_ev_delete_gpe_block(object->device. + gpe_block); + } + + /*lint -fallthrough */ + + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + /* Walk the address handler list for this object */ + + handler_desc = object->common_notify.handler; + while (handler_desc) { + next_desc = handler_desc->address_space.next; + acpi_ut_remove_reference(handler_desc); + handler_desc = next_desc; + } + break; + + case ACPI_TYPE_MUTEX: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "***** Mutex %p, OS Mutex %p\n", + object, object->mutex.os_mutex)); + + if (object == acpi_gbl_global_lock_mutex) { + + /* Global Lock has extra semaphore */ + + (void) + acpi_os_delete_semaphore + (acpi_gbl_global_lock_semaphore); + acpi_gbl_global_lock_semaphore = NULL; + + acpi_os_delete_mutex(object->mutex.os_mutex); + acpi_gbl_global_lock_mutex = NULL; + } else { + acpi_ex_unlink_mutex(object); + acpi_os_delete_mutex(object->mutex.os_mutex); + } + break; + + case ACPI_TYPE_EVENT: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "***** Event %p, OS Semaphore %p\n", + object, object->event.os_semaphore)); + + (void)acpi_os_delete_semaphore(object->event.os_semaphore); + object->event.os_semaphore = NULL; + break; + + case ACPI_TYPE_METHOD: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "***** Method %p\n", object)); + + /* Delete the method mutex if it exists */ + + if (object->method.mutex) { + acpi_os_delete_mutex(object->method.mutex->mutex. + os_mutex); + acpi_ut_delete_object_desc(object->method.mutex); + object->method.mutex = NULL; + } + break; + + case ACPI_TYPE_REGION: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "***** Region %p\n", object)); + + /* + * Update address_range list. However, only permanent regions + * are installed in this list. (Not created within a method) + */ + if (!(object->region.node->flags & ANOBJ_TEMPORARY)) { + acpi_ut_remove_address_range(object->region.space_id, + object->region.node); + } + + second_desc = acpi_ns_get_secondary_object(object); + if (second_desc) { + /* + * Free the region_context if and only if the handler is one of the + * default handlers -- and therefore, we created the context object + * locally, it was not created by an external caller. + */ + handler_desc = object->region.handler; + if (handler_desc) { + next_desc = + handler_desc->address_space.region_list; + start_desc = next_desc; + last_obj_ptr = + &handler_desc->address_space.region_list; + + /* Remove the region object from the handler list */ + + while (next_desc) { + if (next_desc == object) { + *last_obj_ptr = + next_desc->region.next; + break; + } + + /* Walk the linked list of handlers */ + + last_obj_ptr = &next_desc->region.next; + next_desc = next_desc->region.next; + + /* Prevent infinite loop if list is corrupted */ + + if (next_desc == start_desc) { + ACPI_ERROR((AE_INFO, + "Circular region list in address handler object %p", + handler_desc)); + return_VOID; + } + } + + if (handler_desc->address_space.handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) { + + /* Deactivate region and free region context */ + + if (handler_desc->address_space.setup) { + (void)handler_desc-> + address_space.setup(object, + ACPI_REGION_DEACTIVATE, + handler_desc-> + address_space. + context, + &second_desc-> + extra. + region_context); + } + } + + acpi_ut_remove_reference(handler_desc); + } + + /* Now we can free the Extra object */ + + acpi_ut_delete_object_desc(second_desc); + } + break; + + case ACPI_TYPE_BUFFER_FIELD: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "***** Buffer Field %p\n", object)); + + second_desc = acpi_ns_get_secondary_object(object); + if (second_desc) { + acpi_ut_delete_object_desc(second_desc); + } + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "***** Bank Field %p\n", object)); + + second_desc = acpi_ns_get_secondary_object(object); + if (second_desc) { + acpi_ut_delete_object_desc(second_desc); + } + break; + + default: + + break; + } + + /* Free any allocated memory (pointer within the object) found above */ + + if (obj_pointer) { + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "Deleting Object Subptr %p\n", obj_pointer)); + ACPI_FREE(obj_pointer); + } + + /* Now the object can be safely deleted */ + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Deleting Object %p [%s]\n", + object, acpi_ut_get_object_type_name(object))); + + acpi_ut_delete_object_desc(object); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_internal_object_list + * + * PARAMETERS: obj_list - Pointer to the list to be deleted + * + * RETURN: None + * + * DESCRIPTION: This function deletes an internal object list, including both + * simple objects and package objects + * + ******************************************************************************/ + +void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list) +{ + union acpi_operand_object **internal_obj; + + ACPI_FUNCTION_ENTRY(); + + /* Walk the null-terminated internal list */ + + for (internal_obj = obj_list; *internal_obj; internal_obj++) { + acpi_ut_remove_reference(*internal_obj); + } + + /* Free the combined parameter pointer list and object array */ + + ACPI_FREE(obj_list); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_update_ref_count + * + * PARAMETERS: object - Object whose ref count is to be updated + * action - What to do (REF_INCREMENT or REF_DECREMENT) + * + * RETURN: None. Sets new reference count within the object + * + * DESCRIPTION: Modify the reference count for an internal acpi object + * + ******************************************************************************/ + +static void +acpi_ut_update_ref_count(union acpi_operand_object *object, u32 action) +{ + u16 original_count; + u16 new_count = 0; + acpi_cpu_flags lock_flags; + + ACPI_FUNCTION_NAME(ut_update_ref_count); + + if (!object) { + return; + } + + /* + * Always get the reference count lock. Note: Interpreter and/or + * Namespace is not always locked when this function is called. + */ + lock_flags = acpi_os_acquire_lock(acpi_gbl_reference_count_lock); + original_count = object->common.reference_count; + + /* Perform the reference count action (increment, decrement) */ + + switch (action) { + case REF_INCREMENT: + + new_count = original_count + 1; + object->common.reference_count = new_count; + acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags); + + /* The current reference count should never be zero here */ + + if (!original_count) { + ACPI_WARNING((AE_INFO, + "Obj %p, Reference Count was zero before increment\n", + object)); + } + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "Obj %p Type %.2X Refs %.2X [Incremented]\n", + object, object->common.type, new_count)); + break; + + case REF_DECREMENT: + + /* The current reference count must be non-zero */ + + if (original_count) { + new_count = original_count - 1; + object->common.reference_count = new_count; + } + + acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags); + + if (!original_count) { + ACPI_WARNING((AE_INFO, + "Obj %p, Reference Count is already zero, cannot decrement\n", + object)); + } + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "Obj %p Type %.2X Refs %.2X [Decremented]\n", + object, object->common.type, new_count)); + + /* Actually delete the object on a reference count of zero */ + + if (new_count == 0) { + acpi_ut_delete_internal_obj(object); + } + break; + + default: + + acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags); + ACPI_ERROR((AE_INFO, "Unknown Reference Count action (0x%X)", + action)); + return; + } + + /* + * Sanity check the reference count, for debug purposes only. + * (A deleted object will have a huge reference count) + */ + if (new_count > ACPI_MAX_REFERENCE_COUNT) { + ACPI_WARNING((AE_INFO, + "Large Reference Count (0x%X) in object %p, Type=0x%.2X", + new_count, object, object->common.type)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_update_object_reference + * + * PARAMETERS: object - Increment ref count for this object + * and all sub-objects + * action - Either REF_INCREMENT or REF_DECREMENT + * + * RETURN: Status + * + * DESCRIPTION: Increment the object reference count + * + * Object references are incremented when: + * 1) An object is attached to a Node (namespace object) + * 2) An object is copied (all subobjects must be incremented) + * + * Object references are decremented when: + * 1) An object is detached from an Node + * + ******************************************************************************/ + +acpi_status +acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) +{ + acpi_status status = AE_OK; + union acpi_generic_state *state_list = NULL; + union acpi_operand_object *next_object = NULL; + union acpi_operand_object *prev_object; + union acpi_generic_state *state; + u32 i; + + ACPI_FUNCTION_NAME(ut_update_object_reference); + + while (object) { + + /* Make sure that this isn't a namespace handle */ + + if (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED) { + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "Object %p is NS handle\n", object)); + return (AE_OK); + } + + /* + * All sub-objects must have their reference count incremented also. + * Different object types have different subobjects. + */ + switch (object->common.type) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_POWER: + case ACPI_TYPE_THERMAL: + /* + * Update the notify objects for these types (if present) + * Two lists, system and device notify handlers. + */ + for (i = 0; i < ACPI_NUM_NOTIFY_TYPES; i++) { + prev_object = + object->common_notify.notify_list[i]; + while (prev_object) { + next_object = + prev_object->notify.next[i]; + acpi_ut_update_ref_count(prev_object, + action); + prev_object = next_object; + } + } + break; + + case ACPI_TYPE_PACKAGE: + /* + * We must update all the sub-objects of the package, + * each of whom may have their own sub-objects. + */ + for (i = 0; i < object->package.count; i++) { + /* + * Null package elements are legal and can be simply + * ignored. + */ + next_object = object->package.elements[i]; + if (!next_object) { + continue; + } + + switch (next_object->common.type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + /* + * For these very simple sub-objects, we can just + * update the reference count here and continue. + * Greatly increases performance of this operation. + */ + acpi_ut_update_ref_count(next_object, + action); + break; + + default: + /* + * For complex sub-objects, push them onto the stack + * for later processing (this eliminates recursion.) + */ + status = + acpi_ut_create_update_state_and_push + (next_object, action, &state_list); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + break; + } + } + next_object = NULL; + break; + + case ACPI_TYPE_BUFFER_FIELD: + + next_object = object->buffer_field.buffer_obj; + break; + + case ACPI_TYPE_LOCAL_REGION_FIELD: + + next_object = object->field.region_obj; + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + next_object = object->bank_field.bank_obj; + status = + acpi_ut_create_update_state_and_push(object-> + bank_field. + region_obj, + action, + &state_list); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + next_object = object->index_field.index_obj; + status = + acpi_ut_create_update_state_and_push(object-> + index_field. + data_obj, + action, + &state_list); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + /* + * The target of an Index (a package, string, or buffer) or a named + * reference must track changes to the ref count of the index or + * target object. + */ + if ((object->reference.class == ACPI_REFCLASS_INDEX) || + (object->reference.class == ACPI_REFCLASS_NAME)) { + next_object = object->reference.object; + } + break; + + case ACPI_TYPE_REGION: + default: + + break; /* No subobjects for all other types */ + } + + /* + * Now we can update the count in the main object. This can only + * happen after we update the sub-objects in case this causes the + * main object to be deleted. + */ + acpi_ut_update_ref_count(object, action); + object = NULL; + + /* Move on to the next object to be updated */ + + if (next_object) { + object = next_object; + next_object = NULL; + } else if (state_list) { + state = acpi_ut_pop_generic_state(&state_list); + object = state->update.object; + acpi_ut_delete_generic_state(state); + } + } + + return (AE_OK); + +error_exit: + + ACPI_EXCEPTION((AE_INFO, status, + "Could not update object reference count")); + + /* Free any stacked Update State objects */ + + while (state_list) { + state = acpi_ut_pop_generic_state(&state_list); + acpi_ut_delete_generic_state(state); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_add_reference + * + * PARAMETERS: object - Object whose reference count is to be + * incremented + * + * RETURN: None + * + * DESCRIPTION: Add one reference to an ACPI object + * + ******************************************************************************/ + +void acpi_ut_add_reference(union acpi_operand_object *object) +{ + + ACPI_FUNCTION_NAME(ut_add_reference); + + /* Ensure that we have a valid object */ + + if (!acpi_ut_valid_internal_object(object)) { + return; + } + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "Obj %p Current Refs=%X [To Be Incremented]\n", + object, object->common.reference_count)); + + /* Increment the reference count */ + + (void)acpi_ut_update_object_reference(object, REF_INCREMENT); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_reference + * + * PARAMETERS: object - Object whose ref count will be decremented + * + * RETURN: None + * + * DESCRIPTION: Decrement the reference count of an ACPI internal object + * + ******************************************************************************/ + +void acpi_ut_remove_reference(union acpi_operand_object *object) +{ + + ACPI_FUNCTION_NAME(ut_remove_reference); + + /* + * Allow a NULL pointer to be passed in, just ignore it. This saves + * each caller from having to check. Also, ignore NS nodes. + */ + if (!object || + (ACPI_GET_DESCRIPTOR_TYPE(object) == ACPI_DESC_TYPE_NAMED)) { + return; + } + + /* Ensure that we have a valid object */ + + if (!acpi_ut_valid_internal_object(object)) { + return; + } + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, + "Obj %p Current Refs=%X [To Be Decremented]\n", + object, object->common.reference_count)); + + /* + * Decrement the reference count, and only actually delete the object + * if the reference count becomes 0. (Must also decrement the ref count + * of all subobjects!) + */ + (void)acpi_ut_update_object_reference(object, REF_DECREMENT); + return; +} diff --git a/kernel/drivers/acpi/acpica/uterror.c b/kernel/drivers/acpi/acpica/uterror.c new file mode 100644 index 000000000..9ef80f282 --- /dev/null +++ b/kernel/drivers/acpi/acpica/uterror.c @@ -0,0 +1,289 @@ +/******************************************************************************* + * + * Module Name: uterror - Various internal error/warning output functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("uterror") + +/* + * This module contains internal error functions that may + * be configured out. + */ +#if !defined (ACPI_NO_ERROR_MESSAGES) +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Warnings for the predefined validation module. Messages are + * only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of error + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_warning(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_WARNING "%s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_info + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Info messages for the predefined validation module. Messages + * are only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_info(const char *module_name, + u32 line_number, + char *pathname, u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_INFO "%s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_bios_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: BIOS error message for predefined names. Messages + * are only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_bios_error(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_BIOS_ERROR "%s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_namespace_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * internal_name - Name or path of the namespace node + * lookup_status - Exception code from NS lookup + * + * RETURN: None + * + * DESCRIPTION: Print error message with the full pathname for the NS node. + * + ******************************************************************************/ + +void +acpi_ut_namespace_error(const char *module_name, + u32 line_number, + const char *internal_name, acpi_status lookup_status) +{ + acpi_status status; + u32 bad_name; + char *name = NULL; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + if (lookup_status == AE_BAD_CHARACTER) { + + /* There is a non-ascii character in the name */ + + ACPI_MOVE_32_TO_32(&bad_name, + ACPI_CAST_PTR(u32, internal_name)); + acpi_os_printf("[0x%.8X] (NON-ASCII)", bad_name); + } else { + /* Convert path to external format */ + + status = acpi_ns_externalize_name(ACPI_UINT32_MAX, + internal_name, NULL, &name); + + /* Print target name */ + + if (ACPI_SUCCESS(status)) { + acpi_os_printf("[%s]", name); + } else { + acpi_os_printf("[COULD NOT EXTERNALIZE NAME]"); + } + + if (name) { + ACPI_FREE(name); + } + } + + acpi_os_printf(" Namespace lookup failure, %s", + acpi_format_exception(lookup_status)); + + ACPI_MSG_SUFFIX; + ACPI_MSG_REDIRECT_END; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_method_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * message - Error message to use on failure + * prefix_node - Prefix relative to the path + * path - Path to the node (optional) + * method_status - Execution status + * + * RETURN: None + * + * DESCRIPTION: Print error message with the full pathname for the method. + * + ******************************************************************************/ + +void +acpi_ut_method_error(const char *module_name, + u32 line_number, + const char *message, + struct acpi_namespace_node *prefix_node, + const char *path, acpi_status method_status) +{ + acpi_status status; + struct acpi_namespace_node *node = prefix_node; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + if (path) { + status = + acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH, + &node); + if (ACPI_FAILURE(status)) { + acpi_os_printf("[Could not get node by pathname]"); + } + } + + acpi_ns_print_node_pathname(node, message); + acpi_os_printf(", %s", acpi_format_exception(method_status)); + + ACPI_MSG_SUFFIX; + ACPI_MSG_REDIRECT_END; +} + +#endif /* ACPI_NO_ERROR_MESSAGES */ diff --git a/kernel/drivers/acpi/acpica/uteval.c b/kernel/drivers/acpi/acpica/uteval.c new file mode 100644 index 000000000..6c738fa0c --- /dev/null +++ b/kernel/drivers/acpi/acpica/uteval.c @@ -0,0 +1,349 @@ +/****************************************************************************** + * + * Module Name: uteval - Object evaluation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("uteval") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_evaluate_object + * + * PARAMETERS: prefix_node - Starting node + * path - Path to object from starting node + * expected_return_types - Bitmap of allowed return types + * return_desc - Where a return value is stored + * + * RETURN: Status + * + * DESCRIPTION: Evaluates a namespace object and verifies the type of the + * return object. Common code that simplifies accessing objects + * that have required return objects of fixed types. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, + char *path, + u32 expected_return_btypes, + union acpi_operand_object **return_desc) +{ + struct acpi_evaluate_info *info; + acpi_status status; + u32 return_btype; + + ACPI_FUNCTION_TRACE(ut_evaluate_object); + + /* Allocate the evaluation information block */ + + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + info->prefix_node = prefix_node; + info->relative_pathname = path; + + /* Evaluate the object/method */ + + status = acpi_ns_evaluate(info); + if (ACPI_FAILURE(status)) { + if (status == AE_NOT_FOUND) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[%4.4s.%s] was not found\n", + acpi_ut_get_node_name(prefix_node), + path)); + } else { + ACPI_ERROR_METHOD("Method execution failed", + prefix_node, path, status); + } + + goto cleanup; + } + + /* Did we get a return object? */ + + if (!info->return_object) { + if (expected_return_btypes) { + ACPI_ERROR_METHOD("No object was returned from", + prefix_node, path, AE_NOT_EXIST); + + status = AE_NOT_EXIST; + } + + goto cleanup; + } + + /* Map the return object type to the bitmapped type */ + + switch ((info->return_object)->common.type) { + case ACPI_TYPE_INTEGER: + + return_btype = ACPI_BTYPE_INTEGER; + break; + + case ACPI_TYPE_BUFFER: + + return_btype = ACPI_BTYPE_BUFFER; + break; + + case ACPI_TYPE_STRING: + + return_btype = ACPI_BTYPE_STRING; + break; + + case ACPI_TYPE_PACKAGE: + + return_btype = ACPI_BTYPE_PACKAGE; + break; + + default: + + return_btype = 0; + break; + } + + if ((acpi_gbl_enable_interpreter_slack) && (!expected_return_btypes)) { + /* + * We received a return object, but one was not expected. This can + * happen frequently if the "implicit return" feature is enabled. + * Just delete the return object and return AE_OK. + */ + acpi_ut_remove_reference(info->return_object); + goto cleanup; + } + + /* Is the return object one of the expected types? */ + + if (!(expected_return_btypes & return_btype)) { + ACPI_ERROR_METHOD("Return object type is incorrect", + prefix_node, path, AE_TYPE); + + ACPI_ERROR((AE_INFO, + "Type returned from %s was incorrect: %s, expected Btypes: 0x%X", + path, + acpi_ut_get_object_type_name(info->return_object), + expected_return_btypes)); + + /* On error exit, we must delete the return object */ + + acpi_ut_remove_reference(info->return_object); + status = AE_TYPE; + goto cleanup; + } + + /* Object type is OK, return it */ + + *return_desc = info->return_object; + +cleanup: + ACPI_FREE(info); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_evaluate_numeric_object + * + * PARAMETERS: object_name - Object name to be evaluated + * device_node - Node for the device + * value - Where the value is returned + * + * RETURN: Status + * + * DESCRIPTION: Evaluates a numeric namespace object for a selected device + * and stores result in *Value. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_evaluate_numeric_object(char *object_name, + struct acpi_namespace_node *device_node, + u64 *value) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_evaluate_numeric_object); + + status = acpi_ut_evaluate_object(device_node, object_name, + ACPI_BTYPE_INTEGER, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the returned Integer */ + + *value = obj_desc->integer.value; + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_STA + * + * PARAMETERS: device_node - Node for the device + * flags - Where the status flags are returned + * + * RETURN: Status + * + * DESCRIPTION: Executes _STA for selected device and stores results in + * *Flags. If _STA does not exist, then the device is assumed + * to be present/functional/enabled (as per the ACPI spec). + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 * flags) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_STA); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__STA, + ACPI_BTYPE_INTEGER, &obj_desc); + if (ACPI_FAILURE(status)) { + if (AE_NOT_FOUND == status) { + /* + * if _STA does not exist, then (as per the ACPI specification), + * the returned flags will indicate that the device is present, + * functional, and enabled. + */ + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "_STA on %4.4s was not found, assuming device is present\n", + acpi_ut_get_node_name(device_node))); + + *flags = ACPI_UINT32_MAX; + status = AE_OK; + } + + return_ACPI_STATUS(status); + } + + /* Extract the status flags */ + + *flags = (u32) obj_desc->integer.value; + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_power_methods + * + * PARAMETERS: device_node - Node for the device + * method_names - Array of power method names + * method_count - Number of methods to execute + * out_values - Where the power method values are returned + * + * RETURN: Status, out_values + * + * DESCRIPTION: Executes the specified power methods for the device and returns + * the result(s). + * + * NOTE: Internal function, no parameter validation + * +******************************************************************************/ + +acpi_status +acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node, + const char **method_names, + u8 method_count, u8 *out_values) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + acpi_status final_status = AE_NOT_FOUND; + u32 i; + + ACPI_FUNCTION_TRACE(ut_execute_power_methods); + + for (i = 0; i < method_count; i++) { + /* + * Execute the power method (_sx_d or _sx_w). The only allowable + * return type is an Integer. + */ + status = acpi_ut_evaluate_object(device_node, + ACPI_CAST_PTR(char, + method_names[i]), + ACPI_BTYPE_INTEGER, &obj_desc); + if (ACPI_SUCCESS(status)) { + out_values[i] = (u8)obj_desc->integer.value; + + /* Delete the return object */ + + acpi_ut_remove_reference(obj_desc); + final_status = AE_OK; /* At least one value is valid */ + continue; + } + + out_values[i] = ACPI_UINT8_MAX; + if (status == AE_NOT_FOUND) { + continue; /* Ignore if not found */ + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Failed %s on Device %4.4s, %s\n", + ACPI_CAST_PTR(char, method_names[i]), + acpi_ut_get_node_name(device_node), + acpi_format_exception(status))); + } + + return_ACPI_STATUS(final_status); +} diff --git a/kernel/drivers/acpi/acpica/utexcep.c b/kernel/drivers/acpi/acpica/utexcep.c new file mode 100644 index 000000000..743a0ae9f --- /dev/null +++ b/kernel/drivers/acpi/acpica/utexcep.c @@ -0,0 +1,159 @@ +/******************************************************************************* + * + * Module Name: utexcep - Exception code support + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#define ACPI_DEFINE_EXCEPTION_TABLE +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utexcep") + +/******************************************************************************* + * + * FUNCTION: acpi_format_exception + * + * PARAMETERS: status - The acpi_status code to be formatted + * + * RETURN: A string containing the exception text. A valid pointer is + * always returned. + * + * DESCRIPTION: This function translates an ACPI exception into an ASCII + * string. Returns "unknown status" string for invalid codes. + * + ******************************************************************************/ +const char *acpi_format_exception(acpi_status status) +{ + const struct acpi_exception_info *exception; + + ACPI_FUNCTION_ENTRY(); + + exception = acpi_ut_validate_exception(status); + if (!exception) { + + /* Exception code was not recognized */ + + ACPI_ERROR((AE_INFO, + "Unknown exception code: 0x%8.8X", status)); + + return ("UNKNOWN_STATUS_CODE"); + } + + return (exception->name); +} + +ACPI_EXPORT_SYMBOL(acpi_format_exception) + +/******************************************************************************* + * + * FUNCTION: acpi_ut_validate_exception + * + * PARAMETERS: status - The acpi_status code to be formatted + * + * RETURN: A string containing the exception text. NULL if exception is + * not valid. + * + * DESCRIPTION: This function validates and translates an ACPI exception into + * an ASCII string. + * + ******************************************************************************/ +const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status status) +{ + u32 sub_status; + const struct acpi_exception_info *exception = NULL; + + ACPI_FUNCTION_ENTRY(); + + /* + * Status is composed of two parts, a "type" and an actual code + */ + sub_status = (status & ~AE_CODE_MASK); + + switch (status & AE_CODE_MASK) { + case AE_CODE_ENVIRONMENTAL: + + if (sub_status <= AE_CODE_ENV_MAX) { + exception = &acpi_gbl_exception_names_env[sub_status]; + } + break; + + case AE_CODE_PROGRAMMER: + + if (sub_status <= AE_CODE_PGM_MAX) { + exception = &acpi_gbl_exception_names_pgm[sub_status]; + } + break; + + case AE_CODE_ACPI_TABLES: + + if (sub_status <= AE_CODE_TBL_MAX) { + exception = &acpi_gbl_exception_names_tbl[sub_status]; + } + break; + + case AE_CODE_AML: + + if (sub_status <= AE_CODE_AML_MAX) { + exception = &acpi_gbl_exception_names_aml[sub_status]; + } + break; + + case AE_CODE_CONTROL: + + if (sub_status <= AE_CODE_CTRL_MAX) { + exception = &acpi_gbl_exception_names_ctrl[sub_status]; + } + break; + + default: + + break; + } + + if (!exception || !exception->name) { + return (NULL); + } + + return (exception); +} diff --git a/kernel/drivers/acpi/acpica/utfileio.c b/kernel/drivers/acpi/acpica/utfileio.c new file mode 100644 index 000000000..7e1168be3 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utfileio.c @@ -0,0 +1,331 @@ +/******************************************************************************* + * + * Module Name: utfileio - simple file I/O routines + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "actables.h" +#include "acapps.h" + +#ifdef ACPI_ASL_COMPILER +#include "aslcompiler.h" +#endif + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("utfileio") + +#ifdef ACPI_APPLICATION +/* Local prototypes */ +static acpi_status +acpi_ut_check_text_mode_corruption(u8 *table, + u32 table_length, u32 file_length); + +static acpi_status +acpi_ut_read_table(FILE * fp, + struct acpi_table_header **table, u32 *table_length); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_check_text_mode_corruption + * + * PARAMETERS: table - Table buffer + * table_length - Length of table from the table header + * file_length - Length of the file that contains the table + * + * RETURN: Status + * + * DESCRIPTION: Check table for text mode file corruption where all linefeed + * characters (LF) have been replaced by carriage return linefeed + * pairs (CR/LF). + * + ******************************************************************************/ + +static acpi_status +acpi_ut_check_text_mode_corruption(u8 *table, u32 table_length, u32 file_length) +{ + u32 i; + u32 pairs = 0; + + if (table_length != file_length) { + ACPI_WARNING((AE_INFO, + "File length (0x%X) is not the same as the table length (0x%X)", + file_length, table_length)); + } + + /* Scan entire table to determine if each LF has been prefixed with a CR */ + + for (i = 1; i < file_length; i++) { + if (table[i] == 0x0A) { + if (table[i - 1] != 0x0D) { + + /* The LF does not have a preceding CR, table not corrupted */ + + return (AE_OK); + } else { + /* Found a CR/LF pair */ + + pairs++; + } + i++; + } + } + + if (!pairs) { + return (AE_OK); + } + + /* + * Entire table scanned, each CR is part of a CR/LF pair -- + * meaning that the table was treated as a text file somewhere. + * + * NOTE: We can't "fix" the table, because any existing CR/LF pairs in the + * original table are left untouched by the text conversion process -- + * meaning that we cannot simply replace CR/LF pairs with LFs. + */ + acpi_os_printf("Table has been corrupted by text mode conversion\n"); + acpi_os_printf("All LFs (%u) were changed to CR/LF pairs\n", pairs); + acpi_os_printf("Table cannot be repaired!\n"); + return (AE_BAD_VALUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_read_table + * + * PARAMETERS: fp - File that contains table + * table - Return value, buffer with table + * table_length - Return value, length of table + * + * RETURN: Status + * + * DESCRIPTION: Load the DSDT from the file pointer + * + ******************************************************************************/ + +static acpi_status +acpi_ut_read_table(FILE * fp, + struct acpi_table_header **table, u32 *table_length) +{ + struct acpi_table_header table_header; + u32 actual; + acpi_status status; + u32 file_size; + u8 standard_header = TRUE; + s32 count; + + /* Get the file size */ + + file_size = cm_get_file_size(fp); + if (file_size == ACPI_UINT32_MAX) { + return (AE_ERROR); + } + + if (file_size < 4) { + return (AE_BAD_HEADER); + } + + /* Read the signature */ + + fseek(fp, 0, SEEK_SET); + + count = fread(&table_header, 1, sizeof(struct acpi_table_header), fp); + if (count != sizeof(struct acpi_table_header)) { + acpi_os_printf("Could not read the table header\n"); + return (AE_BAD_HEADER); + } + + /* The RSDP table does not have standard ACPI header */ + + if (ACPI_VALIDATE_RSDP_SIG(table_header.signature)) { + *table_length = file_size; + standard_header = FALSE; + } else { + +#if 0 + /* Validate the table header/length */ + + status = acpi_tb_validate_table_header(&table_header); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Table header is invalid!\n"); + return (status); + } +#endif + + /* File size must be at least as long as the Header-specified length */ + + if (table_header.length > file_size) { + acpi_os_printf + ("TableHeader length [0x%X] greater than the input file size [0x%X]\n", + table_header.length, file_size); + +#ifdef ACPI_ASL_COMPILER + status = fl_check_for_ascii(fp, NULL, FALSE); + if (ACPI_SUCCESS(status)) { + acpi_os_printf + ("File appears to be ASCII only, must be binary\n"); + } +#endif + return (AE_BAD_HEADER); + } +#ifdef ACPI_OBSOLETE_CODE + /* We only support a limited number of table types */ + + if (!ACPI_COMPARE_NAME + ((char *)table_header.signature, ACPI_SIG_DSDT) + && !ACPI_COMPARE_NAME((char *)table_header.signature, + ACPI_SIG_PSDT) + && !ACPI_COMPARE_NAME((char *)table_header.signature, + ACPI_SIG_SSDT)) { + acpi_os_printf + ("Table signature [%4.4s] is invalid or not supported\n", + (char *)table_header.signature); + ACPI_DUMP_BUFFER(&table_header, + sizeof(struct acpi_table_header)); + return (AE_ERROR); + } +#endif + + *table_length = table_header.length; + } + + /* Allocate a buffer for the table */ + + *table = acpi_os_allocate((size_t) file_size); + if (!*table) { + acpi_os_printf + ("Could not allocate memory for ACPI table %4.4s (size=0x%X)\n", + table_header.signature, *table_length); + return (AE_NO_MEMORY); + } + + /* Get the rest of the table */ + + fseek(fp, 0, SEEK_SET); + actual = fread(*table, 1, (size_t) file_size, fp); + if (actual == file_size) { + if (standard_header) { + + /* Now validate the checksum */ + + status = acpi_tb_verify_checksum((void *)*table, + ACPI_CAST_PTR(struct + acpi_table_header, + *table)-> + length); + + if (status == AE_BAD_CHECKSUM) { + status = + acpi_ut_check_text_mode_corruption((u8 *) + *table, + file_size, + (*table)-> + length); + return (status); + } + } + return (AE_OK); + } + + if (actual > 0) { + acpi_os_printf("Warning - reading table, asked for %X got %X\n", + file_size, actual); + return (AE_OK); + } + + acpi_os_printf("Error - could not read the table file\n"); + acpi_os_free(*table); + *table = NULL; + *table_length = 0; + return (AE_ERROR); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_read_table_from_file + * + * PARAMETERS: filename - File where table is located + * table - Where a pointer to the table is returned + * + * RETURN: Status + * + * DESCRIPTION: Get an ACPI table from a file + * + ******************************************************************************/ + +acpi_status +acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table) +{ + FILE *file; + u32 file_size; + u32 table_length; + acpi_status status = AE_ERROR; + + /* Open the file, get current size */ + + file = fopen(filename, "rb"); + if (!file) { + perror("Could not open input file"); + return (status); + } + + file_size = cm_get_file_size(file); + if (file_size == ACPI_UINT32_MAX) { + goto exit; + } + + /* Get the entire file */ + + fprintf(stderr, + "Loading Acpi table from file %10s - Length %.8u (%06X)\n", + filename, file_size, file_size); + + status = acpi_ut_read_table(file, table, &table_length); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not get table from the file\n"); + } + +exit: + fclose(file); + return (status); +} + +#endif diff --git a/kernel/drivers/acpi/acpica/utglobal.c b/kernel/drivers/acpi/acpica/utglobal.c new file mode 100644 index 000000000..5e8df9177 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utglobal.c @@ -0,0 +1,223 @@ +/****************************************************************************** + * + * Module Name: utglobal - Global variables for the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES +#define DEFINE_ACPI_GLOBALS + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utglobal") + +/******************************************************************************* + * + * Static global variable initialization. + * + ******************************************************************************/ +/* Various state name strings */ +const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = { + "\\_S0_", + "\\_S1_", + "\\_S2_", + "\\_S3_", + "\\_S4_", + "\\_S5_" +}; + +const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS] = { + "_S0W", + "_S1W", + "_S2W", + "_S3W", + "_S4W" +}; + +const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS] = { + "_S1D", + "_S2D", + "_S3D", + "_S4D" +}; + +/******************************************************************************* + * + * Namespace globals + * + ******************************************************************************/ +/* + * Predefined ACPI Names (Built-in to the Interpreter) + * + * NOTES: + * 1) _SB_ is defined to be a device to allow \_SB_._INI to be run + * during the initialization sequence. + * 2) _TZ_ is defined to be a thermal zone in order to allow ASL code to + * perform a Notify() operation on it. 09/2010: Changed to type Device. + * This still allows notifies, but does not confuse host code that + * searches for valid thermal_zone objects. + */ +const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = { + {"_GPE", ACPI_TYPE_LOCAL_SCOPE, NULL}, + {"_PR_", ACPI_TYPE_LOCAL_SCOPE, NULL}, + {"_SB_", ACPI_TYPE_DEVICE, NULL}, + {"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL}, + {"_TZ_", ACPI_TYPE_DEVICE, NULL}, + {"_REV", ACPI_TYPE_INTEGER, (char *)ACPI_CA_SUPPORT_LEVEL}, + {"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME}, + {"_GL_", ACPI_TYPE_MUTEX, (char *)1}, + +#if !defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY) + {"_OSI", ACPI_TYPE_METHOD, (char *)1}, +#endif + + /* Table terminator */ + + {NULL, ACPI_TYPE_ANY, NULL} +}; + +#if (!ACPI_REDUCED_HARDWARE) +/****************************************************************************** + * + * Event and Hardware globals + * + ******************************************************************************/ + +struct acpi_bit_register_info acpi_gbl_bit_register_info[ACPI_NUM_BITREG] = { + /* Name Parent Register Register Bit Position Register Bit Mask */ + + /* ACPI_BITREG_TIMER_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_TIMER_STATUS, + ACPI_BITMASK_TIMER_STATUS}, + /* ACPI_BITREG_BUS_MASTER_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_BUS_MASTER_STATUS, + ACPI_BITMASK_BUS_MASTER_STATUS}, + /* ACPI_BITREG_GLOBAL_LOCK_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_GLOBAL_LOCK_STATUS, + ACPI_BITMASK_GLOBAL_LOCK_STATUS}, + /* ACPI_BITREG_POWER_BUTTON_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_POWER_BUTTON_STATUS, + ACPI_BITMASK_POWER_BUTTON_STATUS}, + /* ACPI_BITREG_SLEEP_BUTTON_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_SLEEP_BUTTON_STATUS, + ACPI_BITMASK_SLEEP_BUTTON_STATUS}, + /* ACPI_BITREG_RT_CLOCK_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_RT_CLOCK_STATUS, + ACPI_BITMASK_RT_CLOCK_STATUS}, + /* ACPI_BITREG_WAKE_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_WAKE_STATUS, + ACPI_BITMASK_WAKE_STATUS}, + /* ACPI_BITREG_PCIEXP_WAKE_STATUS */ {ACPI_REGISTER_PM1_STATUS, + ACPI_BITPOSITION_PCIEXP_WAKE_STATUS, + ACPI_BITMASK_PCIEXP_WAKE_STATUS}, + + /* ACPI_BITREG_TIMER_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, + ACPI_BITPOSITION_TIMER_ENABLE, + ACPI_BITMASK_TIMER_ENABLE}, + /* ACPI_BITREG_GLOBAL_LOCK_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, + ACPI_BITPOSITION_GLOBAL_LOCK_ENABLE, + ACPI_BITMASK_GLOBAL_LOCK_ENABLE}, + /* ACPI_BITREG_POWER_BUTTON_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, + ACPI_BITPOSITION_POWER_BUTTON_ENABLE, + ACPI_BITMASK_POWER_BUTTON_ENABLE}, + /* ACPI_BITREG_SLEEP_BUTTON_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, + ACPI_BITPOSITION_SLEEP_BUTTON_ENABLE, + ACPI_BITMASK_SLEEP_BUTTON_ENABLE}, + /* ACPI_BITREG_RT_CLOCK_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, + ACPI_BITPOSITION_RT_CLOCK_ENABLE, + ACPI_BITMASK_RT_CLOCK_ENABLE}, + /* ACPI_BITREG_PCIEXP_WAKE_DISABLE */ {ACPI_REGISTER_PM1_ENABLE, + ACPI_BITPOSITION_PCIEXP_WAKE_DISABLE, + ACPI_BITMASK_PCIEXP_WAKE_DISABLE}, + + /* ACPI_BITREG_SCI_ENABLE */ {ACPI_REGISTER_PM1_CONTROL, + ACPI_BITPOSITION_SCI_ENABLE, + ACPI_BITMASK_SCI_ENABLE}, + /* ACPI_BITREG_BUS_MASTER_RLD */ {ACPI_REGISTER_PM1_CONTROL, + ACPI_BITPOSITION_BUS_MASTER_RLD, + ACPI_BITMASK_BUS_MASTER_RLD}, + /* ACPI_BITREG_GLOBAL_LOCK_RELEASE */ {ACPI_REGISTER_PM1_CONTROL, + ACPI_BITPOSITION_GLOBAL_LOCK_RELEASE, + ACPI_BITMASK_GLOBAL_LOCK_RELEASE}, + /* ACPI_BITREG_SLEEP_TYPE */ {ACPI_REGISTER_PM1_CONTROL, + ACPI_BITPOSITION_SLEEP_TYPE, + ACPI_BITMASK_SLEEP_TYPE}, + /* ACPI_BITREG_SLEEP_ENABLE */ {ACPI_REGISTER_PM1_CONTROL, + ACPI_BITPOSITION_SLEEP_ENABLE, + ACPI_BITMASK_SLEEP_ENABLE}, + + /* ACPI_BITREG_ARB_DIS */ {ACPI_REGISTER_PM2_CONTROL, + ACPI_BITPOSITION_ARB_DISABLE, + ACPI_BITMASK_ARB_DISABLE} +}; + +struct acpi_fixed_event_info acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS] = { + /* ACPI_EVENT_PMTIMER */ {ACPI_BITREG_TIMER_STATUS, + ACPI_BITREG_TIMER_ENABLE, + ACPI_BITMASK_TIMER_STATUS, + ACPI_BITMASK_TIMER_ENABLE}, + /* ACPI_EVENT_GLOBAL */ {ACPI_BITREG_GLOBAL_LOCK_STATUS, + ACPI_BITREG_GLOBAL_LOCK_ENABLE, + ACPI_BITMASK_GLOBAL_LOCK_STATUS, + ACPI_BITMASK_GLOBAL_LOCK_ENABLE}, + /* ACPI_EVENT_POWER_BUTTON */ {ACPI_BITREG_POWER_BUTTON_STATUS, + ACPI_BITREG_POWER_BUTTON_ENABLE, + ACPI_BITMASK_POWER_BUTTON_STATUS, + ACPI_BITMASK_POWER_BUTTON_ENABLE}, + /* ACPI_EVENT_SLEEP_BUTTON */ {ACPI_BITREG_SLEEP_BUTTON_STATUS, + ACPI_BITREG_SLEEP_BUTTON_ENABLE, + ACPI_BITMASK_SLEEP_BUTTON_STATUS, + ACPI_BITMASK_SLEEP_BUTTON_ENABLE}, + /* ACPI_EVENT_RTC */ {ACPI_BITREG_RT_CLOCK_STATUS, + ACPI_BITREG_RT_CLOCK_ENABLE, + ACPI_BITMASK_RT_CLOCK_STATUS, + ACPI_BITMASK_RT_CLOCK_ENABLE}, +}; +#endif /* !ACPI_REDUCED_HARDWARE */ + +/* Public globals */ + +ACPI_EXPORT_SYMBOL(acpi_gbl_FADT) +ACPI_EXPORT_SYMBOL(acpi_dbg_level) +ACPI_EXPORT_SYMBOL(acpi_dbg_layer) +ACPI_EXPORT_SYMBOL(acpi_gpe_count) +ACPI_EXPORT_SYMBOL(acpi_current_gpe_count) diff --git a/kernel/drivers/acpi/acpica/uthex.c b/kernel/drivers/acpi/acpica/uthex.c new file mode 100644 index 000000000..aa448278b --- /dev/null +++ b/kernel/drivers/acpi/acpica/uthex.c @@ -0,0 +1,100 @@ +/****************************************************************************** + * + * Module Name: uthex -- Hex/ASCII support functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_COMPILER +ACPI_MODULE_NAME("uthex") + +/* Hex to ASCII conversion table */ +static char acpi_gbl_hex_to_ascii[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F' +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_hex_to_ascii_char + * + * PARAMETERS: integer - Contains the hex digit + * position - bit position of the digit within the + * integer (multiple of 4) + * + * RETURN: The converted Ascii character + * + * DESCRIPTION: Convert a hex digit to an Ascii character + * + ******************************************************************************/ + +char acpi_ut_hex_to_ascii_char(u64 integer, u32 position) +{ + + return (acpi_gbl_hex_to_ascii[(integer >> position) & 0xF]); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_hex_char_to_value + * + * PARAMETERS: ascii_char - Hex character in Ascii + * + * RETURN: The binary value of the ascii/hex character + * + * DESCRIPTION: Perform ascii-to-hex translation + * + ******************************************************************************/ + +u8 acpi_ut_ascii_char_to_hex(int hex_char) +{ + + if (hex_char <= 0x39) { + return ((u8)(hex_char - 0x30)); + } + + if (hex_char <= 0x46) { + return ((u8)(hex_char - 0x37)); + } + + return ((u8)(hex_char - 0x57)); +} diff --git a/kernel/drivers/acpi/acpica/utids.c b/kernel/drivers/acpi/acpica/utids.c new file mode 100644 index 000000000..27431cfc1 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utids.c @@ -0,0 +1,418 @@ +/****************************************************************************** + * + * Module Name: utids - support for device Ids - HID, UID, CID + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utids") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_HID + * + * PARAMETERS: device_node - Node for the device + * return_id - Where the string HID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _HID control method that returns the hardware + * ID of the device. The HID is either an 32-bit encoded EISAID + * Integer or a String. A string is always returned. An EISAID + * is converted to a string. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ +acpi_status +acpi_ut_execute_HID(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id **return_id) +{ + union acpi_operand_object *obj_desc; + struct acpi_pnp_device_id *hid; + u32 length; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_HID); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__HID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the size of the String to be returned, includes null terminator */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + length = ACPI_EISAID_STRING_SIZE; + } else { + length = obj_desc->string.length + 1; + } + + /* Allocate a buffer for the HID */ + + hid = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) + + (acpi_size) length); + if (!hid) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for the string starts after PNP_DEVICE_ID struct */ + + hid->string = + ACPI_ADD_PTR(char, hid, sizeof(struct acpi_pnp_device_id)); + + /* Convert EISAID to a string or simply copy existing string */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + acpi_ex_eisa_id_to_string(hid->string, obj_desc->integer.value); + } else { + ACPI_STRCPY(hid->string, obj_desc->string.pointer); + } + + hid->length = length; + *return_id = hid; + +cleanup: + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_SUB + * + * PARAMETERS: device_node - Node for the device + * return_id - Where the _SUB is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _SUB control method that returns the subsystem + * ID of the device. The _SUB value is always a string containing + * either a valid PNP or ACPI ID. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_SUB(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id **return_id) +{ + union acpi_operand_object *obj_desc; + struct acpi_pnp_device_id *sub; + u32 length; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_SUB); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__SUB, + ACPI_BTYPE_STRING, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the size of the String to be returned, includes null terminator */ + + length = obj_desc->string.length + 1; + + /* Allocate a buffer for the SUB */ + + sub = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) + + (acpi_size) length); + if (!sub) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for the string starts after PNP_DEVICE_ID struct */ + + sub->string = + ACPI_ADD_PTR(char, sub, sizeof(struct acpi_pnp_device_id)); + + /* Simply copy existing string */ + + ACPI_STRCPY(sub->string, obj_desc->string.pointer); + sub->length = length; + *return_id = sub; + +cleanup: + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_UID + * + * PARAMETERS: device_node - Node for the device + * return_id - Where the string UID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _UID control method that returns the unique + * ID of the device. The UID is either a 64-bit Integer (NOT an + * EISAID) or a string. Always returns a string. A 64-bit integer + * is converted to a decimal string. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_UID(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id **return_id) +{ + union acpi_operand_object *obj_desc; + struct acpi_pnp_device_id *uid; + u32 length; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_UID); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__UID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the size of the String to be returned, includes null terminator */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + length = ACPI_MAX64_DECIMAL_DIGITS + 1; + } else { + length = obj_desc->string.length + 1; + } + + /* Allocate a buffer for the UID */ + + uid = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) + + (acpi_size) length); + if (!uid) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for the string starts after PNP_DEVICE_ID struct */ + + uid->string = + ACPI_ADD_PTR(char, uid, sizeof(struct acpi_pnp_device_id)); + + /* Convert an Integer to string, or just copy an existing string */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + acpi_ex_integer_to_string(uid->string, obj_desc->integer.value); + } else { + ACPI_STRCPY(uid->string, obj_desc->string.pointer); + } + + uid->length = length; + *return_id = uid; + +cleanup: + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_CID + * + * PARAMETERS: device_node - Node for the device + * return_cid_list - Where the CID list is returned + * + * RETURN: Status, list of CID strings + * + * DESCRIPTION: Executes the _CID control method that returns one or more + * compatible hardware IDs for the device. + * + * NOTE: Internal function, no parameter validation + * + * A _CID method can return either a single compatible ID or a package of + * compatible IDs. Each compatible ID can be one of the following: + * 1) Integer (32 bit compressed EISA ID) or + * 2) String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss") + * + * The Integer CIDs are converted to string format by this function. + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_CID(struct acpi_namespace_node *device_node, + struct acpi_pnp_device_id_list **return_cid_list) +{ + union acpi_operand_object **cid_objects; + union acpi_operand_object *obj_desc; + struct acpi_pnp_device_id_list *cid_list; + char *next_id_string; + u32 string_area_size; + u32 length; + u32 cid_list_size; + acpi_status status; + u32 count; + u32 i; + + ACPI_FUNCTION_TRACE(ut_execute_CID); + + /* Evaluate the _CID method for this device */ + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__CID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING + | ACPI_BTYPE_PACKAGE, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Get the count and size of the returned _CIDs. _CID can return either + * a Package of Integers/Strings or a single Integer or String. + * Note: This section also validates that all CID elements are of the + * correct type (Integer or String). + */ + if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { + count = obj_desc->package.count; + cid_objects = obj_desc->package.elements; + } else { /* Single Integer or String CID */ + + count = 1; + cid_objects = &obj_desc; + } + + string_area_size = 0; + for (i = 0; i < count; i++) { + + /* String lengths include null terminator */ + + switch (cid_objects[i]->common.type) { + case ACPI_TYPE_INTEGER: + + string_area_size += ACPI_EISAID_STRING_SIZE; + break; + + case ACPI_TYPE_STRING: + + string_area_size += cid_objects[i]->string.length + 1; + break; + + default: + + status = AE_TYPE; + goto cleanup; + } + } + + /* + * Now that we know the length of the CIDs, allocate return buffer: + * 1) Size of the base structure + + * 2) Size of the CID PNP_DEVICE_ID array + + * 3) Size of the actual CID strings + */ + cid_list_size = sizeof(struct acpi_pnp_device_id_list) + + ((count - 1) * sizeof(struct acpi_pnp_device_id)) + + string_area_size; + + cid_list = ACPI_ALLOCATE_ZEROED(cid_list_size); + if (!cid_list) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for CID strings starts after the CID PNP_DEVICE_ID array */ + + next_id_string = ACPI_CAST_PTR(char, cid_list->ids) + + ((acpi_size) count * sizeof(struct acpi_pnp_device_id)); + + /* Copy/convert the CIDs to the return buffer */ + + for (i = 0; i < count; i++) { + if (cid_objects[i]->common.type == ACPI_TYPE_INTEGER) { + + /* Convert the Integer (EISAID) CID to a string */ + + acpi_ex_eisa_id_to_string(next_id_string, + cid_objects[i]->integer. + value); + length = ACPI_EISAID_STRING_SIZE; + } else { /* ACPI_TYPE_STRING */ + + /* Copy the String CID from the returned object */ + + ACPI_STRCPY(next_id_string, + cid_objects[i]->string.pointer); + length = cid_objects[i]->string.length + 1; + } + + cid_list->ids[i].string = next_id_string; + cid_list->ids[i].length = length; + next_id_string += length; + } + + /* Finish the CID list */ + + cid_list->count = count; + cid_list->list_size = cid_list_size; + *return_cid_list = cid_list; + +cleanup: + + /* On exit, we must delete the _CID return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/utinit.c b/kernel/drivers/acpi/acpica/utinit.c new file mode 100644 index 000000000..e402e07b4 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utinit.c @@ -0,0 +1,315 @@ +/****************************************************************************** + * + * Module Name: utinit - Common ACPI subsystem initialization + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acevents.h" +#include "actables.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utinit") + +/* Local prototypes */ +static void acpi_ut_terminate(void); + +#if (!ACPI_REDUCED_HARDWARE) + +static void acpi_ut_free_gpe_lists(void); + +#else + +#define acpi_ut_free_gpe_lists() +#endif /* !ACPI_REDUCED_HARDWARE */ + +#if (!ACPI_REDUCED_HARDWARE) +/****************************************************************************** + * + * FUNCTION: acpi_ut_free_gpe_lists + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Free global GPE lists + * + ******************************************************************************/ + +static void acpi_ut_free_gpe_lists(void) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_block_info *next_gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_xrupt_info *next_gpe_xrupt_info; + + /* Free global GPE blocks and related info structures */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + next_gpe_block = gpe_block->next; + ACPI_FREE(gpe_block->event_info); + ACPI_FREE(gpe_block->register_info); + ACPI_FREE(gpe_block); + + gpe_block = next_gpe_block; + } + next_gpe_xrupt_info = gpe_xrupt_info->next; + ACPI_FREE(gpe_xrupt_info); + gpe_xrupt_info = next_gpe_xrupt_info; + } +} +#endif /* !ACPI_REDUCED_HARDWARE */ + +/******************************************************************************* + * + * FUNCTION: acpi_ut_init_globals + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize ACPICA globals. All globals that require specific + * initialization should be initialized here. This allows for + * a warm restart. + * + ******************************************************************************/ + +acpi_status acpi_ut_init_globals(void) +{ + acpi_status status; + u32 i; + + ACPI_FUNCTION_TRACE(ut_init_globals); + + /* Create all memory caches */ + + status = acpi_ut_create_caches(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Address Range lists */ + + for (i = 0; i < ACPI_ADDRESS_RANGE_MAX; i++) { + acpi_gbl_address_range_list[i] = NULL; + } + + /* Mutex locked flags */ + + for (i = 0; i < ACPI_NUM_MUTEX; i++) { + acpi_gbl_mutex_info[i].mutex = NULL; + acpi_gbl_mutex_info[i].thread_id = ACPI_MUTEX_NOT_ACQUIRED; + acpi_gbl_mutex_info[i].use_count = 0; + } + + for (i = 0; i < ACPI_NUM_OWNERID_MASKS; i++) { + acpi_gbl_owner_id_mask[i] = 0; + } + + /* Last owner_ID is never valid */ + + acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS - 1] = 0x80000000; + + /* Event counters */ + + acpi_method_count = 0; + acpi_sci_count = 0; + acpi_gpe_count = 0; + + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + acpi_fixed_event_count[i] = 0; + } + +#if (!ACPI_REDUCED_HARDWARE) + + /* GPE/SCI support */ + + acpi_gbl_all_gpes_initialized = FALSE; + acpi_gbl_gpe_xrupt_list_head = NULL; + acpi_gbl_gpe_fadt_blocks[0] = NULL; + acpi_gbl_gpe_fadt_blocks[1] = NULL; + acpi_current_gpe_count = 0; + + acpi_gbl_global_event_handler = NULL; + acpi_gbl_sci_handler_list = NULL; + +#endif /* !ACPI_REDUCED_HARDWARE */ + + /* Global handlers */ + + acpi_gbl_global_notify[0].handler = NULL; + acpi_gbl_global_notify[1].handler = NULL; + acpi_gbl_exception_handler = NULL; + acpi_gbl_init_handler = NULL; + acpi_gbl_table_handler = NULL; + acpi_gbl_interface_handler = NULL; + + /* Global Lock support */ + + acpi_gbl_global_lock_semaphore = NULL; + acpi_gbl_global_lock_mutex = NULL; + acpi_gbl_global_lock_acquired = FALSE; + acpi_gbl_global_lock_handle = 0; + acpi_gbl_global_lock_present = FALSE; + + /* Miscellaneous variables */ + + acpi_gbl_DSDT = NULL; + acpi_gbl_cm_single_step = FALSE; + acpi_gbl_shutdown = FALSE; + acpi_gbl_ns_lookup_count = 0; + acpi_gbl_ps_find_count = 0; + acpi_gbl_acpi_hardware_present = TRUE; + acpi_gbl_last_owner_id_index = 0; + acpi_gbl_next_owner_id_offset = 0; + acpi_gbl_trace_dbg_level = 0; + acpi_gbl_trace_dbg_layer = 0; + acpi_gbl_debugger_configuration = DEBUGGER_THREADING; + acpi_gbl_osi_mutex = NULL; + acpi_gbl_reg_methods_executed = FALSE; + + /* Hardware oriented */ + + acpi_gbl_events_initialized = FALSE; + acpi_gbl_system_awake_and_running = TRUE; + + /* Namespace */ + + acpi_gbl_module_code_list = NULL; + acpi_gbl_root_node = NULL; + acpi_gbl_root_node_struct.name.integer = ACPI_ROOT_NAME; + acpi_gbl_root_node_struct.descriptor_type = ACPI_DESC_TYPE_NAMED; + acpi_gbl_root_node_struct.type = ACPI_TYPE_DEVICE; + acpi_gbl_root_node_struct.parent = NULL; + acpi_gbl_root_node_struct.child = NULL; + acpi_gbl_root_node_struct.peer = NULL; + acpi_gbl_root_node_struct.object = NULL; + +#ifdef ACPI_DISASSEMBLER + acpi_gbl_external_list = NULL; + acpi_gbl_num_external_methods = 0; + acpi_gbl_resolved_external_methods = 0; +#endif + +#ifdef ACPI_DEBUG_OUTPUT + acpi_gbl_lowest_stack_pointer = ACPI_CAST_PTR(acpi_size, ACPI_SIZE_MAX); +#endif + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_gbl_display_final_mem_stats = FALSE; + acpi_gbl_disable_mem_tracking = FALSE; +#endif + + ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = FALSE); + + return_ACPI_STATUS(AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_ut_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Free global memory + * + ******************************************************************************/ + +static void acpi_ut_terminate(void) +{ + ACPI_FUNCTION_TRACE(ut_terminate); + + acpi_ut_free_gpe_lists(); + acpi_ut_delete_address_lists(); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_subsystem_shutdown + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Shutdown the various components. Do not delete the mutex + * objects here, because the AML debugger may be still running. + * + ******************************************************************************/ + +void acpi_ut_subsystem_shutdown(void) +{ + ACPI_FUNCTION_TRACE(ut_subsystem_shutdown); + +#ifndef ACPI_ASL_COMPILER + + /* Close the acpi_event Handling */ + + acpi_ev_terminate(); + + /* Delete any dynamic _OSI interfaces */ + + acpi_ut_interface_terminate(); +#endif + + /* Close the Namespace */ + + acpi_ns_terminate(); + + /* Delete the ACPI tables */ + + acpi_tb_terminate(); + + /* Close the globals */ + + acpi_ut_terminate(); + + /* Purge the local caches */ + + (void)acpi_ut_delete_caches(); + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/utlock.c b/kernel/drivers/acpi/acpica/utlock.c new file mode 100644 index 000000000..089f78bbd --- /dev/null +++ b/kernel/drivers/acpi/acpica/utlock.c @@ -0,0 +1,175 @@ +/****************************************************************************** + * + * Module Name: utlock - Reader/Writer lock interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utlock") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_rw_lock + * acpi_ut_delete_rw_lock + * + * PARAMETERS: lock - Pointer to a valid RW lock + * + * RETURN: Status + * + * DESCRIPTION: Reader/writer lock creation and deletion interfaces. + * + ******************************************************************************/ +acpi_status acpi_ut_create_rw_lock(struct acpi_rw_lock *lock) +{ + acpi_status status; + + lock->num_readers = 0; + status = acpi_os_create_mutex(&lock->reader_mutex); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = acpi_os_create_mutex(&lock->writer_mutex); + return (status); +} + +void acpi_ut_delete_rw_lock(struct acpi_rw_lock *lock) +{ + + acpi_os_delete_mutex(lock->reader_mutex); + acpi_os_delete_mutex(lock->writer_mutex); + + lock->num_readers = 0; + lock->reader_mutex = NULL; + lock->writer_mutex = NULL; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_acquire_read_lock + * acpi_ut_release_read_lock + * + * PARAMETERS: lock - Pointer to a valid RW lock + * + * RETURN: Status + * + * DESCRIPTION: Reader interfaces for reader/writer locks. On acquisition, + * only the first reader acquires the write mutex. On release, + * only the last reader releases the write mutex. Although this + * algorithm can in theory starve writers, this should not be a + * problem with ACPICA since the subsystem is infrequently used + * in comparison to (for example) an I/O system. + * + ******************************************************************************/ + +acpi_status acpi_ut_acquire_read_lock(struct acpi_rw_lock *lock) +{ + acpi_status status; + + status = acpi_os_acquire_mutex(lock->reader_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Acquire the write lock only for the first reader */ + + lock->num_readers++; + if (lock->num_readers == 1) { + status = + acpi_os_acquire_mutex(lock->writer_mutex, + ACPI_WAIT_FOREVER); + } + + acpi_os_release_mutex(lock->reader_mutex); + return (status); +} + +acpi_status acpi_ut_release_read_lock(struct acpi_rw_lock *lock) +{ + acpi_status status; + + status = acpi_os_acquire_mutex(lock->reader_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Release the write lock only for the very last reader */ + + lock->num_readers--; + if (lock->num_readers == 0) { + acpi_os_release_mutex(lock->writer_mutex); + } + + acpi_os_release_mutex(lock->reader_mutex); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_acquire_write_lock + * acpi_ut_release_write_lock + * + * PARAMETERS: lock - Pointer to a valid RW lock + * + * RETURN: Status + * + * DESCRIPTION: Writer interfaces for reader/writer locks. Simply acquire or + * release the writer mutex associated with the lock. Acquisition + * of the lock is fully exclusive and will block all readers and + * writers until it is released. + * + ******************************************************************************/ + +acpi_status acpi_ut_acquire_write_lock(struct acpi_rw_lock *lock) +{ + acpi_status status; + + status = acpi_os_acquire_mutex(lock->writer_mutex, ACPI_WAIT_FOREVER); + return (status); +} + +void acpi_ut_release_write_lock(struct acpi_rw_lock *lock) +{ + + acpi_os_release_mutex(lock->writer_mutex); +} diff --git a/kernel/drivers/acpi/acpica/utmath.c b/kernel/drivers/acpi/acpica/utmath.c new file mode 100644 index 000000000..f9ff100f0 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utmath.c @@ -0,0 +1,324 @@ +/******************************************************************************* + * + * Module Name: utmath - Integer math support routines + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utmath") + +/* + * Optional support for 64-bit double-precision integer divide. This code + * is configurable and is implemented in order to support 32-bit kernel + * environments where a 64-bit double-precision math library is not available. + * + * Support for a more normal 64-bit divide/modulo (with check for a divide- + * by-zero) appears after this optional section of code. + */ +#ifndef ACPI_USE_NATIVE_DIVIDE +/* Structures used only for 64-bit divide */ +typedef struct uint64_struct { + u32 lo; + u32 hi; + +} uint64_struct; + +typedef union uint64_overlay { + u64 full; + struct uint64_struct part; + +} uint64_overlay; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_short_divide + * + * PARAMETERS: dividend - 64-bit dividend + * divisor - 32-bit divisor + * out_quotient - Pointer to where the quotient is returned + * out_remainder - Pointer to where the remainder is returned + * + * RETURN: Status (Checks for divide-by-zero) + * + * DESCRIPTION: Perform a short (maximum 64 bits divided by 32 bits) + * divide and modulo. The result is a 64-bit quotient and a + * 32-bit remainder. + * + ******************************************************************************/ + +acpi_status +acpi_ut_short_divide(u64 dividend, + u32 divisor, u64 *out_quotient, u32 *out_remainder) +{ + union uint64_overlay dividend_ovl; + union uint64_overlay quotient; + u32 remainder32; + + ACPI_FUNCTION_TRACE(ut_short_divide); + + /* Always check for a zero divisor */ + + if (divisor == 0) { + ACPI_ERROR((AE_INFO, "Divide by zero")); + return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO); + } + + dividend_ovl.full = dividend; + + /* + * The quotient is 64 bits, the remainder is always 32 bits, + * and is generated by the second divide. + */ + ACPI_DIV_64_BY_32(0, dividend_ovl.part.hi, divisor, + quotient.part.hi, remainder32); + ACPI_DIV_64_BY_32(remainder32, dividend_ovl.part.lo, divisor, + quotient.part.lo, remainder32); + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = quotient.full; + } + if (out_remainder) { + *out_remainder = remainder32; + } + + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_divide + * + * PARAMETERS: in_dividend - Dividend + * in_divisor - Divisor + * out_quotient - Pointer to where the quotient is returned + * out_remainder - Pointer to where the remainder is returned + * + * RETURN: Status (Checks for divide-by-zero) + * + * DESCRIPTION: Perform a divide and modulo. + * + ******************************************************************************/ + +acpi_status +acpi_ut_divide(u64 in_dividend, + u64 in_divisor, u64 *out_quotient, u64 *out_remainder) +{ + union uint64_overlay dividend; + union uint64_overlay divisor; + union uint64_overlay quotient; + union uint64_overlay remainder; + union uint64_overlay normalized_dividend; + union uint64_overlay normalized_divisor; + u32 partial1; + union uint64_overlay partial2; + union uint64_overlay partial3; + + ACPI_FUNCTION_TRACE(ut_divide); + + /* Always check for a zero divisor */ + + if (in_divisor == 0) { + ACPI_ERROR((AE_INFO, "Divide by zero")); + return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO); + } + + divisor.full = in_divisor; + dividend.full = in_dividend; + if (divisor.part.hi == 0) { + /* + * 1) Simplest case is where the divisor is 32 bits, we can + * just do two divides + */ + remainder.part.hi = 0; + + /* + * The quotient is 64 bits, the remainder is always 32 bits, + * and is generated by the second divide. + */ + ACPI_DIV_64_BY_32(0, dividend.part.hi, divisor.part.lo, + quotient.part.hi, partial1); + ACPI_DIV_64_BY_32(partial1, dividend.part.lo, divisor.part.lo, + quotient.part.lo, remainder.part.lo); + } + + else { + /* + * 2) The general case where the divisor is a full 64 bits + * is more difficult + */ + quotient.part.hi = 0; + normalized_dividend = dividend; + normalized_divisor = divisor; + + /* Normalize the operands (shift until the divisor is < 32 bits) */ + + do { + ACPI_SHIFT_RIGHT_64(normalized_divisor.part.hi, + normalized_divisor.part.lo); + ACPI_SHIFT_RIGHT_64(normalized_dividend.part.hi, + normalized_dividend.part.lo); + + } while (normalized_divisor.part.hi != 0); + + /* Partial divide */ + + ACPI_DIV_64_BY_32(normalized_dividend.part.hi, + normalized_dividend.part.lo, + normalized_divisor.part.lo, + quotient.part.lo, partial1); + + /* + * The quotient is always 32 bits, and simply requires adjustment. + * The 64-bit remainder must be generated. + */ + partial1 = quotient.part.lo * divisor.part.hi; + partial2.full = (u64) quotient.part.lo * divisor.part.lo; + partial3.full = (u64) partial2.part.hi + partial1; + + remainder.part.hi = partial3.part.lo; + remainder.part.lo = partial2.part.lo; + + if (partial3.part.hi == 0) { + if (partial3.part.lo >= dividend.part.hi) { + if (partial3.part.lo == dividend.part.hi) { + if (partial2.part.lo > dividend.part.lo) { + quotient.part.lo--; + remainder.full -= divisor.full; + } + } else { + quotient.part.lo--; + remainder.full -= divisor.full; + } + } + + remainder.full = remainder.full - dividend.full; + remainder.part.hi = (u32) - ((s32) remainder.part.hi); + remainder.part.lo = (u32) - ((s32) remainder.part.lo); + + if (remainder.part.lo) { + remainder.part.hi--; + } + } + } + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = quotient.full; + } + if (out_remainder) { + *out_remainder = remainder.full; + } + + return_ACPI_STATUS(AE_OK); +} + +#else +/******************************************************************************* + * + * FUNCTION: acpi_ut_short_divide, acpi_ut_divide + * + * PARAMETERS: See function headers above + * + * DESCRIPTION: Native versions of the ut_divide functions. Use these if either + * 1) The target is a 64-bit platform and therefore 64-bit + * integer math is supported directly by the machine. + * 2) The target is a 32-bit or 16-bit platform, and the + * double-precision integer math library is available to + * perform the divide. + * + ******************************************************************************/ +acpi_status +acpi_ut_short_divide(u64 in_dividend, + u32 divisor, u64 *out_quotient, u32 *out_remainder) +{ + + ACPI_FUNCTION_TRACE(ut_short_divide); + + /* Always check for a zero divisor */ + + if (divisor == 0) { + ACPI_ERROR((AE_INFO, "Divide by zero")); + return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO); + } + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = in_dividend / divisor; + } + if (out_remainder) { + *out_remainder = (u32) (in_dividend % divisor); + } + + return_ACPI_STATUS(AE_OK); +} + +acpi_status +acpi_ut_divide(u64 in_dividend, + u64 in_divisor, u64 *out_quotient, u64 *out_remainder) +{ + ACPI_FUNCTION_TRACE(ut_divide); + + /* Always check for a zero divisor */ + + if (in_divisor == 0) { + ACPI_ERROR((AE_INFO, "Divide by zero")); + return_ACPI_STATUS(AE_AML_DIVIDE_BY_ZERO); + } + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = in_dividend / in_divisor; + } + if (out_remainder) { + *out_remainder = in_dividend % in_divisor; + } + + return_ACPI_STATUS(AE_OK); +} + +#endif diff --git a/kernel/drivers/acpi/acpica/utmisc.c b/kernel/drivers/acpi/acpica/utmisc.c new file mode 100644 index 000000000..cbb7034d2 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utmisc.c @@ -0,0 +1,411 @@ +/******************************************************************************* + * + * Module Name: utmisc - common utility procedures + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utmisc") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_is_pci_root_bridge + * + * PARAMETERS: id - The HID/CID in string format + * + * RETURN: TRUE if the Id is a match for a PCI/PCI-Express Root Bridge + * + * DESCRIPTION: Determine if the input ID is a PCI Root Bridge ID. + * + ******************************************************************************/ +u8 acpi_ut_is_pci_root_bridge(char *id) +{ + + /* + * Check if this is a PCI root bridge. + * ACPI 3.0+: check for a PCI Express root also. + */ + if (!(ACPI_STRCMP(id, + PCI_ROOT_HID_STRING)) || + !(ACPI_STRCMP(id, PCI_EXPRESS_ROOT_HID_STRING))) { + return (TRUE); + } + + return (FALSE); +} + +#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP) +/******************************************************************************* + * + * FUNCTION: acpi_ut_is_aml_table + * + * PARAMETERS: table - An ACPI table + * + * RETURN: TRUE if table contains executable AML; FALSE otherwise + * + * DESCRIPTION: Check ACPI Signature for a table that contains AML code. + * Currently, these are DSDT,SSDT,PSDT. All other table types are + * data tables that do not contain AML code. + * + ******************************************************************************/ + +u8 acpi_ut_is_aml_table(struct acpi_table_header *table) +{ + + /* These are the only tables that contain executable AML */ + + if (ACPI_COMPARE_NAME(table->signature, ACPI_SIG_DSDT) || + ACPI_COMPARE_NAME(table->signature, ACPI_SIG_PSDT) || + ACPI_COMPARE_NAME(table->signature, ACPI_SIG_SSDT)) { + return (TRUE); + } + + return (FALSE); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dword_byte_swap + * + * PARAMETERS: value - Value to be converted + * + * RETURN: u32 integer with bytes swapped + * + * DESCRIPTION: Convert a 32-bit value to big-endian (swap the bytes) + * + ******************************************************************************/ + +u32 acpi_ut_dword_byte_swap(u32 value) +{ + union { + u32 value; + u8 bytes[4]; + } out; + union { + u32 value; + u8 bytes[4]; + } in; + + ACPI_FUNCTION_ENTRY(); + + in.value = value; + + out.bytes[0] = in.bytes[3]; + out.bytes[1] = in.bytes[2]; + out.bytes[2] = in.bytes[1]; + out.bytes[3] = in.bytes[0]; + + return (out.value); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_set_integer_width + * + * PARAMETERS: Revision From DSDT header + * + * RETURN: None + * + * DESCRIPTION: Set the global integer bit width based upon the revision + * of the DSDT. For Revision 1 and 0, Integers are 32 bits. + * For Revision 2 and above, Integers are 64 bits. Yes, this + * makes a difference. + * + ******************************************************************************/ + +void acpi_ut_set_integer_width(u8 revision) +{ + + if (revision < 2) { + + /* 32-bit case */ + + acpi_gbl_integer_bit_width = 32; + acpi_gbl_integer_nybble_width = 8; + acpi_gbl_integer_byte_width = 4; + } else { + /* 64-bit case (ACPI 2.0+) */ + + acpi_gbl_integer_bit_width = 64; + acpi_gbl_integer_nybble_width = 16; + acpi_gbl_integer_byte_width = 8; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_update_state_and_push + * + * PARAMETERS: object - Object to be added to the new state + * action - Increment/Decrement + * state_list - List the state will be added to + * + * RETURN: Status + * + * DESCRIPTION: Create a new state and push it + * + ******************************************************************************/ + +acpi_status +acpi_ut_create_update_state_and_push(union acpi_operand_object *object, + u16 action, + union acpi_generic_state **state_list) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_ENTRY(); + + /* Ignore null objects; these are expected */ + + if (!object) { + return (AE_OK); + } + + state = acpi_ut_create_update_state(object, action); + if (!state) { + return (AE_NO_MEMORY); + } + + acpi_ut_push_generic_state(state_list, state); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_walk_package_tree + * + * PARAMETERS: source_object - The package to walk + * target_object - Target object (if package is being copied) + * walk_callback - Called once for each package element + * context - Passed to the callback function + * + * RETURN: Status + * + * DESCRIPTION: Walk through a package + * + ******************************************************************************/ + +acpi_status +acpi_ut_walk_package_tree(union acpi_operand_object *source_object, + void *target_object, + acpi_pkg_callback walk_callback, void *context) +{ + acpi_status status = AE_OK; + union acpi_generic_state *state_list = NULL; + union acpi_generic_state *state; + u32 this_index; + union acpi_operand_object *this_source_obj; + + ACPI_FUNCTION_TRACE(ut_walk_package_tree); + + state = acpi_ut_create_pkg_state(source_object, target_object, 0); + if (!state) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + while (state) { + + /* Get one element of the package */ + + this_index = state->pkg.index; + this_source_obj = (union acpi_operand_object *) + state->pkg.source_object->package.elements[this_index]; + + /* + * Check for: + * 1) An uninitialized package element. It is completely + * legal to declare a package and leave it uninitialized + * 2) Not an internal object - can be a namespace node instead + * 3) Any type other than a package. Packages are handled in else + * case below. + */ + if ((!this_source_obj) || + (ACPI_GET_DESCRIPTOR_TYPE(this_source_obj) != + ACPI_DESC_TYPE_OPERAND) + || (this_source_obj->common.type != ACPI_TYPE_PACKAGE)) { + status = + walk_callback(ACPI_COPY_TYPE_SIMPLE, + this_source_obj, state, context); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + state->pkg.index++; + while (state->pkg.index >= + state->pkg.source_object->package.count) { + /* + * We've handled all of the objects at this level, This means + * that we have just completed a package. That package may + * have contained one or more packages itself. + * + * Delete this state and pop the previous state (package). + */ + acpi_ut_delete_generic_state(state); + state = acpi_ut_pop_generic_state(&state_list); + + /* Finished when there are no more states */ + + if (!state) { + /* + * We have handled all of the objects in the top level + * package just add the length of the package objects + * and exit + */ + return_ACPI_STATUS(AE_OK); + } + + /* + * Go back up a level and move the index past the just + * completed package object. + */ + state->pkg.index++; + } + } else { + /* This is a subobject of type package */ + + status = + walk_callback(ACPI_COPY_TYPE_PACKAGE, + this_source_obj, state, context); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Push the current state and create a new one + * The callback above returned a new target package object. + */ + acpi_ut_push_generic_state(&state_list, state); + state = acpi_ut_create_pkg_state(this_source_obj, + state->pkg. + this_target_obj, 0); + if (!state) { + + /* Free any stacked Update State objects */ + + while (state_list) { + state = + acpi_ut_pop_generic_state + (&state_list); + acpi_ut_delete_generic_state(state); + } + return_ACPI_STATUS(AE_NO_MEMORY); + } + } + } + + /* We should never get here */ + + return_ACPI_STATUS(AE_AML_INTERNAL); +} + +#ifdef ACPI_DEBUG_OUTPUT +/******************************************************************************* + * + * FUNCTION: acpi_ut_display_init_pathname + * + * PARAMETERS: type - Object type of the node + * obj_handle - Handle whose pathname will be displayed + * path - Additional path string to be appended. + * (NULL if no extra path) + * + * RETURN: acpi_status + * + * DESCRIPTION: Display full pathname of an object, DEBUG ONLY + * + ******************************************************************************/ + +void +acpi_ut_display_init_pathname(u8 type, + struct acpi_namespace_node *obj_handle, + char *path) +{ + acpi_status status; + struct acpi_buffer buffer; + + ACPI_FUNCTION_ENTRY(); + + /* Only print the path if the appropriate debug level is enabled */ + + if (!(acpi_dbg_level & ACPI_LV_INIT_NAMES)) { + return; + } + + /* Get the full pathname to the node */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname(obj_handle, &buffer); + if (ACPI_FAILURE(status)) { + return; + } + + /* Print what we're doing */ + + switch (type) { + case ACPI_TYPE_METHOD: + + acpi_os_printf("Executing "); + break; + + default: + + acpi_os_printf("Initializing "); + break; + } + + /* Print the object type and pathname */ + + acpi_os_printf("%-12s %s", + acpi_ut_get_type_name(type), (char *)buffer.pointer); + + /* Extra path is used to append names like _STA, _INI, etc. */ + + if (path) { + acpi_os_printf(".%s", path); + } + acpi_os_printf("\n"); + + ACPI_FREE(buffer.pointer); +} +#endif diff --git a/kernel/drivers/acpi/acpica/utmutex.c b/kernel/drivers/acpi/acpica/utmutex.c new file mode 100644 index 000000000..938795507 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utmutex.c @@ -0,0 +1,360 @@ +/******************************************************************************* + * + * Module Name: utmutex - local mutex support + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utmutex") + +/* Local prototypes */ +static acpi_status acpi_ut_create_mutex(acpi_mutex_handle mutex_id); + +static void acpi_ut_delete_mutex(acpi_mutex_handle mutex_id); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_mutex_initialize + * + * PARAMETERS: None. + * + * RETURN: Status + * + * DESCRIPTION: Create the system mutex objects. This includes mutexes, + * spin locks, and reader/writer locks. + * + ******************************************************************************/ + +acpi_status acpi_ut_mutex_initialize(void) +{ + u32 i; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_mutex_initialize); + + /* Create each of the predefined mutex objects */ + + for (i = 0; i < ACPI_NUM_MUTEX; i++) { + status = acpi_ut_create_mutex(i); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* Create the spinlocks for use at interrupt level or for speed */ + + status = acpi_os_create_lock (&acpi_gbl_gpe_lock); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_os_create_raw_lock (&acpi_gbl_hardware_lock); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_os_create_lock(&acpi_gbl_reference_count_lock); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Mutex for _OSI support */ + + status = acpi_os_create_mutex(&acpi_gbl_osi_mutex); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Create the reader/writer lock for namespace access */ + + status = acpi_ut_create_rw_lock(&acpi_gbl_namespace_rw_lock); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_mutex_terminate + * + * PARAMETERS: None. + * + * RETURN: None. + * + * DESCRIPTION: Delete all of the system mutex objects. This includes mutexes, + * spin locks, and reader/writer locks. + * + ******************************************************************************/ + +void acpi_ut_mutex_terminate(void) +{ + u32 i; + + ACPI_FUNCTION_TRACE(ut_mutex_terminate); + + /* Delete each predefined mutex object */ + + for (i = 0; i < ACPI_NUM_MUTEX; i++) { + acpi_ut_delete_mutex(i); + } + + acpi_os_delete_mutex(acpi_gbl_osi_mutex); + + /* Delete the spinlocks */ + + acpi_os_delete_lock(acpi_gbl_gpe_lock); + acpi_os_delete_raw_lock(acpi_gbl_hardware_lock); + acpi_os_delete_lock(acpi_gbl_reference_count_lock); + + /* Delete the reader/writer lock */ + + acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_lock); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_mutex + * + * PARAMETERS: mutex_ID - ID of the mutex to be created + * + * RETURN: Status + * + * DESCRIPTION: Create a mutex object. + * + ******************************************************************************/ + +static acpi_status acpi_ut_create_mutex(acpi_mutex_handle mutex_id) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_U32(ut_create_mutex, mutex_id); + + if (!acpi_gbl_mutex_info[mutex_id].mutex) { + status = + acpi_os_create_mutex(&acpi_gbl_mutex_info[mutex_id].mutex); + acpi_gbl_mutex_info[mutex_id].thread_id = + ACPI_MUTEX_NOT_ACQUIRED; + acpi_gbl_mutex_info[mutex_id].use_count = 0; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_mutex + * + * PARAMETERS: mutex_ID - ID of the mutex to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Delete a mutex object. + * + ******************************************************************************/ + +static void acpi_ut_delete_mutex(acpi_mutex_handle mutex_id) +{ + + ACPI_FUNCTION_TRACE_U32(ut_delete_mutex, mutex_id); + + acpi_os_delete_mutex(acpi_gbl_mutex_info[mutex_id].mutex); + + acpi_gbl_mutex_info[mutex_id].mutex = NULL; + acpi_gbl_mutex_info[mutex_id].thread_id = ACPI_MUTEX_NOT_ACQUIRED; + + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_acquire_mutex + * + * PARAMETERS: mutex_ID - ID of the mutex to be acquired + * + * RETURN: Status + * + * DESCRIPTION: Acquire a mutex object. + * + ******************************************************************************/ + +acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) +{ + acpi_status status; + acpi_thread_id this_thread_id; + + ACPI_FUNCTION_NAME(ut_acquire_mutex); + + if (mutex_id > ACPI_MAX_MUTEX) { + return (AE_BAD_PARAMETER); + } + + this_thread_id = acpi_os_get_thread_id(); + +#ifdef ACPI_MUTEX_DEBUG + { + u32 i; + /* + * Mutex debug code, for internal debugging only. + * + * Deadlock prevention. Check if this thread owns any mutexes of value + * greater than or equal to this one. If so, the thread has violated + * the mutex ordering rule. This indicates a coding error somewhere in + * the ACPI subsystem code. + */ + for (i = mutex_id; i < ACPI_NUM_MUTEX; i++) { + if (acpi_gbl_mutex_info[i].thread_id == this_thread_id) { + if (i == mutex_id) { + ACPI_ERROR((AE_INFO, + "Mutex [%s] already acquired by this thread [%u]", + acpi_ut_get_mutex_name + (mutex_id), + (u32)this_thread_id)); + + return (AE_ALREADY_ACQUIRED); + } + + ACPI_ERROR((AE_INFO, + "Invalid acquire order: Thread %u owns [%s], wants [%s]", + (u32)this_thread_id, + acpi_ut_get_mutex_name(i), + acpi_ut_get_mutex_name(mutex_id))); + + return (AE_ACQUIRE_DEADLOCK); + } + } + } +#endif + + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, + "Thread %u attempting to acquire Mutex [%s]\n", + (u32)this_thread_id, + acpi_ut_get_mutex_name(mutex_id))); + + status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex, + ACPI_WAIT_FOREVER); + if (ACPI_SUCCESS(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, + "Thread %u acquired Mutex [%s]\n", + (u32)this_thread_id, + acpi_ut_get_mutex_name(mutex_id))); + + acpi_gbl_mutex_info[mutex_id].use_count++; + acpi_gbl_mutex_info[mutex_id].thread_id = this_thread_id; + } else { + ACPI_EXCEPTION((AE_INFO, status, + "Thread %u could not acquire Mutex [0x%X]", + (u32)this_thread_id, mutex_id)); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_release_mutex + * + * PARAMETERS: mutex_ID - ID of the mutex to be released + * + * RETURN: Status + * + * DESCRIPTION: Release a mutex object. + * + ******************************************************************************/ + +acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) +{ + ACPI_FUNCTION_NAME(ut_release_mutex); + + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %u releasing Mutex [%s]\n", + (u32)acpi_os_get_thread_id(), + acpi_ut_get_mutex_name(mutex_id))); + + if (mutex_id > ACPI_MAX_MUTEX) { + return (AE_BAD_PARAMETER); + } + + /* + * Mutex must be acquired in order to release it! + */ + if (acpi_gbl_mutex_info[mutex_id].thread_id == ACPI_MUTEX_NOT_ACQUIRED) { + ACPI_ERROR((AE_INFO, + "Mutex [0x%X] is not acquired, cannot release", + mutex_id)); + + return (AE_NOT_ACQUIRED); + } +#ifdef ACPI_MUTEX_DEBUG + { + u32 i; + /* + * Mutex debug code, for internal debugging only. + * + * Deadlock prevention. Check if this thread owns any mutexes of value + * greater than this one. If so, the thread has violated the mutex + * ordering rule. This indicates a coding error somewhere in + * the ACPI subsystem code. + */ + for (i = mutex_id; i < ACPI_NUM_MUTEX; i++) { + if (acpi_gbl_mutex_info[i].thread_id == + acpi_os_get_thread_id()) { + if (i == mutex_id) { + continue; + } + + ACPI_ERROR((AE_INFO, + "Invalid release order: owns [%s], releasing [%s]", + acpi_ut_get_mutex_name(i), + acpi_ut_get_mutex_name(mutex_id))); + + return (AE_RELEASE_DEADLOCK); + } + } + } +#endif + + /* Mark unlocked FIRST */ + + acpi_gbl_mutex_info[mutex_id].thread_id = ACPI_MUTEX_NOT_ACQUIRED; + + acpi_os_release_mutex(acpi_gbl_mutex_info[mutex_id].mutex); + return (AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/utobject.c b/kernel/drivers/acpi/acpica/utobject.c new file mode 100644 index 000000000..7d83efe1e --- /dev/null +++ b/kernel/drivers/acpi/acpica/utobject.c @@ -0,0 +1,706 @@ +/****************************************************************************** + * + * Module Name: utobject - ACPI object create/delete/size/cache routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utobject") + +/* Local prototypes */ +static acpi_status +acpi_ut_get_simple_object_size(union acpi_operand_object *obj, + acpi_size * obj_length); + +static acpi_status +acpi_ut_get_package_object_size(union acpi_operand_object *obj, + acpi_size * obj_length); + +static acpi_status +acpi_ut_get_element_length(u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, void *context); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_internal_object_dbg + * + * PARAMETERS: module_name - Source file name of caller + * line_number - Line number of caller + * component_id - Component type of caller + * type - ACPI Type of the new object + * + * RETURN: A new internal object, null on failure + * + * DESCRIPTION: Create and initialize a new internal object. + * + * NOTE: We always allocate the worst-case object descriptor because + * these objects are cached, and we want them to be + * one-size-satisifies-any-request. This in itself may not be + * the most memory efficient, but the efficiency of the object + * cache should more than make up for this! + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char + *module_name, + u32 line_number, + u32 component_id, + acpi_object_type + type) +{ + union acpi_operand_object *object; + union acpi_operand_object *second_object; + + ACPI_FUNCTION_TRACE_STR(ut_create_internal_object_dbg, + acpi_ut_get_type_name(type)); + + /* Allocate the raw object descriptor */ + + object = + acpi_ut_allocate_object_desc_dbg(module_name, line_number, + component_id); + if (!object) { + return_PTR(NULL); + } + + switch (type) { + case ACPI_TYPE_REGION: + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + + /* These types require a secondary object */ + + second_object = acpi_ut_allocate_object_desc_dbg(module_name, + line_number, + component_id); + if (!second_object) { + acpi_ut_delete_object_desc(object); + return_PTR(NULL); + } + + second_object->common.type = ACPI_TYPE_LOCAL_EXTRA; + second_object->common.reference_count = 1; + + /* Link the second object to the first */ + + object->common.next_object = second_object; + break; + + default: + + /* All others have no secondary object */ + break; + } + + /* Save the object type in the object descriptor */ + + object->common.type = (u8) type; + + /* Init the reference count */ + + object->common.reference_count = 1; + + /* Any per-type initialization should go here */ + + return_PTR(object); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_package_object + * + * PARAMETERS: count - Number of package elements + * + * RETURN: Pointer to a new Package object, null on failure + * + * DESCRIPTION: Create a fully initialized package object + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ut_create_package_object(u32 count) +{ + union acpi_operand_object *package_desc; + union acpi_operand_object **package_elements; + + ACPI_FUNCTION_TRACE_U32(ut_create_package_object, count); + + /* Create a new Package object */ + + package_desc = acpi_ut_create_internal_object(ACPI_TYPE_PACKAGE); + if (!package_desc) { + return_PTR(NULL); + } + + /* + * Create the element array. Count+1 allows the array to be null + * terminated. + */ + package_elements = ACPI_ALLOCATE_ZEROED(((acpi_size) count + + 1) * sizeof(void *)); + if (!package_elements) { + ACPI_FREE(package_desc); + return_PTR(NULL); + } + + package_desc->package.count = count; + package_desc->package.elements = package_elements; + return_PTR(package_desc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_integer_object + * + * PARAMETERS: initial_value - Initial value for the integer + * + * RETURN: Pointer to a new Integer object, null on failure + * + * DESCRIPTION: Create an initialized integer object + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ut_create_integer_object(u64 initial_value) +{ + union acpi_operand_object *integer_desc; + + ACPI_FUNCTION_TRACE(ut_create_integer_object); + + /* Create and initialize a new integer object */ + + integer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!integer_desc) { + return_PTR(NULL); + } + + integer_desc->integer.value = initial_value; + return_PTR(integer_desc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_buffer_object + * + * PARAMETERS: buffer_size - Size of buffer to be created + * + * RETURN: Pointer to a new Buffer object, null on failure + * + * DESCRIPTION: Create a fully initialized buffer object + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size) +{ + union acpi_operand_object *buffer_desc; + u8 *buffer = NULL; + + ACPI_FUNCTION_TRACE_U32(ut_create_buffer_object, buffer_size); + + /* Create a new Buffer object */ + + buffer_desc = acpi_ut_create_internal_object(ACPI_TYPE_BUFFER); + if (!buffer_desc) { + return_PTR(NULL); + } + + /* Create an actual buffer only if size > 0 */ + + if (buffer_size > 0) { + + /* Allocate the actual buffer */ + + buffer = ACPI_ALLOCATE_ZEROED(buffer_size); + if (!buffer) { + ACPI_ERROR((AE_INFO, "Could not allocate size %u", + (u32) buffer_size)); + acpi_ut_remove_reference(buffer_desc); + return_PTR(NULL); + } + } + + /* Complete buffer object initialization */ + + buffer_desc->buffer.flags |= AOPOBJ_DATA_VALID; + buffer_desc->buffer.pointer = buffer; + buffer_desc->buffer.length = (u32) buffer_size; + + /* Return the new buffer descriptor */ + + return_PTR(buffer_desc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_string_object + * + * PARAMETERS: string_size - Size of string to be created. Does not + * include NULL terminator, this is added + * automatically. + * + * RETURN: Pointer to a new String object + * + * DESCRIPTION: Create a fully initialized string object + * + ******************************************************************************/ + +union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size) +{ + union acpi_operand_object *string_desc; + char *string; + + ACPI_FUNCTION_TRACE_U32(ut_create_string_object, string_size); + + /* Create a new String object */ + + string_desc = acpi_ut_create_internal_object(ACPI_TYPE_STRING); + if (!string_desc) { + return_PTR(NULL); + } + + /* + * Allocate the actual string buffer -- (Size + 1) for NULL terminator. + * NOTE: Zero-length strings are NULL terminated + */ + string = ACPI_ALLOCATE_ZEROED(string_size + 1); + if (!string) { + ACPI_ERROR((AE_INFO, "Could not allocate size %u", + (u32) string_size)); + acpi_ut_remove_reference(string_desc); + return_PTR(NULL); + } + + /* Complete string object initialization */ + + string_desc->string.pointer = string; + string_desc->string.length = (u32) string_size; + + /* Return the new string descriptor */ + + return_PTR(string_desc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_valid_internal_object + * + * PARAMETERS: object - Object to be validated + * + * RETURN: TRUE if object is valid, FALSE otherwise + * + * DESCRIPTION: Validate a pointer to be of type union acpi_operand_object + * + ******************************************************************************/ + +u8 acpi_ut_valid_internal_object(void *object) +{ + + ACPI_FUNCTION_NAME(ut_valid_internal_object); + + /* Check for a null pointer */ + + if (!object) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "**** Null Object Ptr\n")); + return (FALSE); + } + + /* Check the descriptor type field */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(object)) { + case ACPI_DESC_TYPE_OPERAND: + + /* The object appears to be a valid union acpi_operand_object */ + + return (TRUE); + + default: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "%p is not an ACPI operand obj [%s]\n", + object, acpi_ut_get_descriptor_name(object))); + break; + } + + return (FALSE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_allocate_object_desc_dbg + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * + * RETURN: Pointer to newly allocated object descriptor. Null on error + * + * DESCRIPTION: Allocate a new object descriptor. Gracefully handle + * error conditions. + * + ******************************************************************************/ + +void *acpi_ut_allocate_object_desc_dbg(const char *module_name, + u32 line_number, u32 component_id) +{ + union acpi_operand_object *object; + + ACPI_FUNCTION_TRACE(ut_allocate_object_desc_dbg); + + object = acpi_os_acquire_object(acpi_gbl_operand_cache); + if (!object) { + ACPI_ERROR((module_name, line_number, + "Could not allocate an object descriptor")); + + return_PTR(NULL); + } + + /* Mark the descriptor type */ + + ACPI_SET_DESCRIPTOR_TYPE(object, ACPI_DESC_TYPE_OPERAND); + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p Size %X\n", + object, (u32) sizeof(union acpi_operand_object))); + + return_PTR(object); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_object_desc + * + * PARAMETERS: object - An Acpi internal object to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Free an ACPI object descriptor or add it to the object cache + * + ******************************************************************************/ + +void acpi_ut_delete_object_desc(union acpi_operand_object *object) +{ + ACPI_FUNCTION_TRACE_PTR(ut_delete_object_desc, object); + + /* Object must be of type union acpi_operand_object */ + + if (ACPI_GET_DESCRIPTOR_TYPE(object) != ACPI_DESC_TYPE_OPERAND) { + ACPI_ERROR((AE_INFO, + "%p is not an ACPI Operand object [%s]", object, + acpi_ut_get_descriptor_name(object))); + return_VOID; + } + + (void)acpi_os_release_object(acpi_gbl_operand_cache, object); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_simple_object_size + * + * PARAMETERS: internal_object - An ACPI operand object + * obj_length - Where the length is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to determine the space required to + * contain a simple object for return to an external user. + * + * The length includes the object structure plus any additional + * needed space. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_get_simple_object_size(union acpi_operand_object *internal_object, + acpi_size * obj_length) +{ + acpi_size length; + acpi_size size; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_PTR(ut_get_simple_object_size, internal_object); + + /* Start with the length of the (external) Acpi object */ + + length = sizeof(union acpi_object); + + /* A NULL object is allowed, can be a legal uninitialized package element */ + + if (!internal_object) { + /* + * Object is NULL, just return the length of union acpi_object + * (A NULL union acpi_object is an object of all zeroes.) + */ + *obj_length = ACPI_ROUND_UP_TO_NATIVE_WORD(length); + return_ACPI_STATUS(AE_OK); + } + + /* A Namespace Node should never appear here */ + + if (ACPI_GET_DESCRIPTOR_TYPE(internal_object) == ACPI_DESC_TYPE_NAMED) { + + /* A namespace node should never get here */ + + return_ACPI_STATUS(AE_AML_INTERNAL); + } + + /* + * The final length depends on the object type + * Strings and Buffers are packed right up against the parent object and + * must be accessed bytewise or there may be alignment problems on + * certain processors + */ + switch (internal_object->common.type) { + case ACPI_TYPE_STRING: + + length += (acpi_size) internal_object->string.length + 1; + break; + + case ACPI_TYPE_BUFFER: + + length += (acpi_size) internal_object->buffer.length; + break; + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_POWER: + + /* No extra data for these types */ + + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + switch (internal_object->reference.class) { + case ACPI_REFCLASS_NAME: + /* + * Get the actual length of the full pathname to this object. + * The reference will be converted to the pathname to the object + */ + size = + acpi_ns_get_pathname_length(internal_object-> + reference.node); + if (!size) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + length += ACPI_ROUND_UP_TO_NATIVE_WORD(size); + break; + + default: + /* + * No other reference opcodes are supported. + * Notably, Locals and Args are not supported, but this may be + * required eventually. + */ + ACPI_ERROR((AE_INFO, + "Cannot convert to external object - " + "unsupported Reference Class [%s] 0x%X in object %p", + acpi_ut_get_reference_name(internal_object), + internal_object->reference.class, + internal_object)); + status = AE_TYPE; + break; + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Cannot convert to external object - " + "unsupported type [%s] 0x%X in object %p", + acpi_ut_get_object_type_name(internal_object), + internal_object->common.type, internal_object)); + status = AE_TYPE; + break; + } + + /* + * Account for the space required by the object rounded up to the next + * multiple of the machine word size. This keeps each object aligned + * on a machine word boundary. (preventing alignment faults on some + * machines.) + */ + *obj_length = ACPI_ROUND_UP_TO_NATIVE_WORD(length); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_element_length + * + * PARAMETERS: acpi_pkg_callback + * + * RETURN: Status + * + * DESCRIPTION: Get the length of one package element. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_get_element_length(u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, void *context) +{ + acpi_status status = AE_OK; + struct acpi_pkg_info *info = (struct acpi_pkg_info *)context; + acpi_size object_space; + + switch (object_type) { + case ACPI_COPY_TYPE_SIMPLE: + /* + * Simple object - just get the size (Null object/entry is handled + * here also) and sum it into the running package length + */ + status = + acpi_ut_get_simple_object_size(source_object, + &object_space); + if (ACPI_FAILURE(status)) { + return (status); + } + + info->length += object_space; + break; + + case ACPI_COPY_TYPE_PACKAGE: + + /* Package object - nothing much to do here, let the walk handle it */ + + info->num_packages++; + state->pkg.this_target_obj = NULL; + break; + + default: + + /* No other types allowed */ + + return (AE_BAD_PARAMETER); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_package_object_size + * + * PARAMETERS: internal_object - An ACPI internal object + * obj_length - Where the length is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to determine the space required to + * contain a package object for return to an external user. + * + * This is moderately complex since a package contains other + * objects including packages. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_get_package_object_size(union acpi_operand_object *internal_object, + acpi_size * obj_length) +{ + acpi_status status; + struct acpi_pkg_info info; + + ACPI_FUNCTION_TRACE_PTR(ut_get_package_object_size, internal_object); + + info.length = 0; + info.object_space = 0; + info.num_packages = 1; + + status = acpi_ut_walk_package_tree(internal_object, NULL, + acpi_ut_get_element_length, &info); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * We have handled all of the objects in all levels of the package. + * just add the length of the package objects themselves. + * Round up to the next machine word. + */ + info.length += ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)) * + (acpi_size) info.num_packages; + + /* Return the total package length */ + + *obj_length = info.length; + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_object_size + * + * PARAMETERS: internal_object - An ACPI internal object + * obj_length - Where the length will be returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to determine the space required to + * contain an object for return to an API user. + * + ******************************************************************************/ + +acpi_status +acpi_ut_get_object_size(union acpi_operand_object *internal_object, + acpi_size * obj_length) +{ + acpi_status status; + + ACPI_FUNCTION_ENTRY(); + + if ((ACPI_GET_DESCRIPTOR_TYPE(internal_object) == + ACPI_DESC_TYPE_OPERAND) + && (internal_object->common.type == ACPI_TYPE_PACKAGE)) { + status = + acpi_ut_get_package_object_size(internal_object, + obj_length); + } else { + status = + acpi_ut_get_simple_object_size(internal_object, obj_length); + } + + return (status); +} diff --git a/kernel/drivers/acpi/acpica/utosi.c b/kernel/drivers/acpi/acpica/utosi.c new file mode 100644 index 000000000..44035abdb --- /dev/null +++ b/kernel/drivers/acpi/acpica/utosi.c @@ -0,0 +1,474 @@ +/****************************************************************************** + * + * Module Name: utosi - Support for the _OSI predefined control method + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utosi") + +/****************************************************************************** + * + * ACPICA policy for new _OSI strings: + * + * It is the stated policy of ACPICA that new _OSI strings will be integrated + * into this module as soon as possible after they are defined. It is strongly + * recommended that all ACPICA hosts mirror this policy and integrate any + * changes to this module as soon as possible. There are several historical + * reasons behind this policy: + * + * 1) New BIOSs tend to test only the case where the host responds TRUE to + * the latest version of Windows, which would respond to the latest/newest + * _OSI string. Not responding TRUE to the latest version of Windows will + * risk executing untested code paths throughout the DSDT and SSDTs. + * + * 2) If a new _OSI string is recognized only after a significant delay, this + * has the potential to cause problems on existing working machines because + * of the possibility that a new and different path through the ASL code + * will be executed. + * + * 3) New _OSI strings are tending to come out about once per year. A delay + * in recognizing a new string for a significant amount of time risks the + * release of another string which only compounds the initial problem. + * + *****************************************************************************/ +/* + * Strings supported by the _OSI predefined control method (which is + * implemented internally within this module.) + * + * March 2009: Removed "Linux" as this host no longer wants to respond true + * for this string. Basically, the only safe OS strings are windows-related + * and in many or most cases represent the only test path within the + * BIOS-provided ASL code. + * + * The last element of each entry is used to track the newest version of + * Windows that the BIOS has requested. + */ +static struct acpi_interface_info acpi_default_supported_interfaces[] = { + /* Operating System Vendor Strings */ + + {"Windows 2000", NULL, 0, ACPI_OSI_WIN_2000}, /* Windows 2000 */ + {"Windows 2001", NULL, 0, ACPI_OSI_WIN_XP}, /* Windows XP */ + {"Windows 2001 SP1", NULL, 0, ACPI_OSI_WIN_XP_SP1}, /* Windows XP SP1 */ + {"Windows 2001.1", NULL, 0, ACPI_OSI_WINSRV_2003}, /* Windows Server 2003 */ + {"Windows 2001 SP2", NULL, 0, ACPI_OSI_WIN_XP_SP2}, /* Windows XP SP2 */ + {"Windows 2001.1 SP1", NULL, 0, ACPI_OSI_WINSRV_2003_SP1}, /* Windows Server 2003 SP1 - Added 03/2006 */ + {"Windows 2006", NULL, 0, ACPI_OSI_WIN_VISTA}, /* Windows vista - Added 03/2006 */ + {"Windows 2006.1", NULL, 0, ACPI_OSI_WINSRV_2008}, /* Windows Server 2008 - Added 09/2009 */ + {"Windows 2006 SP1", NULL, 0, ACPI_OSI_WIN_VISTA_SP1}, /* Windows Vista SP1 - Added 09/2009 */ + {"Windows 2006 SP2", NULL, 0, ACPI_OSI_WIN_VISTA_SP2}, /* Windows Vista SP2 - Added 09/2010 */ + {"Windows 2009", NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */ + {"Windows 2012", NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8 and Server 2012 - Added 08/2012 */ + {"Windows 2013", NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8.1 and Server 2012 R2 - Added 01/2014 */ + {"Windows 2015", NULL, 0, ACPI_OSI_WIN_10}, /* Windows 10 - Added 03/2015 */ + + /* Feature Group Strings */ + + {"Extended Address Space Descriptor", NULL, ACPI_OSI_FEATURE, 0}, + + /* + * All "optional" feature group strings (features that are implemented + * by the host) should be dynamically modified to VALID by the host via + * acpi_install_interface or acpi_update_interfaces. Such optional feature + * group strings are set as INVALID by default here. + */ + + {"Module Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"Processor Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"3.0 Thermal Model", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"3.0 _SCP Extensions", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"Processor Aggregator Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0} +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_initialize_interfaces + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize the global _OSI supported interfaces list + * + ******************************************************************************/ + +acpi_status acpi_ut_initialize_interfaces(void) +{ + acpi_status status; + u32 i; + + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + acpi_gbl_supported_interfaces = acpi_default_supported_interfaces; + + /* Link the static list of supported interfaces */ + + for (i = 0; + i < (ACPI_ARRAY_LENGTH(acpi_default_supported_interfaces) - 1); + i++) { + acpi_default_supported_interfaces[i].next = + &acpi_default_supported_interfaces[(acpi_size) i + 1]; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_interface_terminate + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Delete all interfaces in the global list. Sets + * acpi_gbl_supported_interfaces to NULL. + * + ******************************************************************************/ + +acpi_status acpi_ut_interface_terminate(void) +{ + acpi_status status; + struct acpi_interface_info *next_interface; + + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + acpi_gbl_supported_interfaces = next_interface->next; + + if (next_interface->flags & ACPI_OSI_DYNAMIC) { + + /* Only interfaces added at runtime can be freed */ + + ACPI_FREE(next_interface->name); + ACPI_FREE(next_interface); + } else { + /* Interface is in static list. Reset it to invalid or valid. */ + + if (next_interface->flags & ACPI_OSI_DEFAULT_INVALID) { + next_interface->flags |= ACPI_OSI_INVALID; + } else { + next_interface->flags &= ~ACPI_OSI_INVALID; + } + } + + next_interface = acpi_gbl_supported_interfaces; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_install_interface + * + * PARAMETERS: interface_name - The interface to install + * + * RETURN: Status + * + * DESCRIPTION: Install the interface into the global interface list. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +acpi_status acpi_ut_install_interface(acpi_string interface_name) +{ + struct acpi_interface_info *interface_info; + + /* Allocate info block and space for the name string */ + + interface_info = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_interface_info)); + if (!interface_info) { + return (AE_NO_MEMORY); + } + + interface_info->name = + ACPI_ALLOCATE_ZEROED(ACPI_STRLEN(interface_name) + 1); + if (!interface_info->name) { + ACPI_FREE(interface_info); + return (AE_NO_MEMORY); + } + + /* Initialize new info and insert at the head of the global list */ + + ACPI_STRCPY(interface_info->name, interface_name); + interface_info->flags = ACPI_OSI_DYNAMIC; + interface_info->next = acpi_gbl_supported_interfaces; + + acpi_gbl_supported_interfaces = interface_info; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_interface + * + * PARAMETERS: interface_name - The interface to remove + * + * RETURN: Status + * + * DESCRIPTION: Remove the interface from the global interface list. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +acpi_status acpi_ut_remove_interface(acpi_string interface_name) +{ + struct acpi_interface_info *previous_interface; + struct acpi_interface_info *next_interface; + + previous_interface = next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (!ACPI_STRCMP(interface_name, next_interface->name)) { + + /* Found: name is in either the static list or was added at runtime */ + + if (next_interface->flags & ACPI_OSI_DYNAMIC) { + + /* Interface was added dynamically, remove and free it */ + + if (previous_interface == next_interface) { + acpi_gbl_supported_interfaces = + next_interface->next; + } else { + previous_interface->next = + next_interface->next; + } + + ACPI_FREE(next_interface->name); + ACPI_FREE(next_interface); + } else { + /* + * Interface is in static list. If marked invalid, then it + * does not actually exist. Else, mark it invalid. + */ + if (next_interface->flags & ACPI_OSI_INVALID) { + return (AE_NOT_EXIST); + } + + next_interface->flags |= ACPI_OSI_INVALID; + } + + return (AE_OK); + } + + previous_interface = next_interface; + next_interface = next_interface->next; + } + + /* Interface was not found */ + + return (AE_NOT_EXIST); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_update_interfaces + * + * PARAMETERS: action - Actions to be performed during the + * update + * + * RETURN: Status + * + * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor + * strings or/and feature group strings. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +acpi_status acpi_ut_update_interfaces(u8 action) +{ + struct acpi_interface_info *next_interface; + + next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (((next_interface->flags & ACPI_OSI_FEATURE) && + (action & ACPI_FEATURE_STRINGS)) || + (!(next_interface->flags & ACPI_OSI_FEATURE) && + (action & ACPI_VENDOR_STRINGS))) { + if (action & ACPI_DISABLE_INTERFACES) { + + /* Mark the interfaces as invalid */ + + next_interface->flags |= ACPI_OSI_INVALID; + } else { + /* Mark the interfaces as valid */ + + next_interface->flags &= ~ACPI_OSI_INVALID; + } + } + + next_interface = next_interface->next; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_interface + * + * PARAMETERS: interface_name - The interface to find + * + * RETURN: struct acpi_interface_info if found. NULL if not found. + * + * DESCRIPTION: Search for the specified interface name in the global list. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name) +{ + struct acpi_interface_info *next_interface; + + next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (!ACPI_STRCMP(interface_name, next_interface->name)) { + return (next_interface); + } + + next_interface = next_interface->next; + } + + return (NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_osi_implementation + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Implementation of the _OSI predefined control method. When + * an invocation of _OSI is encountered in the system AML, + * control is transferred to this function. + * + ******************************************************************************/ + +acpi_status acpi_ut_osi_implementation(struct acpi_walk_state * walk_state) +{ + union acpi_operand_object *string_desc; + union acpi_operand_object *return_desc; + struct acpi_interface_info *interface_info; + acpi_interface_handler interface_handler; + acpi_status status; + u32 return_value; + + ACPI_FUNCTION_TRACE(ut_osi_implementation); + + /* Validate the string input argument (from the AML caller) */ + + string_desc = walk_state->arguments[0].object; + if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) { + return_ACPI_STATUS(AE_TYPE); + } + + /* Create a return object */ + + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + + /* Default return value is 0, NOT SUPPORTED */ + + return_value = 0; + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + return_ACPI_STATUS(status); + } + + /* Lookup the interface in the global _OSI list */ + + interface_info = acpi_ut_get_interface(string_desc->string.pointer); + if (interface_info && !(interface_info->flags & ACPI_OSI_INVALID)) { + /* + * The interface is supported. + * Update the osi_data if necessary. We keep track of the latest + * version of Windows that has been requested by the BIOS. + */ + if (interface_info->value > acpi_gbl_osi_data) { + acpi_gbl_osi_data = interface_info->value; + } + + return_value = ACPI_UINT32_MAX; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + + /* + * Invoke an optional _OSI interface handler. The host OS may wish + * to do some interface-specific handling. For example, warn about + * certain interfaces or override the true/false support value. + */ + interface_handler = acpi_gbl_interface_handler; + if (interface_handler) { + return_value = + interface_handler(string_desc->string.pointer, + return_value); + } + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, + "ACPI: BIOS _OSI(\"%s\") is %ssupported\n", + string_desc->string.pointer, + return_value == 0 ? "not " : "")); + + /* Complete the return object */ + + return_desc->integer.value = return_value; + walk_state->return_desc = return_desc; + return_ACPI_STATUS(AE_OK); +} diff --git a/kernel/drivers/acpi/acpica/utownerid.c b/kernel/drivers/acpi/acpica/utownerid.c new file mode 100644 index 000000000..295921706 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utownerid.c @@ -0,0 +1,218 @@ +/******************************************************************************* + * + * Module Name: utownerid - Support for Table/Method Owner IDs + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utownerid") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_allocate_owner_id + * + * PARAMETERS: owner_id - Where the new owner ID is returned + * + * RETURN: Status + * + * DESCRIPTION: Allocate a table or method owner ID. The owner ID is used to + * track objects created by the table or method, to be deleted + * when the method exits or the table is unloaded. + * + ******************************************************************************/ +acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id) +{ + u32 i; + u32 j; + u32 k; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_allocate_owner_id); + + /* Guard against multiple allocations of ID to the same location */ + + if (*owner_id) { + ACPI_ERROR((AE_INFO, "Owner ID [0x%2.2X] already exists", + *owner_id)); + return_ACPI_STATUS(AE_ALREADY_EXISTS); + } + + /* Mutex for the global ID mask */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Find a free owner ID, cycle through all possible IDs on repeated + * allocations. (ACPI_NUM_OWNERID_MASKS + 1) because first index may have + * to be scanned twice. + */ + for (i = 0, j = acpi_gbl_last_owner_id_index; + i < (ACPI_NUM_OWNERID_MASKS + 1); i++, j++) { + if (j >= ACPI_NUM_OWNERID_MASKS) { + j = 0; /* Wraparound to start of mask array */ + } + + for (k = acpi_gbl_next_owner_id_offset; k < 32; k++) { + if (acpi_gbl_owner_id_mask[j] == ACPI_UINT32_MAX) { + + /* There are no free IDs in this mask */ + + break; + } + + if (!(acpi_gbl_owner_id_mask[j] & (1 << k))) { + /* + * Found a free ID. The actual ID is the bit index plus one, + * making zero an invalid Owner ID. Save this as the last ID + * allocated and update the global ID mask. + */ + acpi_gbl_owner_id_mask[j] |= (1 << k); + + acpi_gbl_last_owner_id_index = (u8)j; + acpi_gbl_next_owner_id_offset = (u8)(k + 1); + + /* + * Construct encoded ID from the index and bit position + * + * Note: Last [j].k (bit 255) is never used and is marked + * permanently allocated (prevents +1 overflow) + */ + *owner_id = + (acpi_owner_id) ((k + 1) + ACPI_MUL_32(j)); + + ACPI_DEBUG_PRINT((ACPI_DB_VALUES, + "Allocated OwnerId: %2.2X\n", + (unsigned int)*owner_id)); + goto exit; + } + } + + acpi_gbl_next_owner_id_offset = 0; + } + + /* + * All owner_ids have been allocated. This typically should + * not happen since the IDs are reused after deallocation. The IDs are + * allocated upon table load (one per table) and method execution, and + * they are released when a table is unloaded or a method completes + * execution. + * + * If this error happens, there may be very deep nesting of invoked control + * methods, or there may be a bug where the IDs are not released. + */ + status = AE_OWNER_ID_LIMIT; + ACPI_ERROR((AE_INFO, + "Could not allocate new OwnerId (255 max), AE_OWNER_ID_LIMIT")); + +exit: + (void)acpi_ut_release_mutex(ACPI_MTX_CACHES); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_release_owner_id + * + * PARAMETERS: owner_id_ptr - Pointer to a previously allocated owner_ID + * + * RETURN: None. No error is returned because we are either exiting a + * control method or unloading a table. Either way, we would + * ignore any error anyway. + * + * DESCRIPTION: Release a table or method owner ID. Valid IDs are 1 - 255 + * + ******************************************************************************/ + +void acpi_ut_release_owner_id(acpi_owner_id * owner_id_ptr) +{ + acpi_owner_id owner_id = *owner_id_ptr; + acpi_status status; + u32 index; + u32 bit; + + ACPI_FUNCTION_TRACE_U32(ut_release_owner_id, owner_id); + + /* Always clear the input owner_id (zero is an invalid ID) */ + + *owner_id_ptr = 0; + + /* Zero is not a valid owner_ID */ + + if (owner_id == 0) { + ACPI_ERROR((AE_INFO, "Invalid OwnerId: 0x%2.2X", owner_id)); + return_VOID; + } + + /* Mutex for the global ID mask */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES); + if (ACPI_FAILURE(status)) { + return_VOID; + } + + /* Normalize the ID to zero */ + + owner_id--; + + /* Decode ID to index/offset pair */ + + index = ACPI_DIV_32(owner_id); + bit = 1 << ACPI_MOD_32(owner_id); + + /* Free the owner ID only if it is valid */ + + if (acpi_gbl_owner_id_mask[index] & bit) { + acpi_gbl_owner_id_mask[index] ^= bit; + } else { + ACPI_ERROR((AE_INFO, + "Release of non-allocated OwnerId: 0x%2.2X", + owner_id + 1)); + } + + (void)acpi_ut_release_mutex(ACPI_MTX_CACHES); + return_VOID; +} diff --git a/kernel/drivers/acpi/acpica/utpredef.c b/kernel/drivers/acpi/acpica/utpredef.c new file mode 100644 index 000000000..29e449935 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utpredef.c @@ -0,0 +1,399 @@ +/****************************************************************************** + * + * Module Name: utpredef - support functions for predefined names + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utpredef") + +/* + * Names for the types that can be returned by the predefined objects. + * Used for warning messages. Must be in the same order as the ACPI_RTYPEs + */ +static const char *ut_rtype_names[] = { + "/Integer", + "/String", + "/Buffer", + "/Package", + "/Reference", +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_next_predefined_method + * + * PARAMETERS: this_name - Entry in the predefined method/name table + * + * RETURN: Pointer to next entry in predefined table. + * + * DESCRIPTION: Get the next entry in the predefine method table. Handles the + * cases where a package info entry follows a method name that + * returns a package. + * + ******************************************************************************/ + +const union acpi_predefined_info *acpi_ut_get_next_predefined_method(const union + acpi_predefined_info + *this_name) +{ + + /* + * Skip next entry in the table if this name returns a Package + * (next entry contains the package info) + */ + if ((this_name->info.expected_btypes & ACPI_RTYPE_PACKAGE) && + (this_name->info.expected_btypes != ACPI_RTYPE_ALL)) { + this_name++; + } + + this_name++; + return (this_name); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_match_predefined_method + * + * PARAMETERS: name - Name to find + * + * RETURN: Pointer to entry in predefined table. NULL indicates not found. + * + * DESCRIPTION: Check an object name against the predefined object list. + * + ******************************************************************************/ + +const union acpi_predefined_info *acpi_ut_match_predefined_method(char *name) +{ + const union acpi_predefined_info *this_name; + + /* Quick check for a predefined name, first character must be underscore */ + + if (name[0] != '_') { + return (NULL); + } + + /* Search info table for a predefined method/object name */ + + this_name = acpi_gbl_predefined_methods; + while (this_name->info.name[0]) { + if (ACPI_COMPARE_NAME(name, this_name->info.name)) { + return (this_name); + } + + this_name = acpi_ut_get_next_predefined_method(this_name); + } + + return (NULL); /* Not found */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_expected_return_types + * + * PARAMETERS: buffer - Where the formatted string is returned + * expected_Btypes - Bitfield of expected data types + * + * RETURN: Formatted string in Buffer. + * + * DESCRIPTION: Format the expected object types into a printable string. + * + ******************************************************************************/ + +void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes) +{ + u32 this_rtype; + u32 i; + u32 j; + + if (!expected_btypes) { + ACPI_STRCPY(buffer, "NONE"); + return; + } + + j = 1; + buffer[0] = 0; + this_rtype = ACPI_RTYPE_INTEGER; + + for (i = 0; i < ACPI_NUM_RTYPES; i++) { + + /* If one of the expected types, concatenate the name of this type */ + + if (expected_btypes & this_rtype) { + ACPI_STRCAT(buffer, &ut_rtype_names[i][j]); + j = 0; /* Use name separator from now on */ + } + + this_rtype <<= 1; /* Next Rtype */ + } +} + +/******************************************************************************* + * + * The remaining functions are used by iASL and acpi_help only + * + ******************************************************************************/ + +#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP) +#include +#include + +/* Local prototypes */ + +static u32 acpi_ut_get_argument_types(char *buffer, u16 argument_types); + +/* Types that can be returned externally by a predefined name */ + +static const char *ut_external_type_names[] = /* Indexed by ACPI_TYPE_* */ +{ + ", UNSUPPORTED-TYPE", + ", Integer", + ", String", + ", Buffer", + ", Package" +}; + +/* Bit widths for resource descriptor predefined names */ + +static const char *ut_resource_type_names[] = { + "/1", + "/2", + "/3", + "/8", + "/16", + "/32", + "/64", + "/variable", +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_match_resource_name + * + * PARAMETERS: name - Name to find + * + * RETURN: Pointer to entry in the resource table. NULL indicates not + * found. + * + * DESCRIPTION: Check an object name against the predefined resource + * descriptor object list. + * + ******************************************************************************/ + +const union acpi_predefined_info *acpi_ut_match_resource_name(char *name) +{ + const union acpi_predefined_info *this_name; + + /* Quick check for a predefined name, first character must be underscore */ + + if (name[0] != '_') { + return (NULL); + } + + /* Search info table for a predefined method/object name */ + + this_name = acpi_gbl_resource_names; + while (this_name->info.name[0]) { + if (ACPI_COMPARE_NAME(name, this_name->info.name)) { + return (this_name); + } + + this_name++; + } + + return (NULL); /* Not found */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_display_predefined_method + * + * PARAMETERS: buffer - Scratch buffer for this function + * this_name - Entry in the predefined method/name table + * multi_line - TRUE if output should be on >1 line + * + * RETURN: None + * + * DESCRIPTION: Display information about a predefined method. Number and + * type of the input arguments, and expected type(s) for the + * return value, if any. + * + ******************************************************************************/ + +void +acpi_ut_display_predefined_method(char *buffer, + const union acpi_predefined_info *this_name, + u8 multi_line) +{ + u32 arg_count; + + /* + * Get the argument count and the string buffer + * containing all argument types + */ + arg_count = acpi_ut_get_argument_types(buffer, + this_name->info.argument_list); + + if (multi_line) { + printf(" "); + } + + printf("%4.4s Requires %s%u argument%s", + this_name->info.name, + (this_name->info.argument_list & ARG_COUNT_IS_MINIMUM) ? + "(at least) " : "", arg_count, arg_count != 1 ? "s" : ""); + + /* Display the types for any arguments */ + + if (arg_count > 0) { + printf(" (%s)", buffer); + } + + if (multi_line) { + printf("\n "); + } + + /* Get the return value type(s) allowed */ + + if (this_name->info.expected_btypes) { + acpi_ut_get_expected_return_types(buffer, + this_name->info. + expected_btypes); + printf(" Return value types: %s\n", buffer); + } else { + printf(" No return value\n"); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_argument_types + * + * PARAMETERS: buffer - Where to return the formatted types + * argument_types - Types field for this method + * + * RETURN: count - the number of arguments required for this method + * + * DESCRIPTION: Format the required data types for this method (Integer, + * String, Buffer, or Package) and return the required argument + * count. + * + ******************************************************************************/ + +static u32 acpi_ut_get_argument_types(char *buffer, u16 argument_types) +{ + u16 this_argument_type; + u16 sub_index; + u16 arg_count; + u32 i; + + *buffer = 0; + sub_index = 2; + + /* First field in the types list is the count of args to follow */ + + arg_count = METHOD_GET_ARG_COUNT(argument_types); + if (arg_count > METHOD_PREDEF_ARGS_MAX) { + printf("**** Invalid argument count (%u) " + "in predefined info structure\n", arg_count); + return (arg_count); + } + + /* Get each argument from the list, convert to ascii, store to buffer */ + + for (i = 0; i < arg_count; i++) { + this_argument_type = METHOD_GET_NEXT_TYPE(argument_types); + + if (!this_argument_type + || (this_argument_type > METHOD_MAX_ARG_TYPE)) { + printf("**** Invalid argument type (%u) " + "in predefined info structure\n", + this_argument_type); + return (arg_count); + } + + strcat(buffer, + ut_external_type_names[this_argument_type] + sub_index); + sub_index = 0; + } + + return (arg_count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_resource_bit_width + * + * PARAMETERS: buffer - Where the formatted string is returned + * types - Bitfield of expected data types + * + * RETURN: Count of return types. Formatted string in Buffer. + * + * DESCRIPTION: Format the resource bit widths into a printable string. + * + ******************************************************************************/ + +u32 acpi_ut_get_resource_bit_width(char *buffer, u16 types) +{ + u32 i; + u16 sub_index; + u32 found; + + *buffer = 0; + sub_index = 1; + found = 0; + + for (i = 0; i < NUM_RESOURCE_WIDTHS; i++) { + if (types & 1) { + strcat(buffer, &(ut_resource_type_names[i][sub_index])); + sub_index = 0; + found++; + } + + types >>= 1; + } + + return (found); +} +#endif diff --git a/kernel/drivers/acpi/acpica/utprint.c b/kernel/drivers/acpi/acpica/utprint.c new file mode 100644 index 000000000..2be6bd4bd --- /dev/null +++ b/kernel/drivers/acpi/acpica/utprint.c @@ -0,0 +1,667 @@ +/****************************************************************************** + * + * Module Name: utprint - Formatted printing routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utprint") + +#define ACPI_FORMAT_SIGN 0x01 +#define ACPI_FORMAT_SIGN_PLUS 0x02 +#define ACPI_FORMAT_SIGN_PLUS_SPACE 0x04 +#define ACPI_FORMAT_ZERO 0x08 +#define ACPI_FORMAT_LEFT 0x10 +#define ACPI_FORMAT_UPPER 0x20 +#define ACPI_FORMAT_PREFIX 0x40 +/* Local prototypes */ +static acpi_size +acpi_ut_bound_string_length(const char *string, acpi_size count); + +static char *acpi_ut_bound_string_output(char *string, const char *end, char c); + +static char *acpi_ut_format_number(char *string, + char *end, + u64 number, + u8 base, s32 width, s32 precision, u8 type); + +static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper); + +/* Module globals */ + +static const char acpi_gbl_lower_hex_digits[] = "0123456789abcdef"; +static const char acpi_gbl_upper_hex_digits[] = "0123456789ABCDEF"; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_bound_string_length + * + * PARAMETERS: string - String with boundary + * count - Boundary of the string + * + * RETURN: Length of the string. Less than or equal to Count. + * + * DESCRIPTION: Calculate the length of a string with boundary. + * + ******************************************************************************/ + +static acpi_size +acpi_ut_bound_string_length(const char *string, acpi_size count) +{ + u32 length = 0; + + while (*string && count) { + length++; + string++; + count--; + } + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_bound_string_output + * + * PARAMETERS: string - String with boundary + * end - Boundary of the string + * c - Character to be output to the string + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Output a character into a string with boundary check. + * + ******************************************************************************/ + +static char *acpi_ut_bound_string_output(char *string, const char *end, char c) +{ + + if (string < end) { + *string = c; + } + + ++string; + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_put_number + * + * PARAMETERS: string - Buffer to hold reverse-ordered string + * number - Integer to be converted + * base - Base of the integer + * upper - Whether or not using upper cased digits + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Convert an integer into a string, note that, the string holds a + * reversed ordered number without the trailing zero. + * + ******************************************************************************/ + +static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper) +{ + const char *digits; + u64 digit_index; + char *pos; + + pos = string; + digits = upper ? acpi_gbl_upper_hex_digits : acpi_gbl_lower_hex_digits; + + if (number == 0) { + *(pos++) = '0'; + } else { + while (number) { + (void)acpi_ut_divide(number, base, &number, + &digit_index); + *(pos++) = digits[digit_index]; + } + } + + /* *(Pos++) = '0'; */ + return (pos); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_scan_number + * + * PARAMETERS: string - String buffer + * number_ptr - Where the number is returned + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Scan a string for a decimal integer. + * + ******************************************************************************/ + +const char *acpi_ut_scan_number(const char *string, u64 *number_ptr) +{ + u64 number = 0; + + while (ACPI_IS_DIGIT(*string)) { + number *= 10; + number += *(string++) - '0'; + } + + *number_ptr = number; + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_print_number + * + * PARAMETERS: string - String buffer + * number - The number to be converted + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Print a decimal integer into a string. + * + ******************************************************************************/ + +const char *acpi_ut_print_number(char *string, u64 number) +{ + char ascii_string[20]; + const char *pos1; + char *pos2; + + pos1 = acpi_ut_put_number(ascii_string, number, 10, FALSE); + pos2 = string; + + while (pos1 != ascii_string) { + *(pos2++) = *(--pos1); + } + + *pos2 = 0; + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_format_number + * + * PARAMETERS: string - String buffer with boundary + * end - Boundary of the string + * number - The number to be converted + * base - Base of the integer + * width - Field width + * precision - Precision of the integer + * type - Special printing flags + * + * RETURN: Updated position for next valid character + * + * DESCRIPTION: Print an integer into a string with any base and any precision. + * + ******************************************************************************/ + +static char *acpi_ut_format_number(char *string, + char *end, + u64 number, + u8 base, s32 width, s32 precision, u8 type) +{ + char *pos; + char sign; + char zero; + u8 need_prefix; + u8 upper; + s32 i; + char reversed_string[66]; + + /* Parameter validation */ + + if (base < 2 || base > 16) { + return (NULL); + } + + if (type & ACPI_FORMAT_LEFT) { + type &= ~ACPI_FORMAT_ZERO; + } + + need_prefix = ((type & ACPI_FORMAT_PREFIX) + && base != 10) ? TRUE : FALSE; + upper = (type & ACPI_FORMAT_UPPER) ? TRUE : FALSE; + zero = (type & ACPI_FORMAT_ZERO) ? '0' : ' '; + + /* Calculate size according to sign and prefix */ + + sign = '\0'; + if (type & ACPI_FORMAT_SIGN) { + if ((s64) number < 0) { + sign = '-'; + number = -(s64) number; + width--; + } else if (type & ACPI_FORMAT_SIGN_PLUS) { + sign = '+'; + width--; + } else if (type & ACPI_FORMAT_SIGN_PLUS_SPACE) { + sign = ' '; + width--; + } + } + if (need_prefix) { + width--; + if (base == 16) { + width--; + } + } + + /* Generate full string in reverse order */ + + pos = acpi_ut_put_number(reversed_string, number, base, upper); + i = ACPI_PTR_DIFF(pos, reversed_string); + + /* Printing 100 using %2d gives "100", not "00" */ + + if (i > precision) { + precision = i; + } + + width -= precision; + + /* Output the string */ + + if (!(type & (ACPI_FORMAT_ZERO | ACPI_FORMAT_LEFT))) { + while (--width >= 0) { + string = acpi_ut_bound_string_output(string, end, ' '); + } + } + if (sign) { + string = acpi_ut_bound_string_output(string, end, sign); + } + if (need_prefix) { + string = acpi_ut_bound_string_output(string, end, '0'); + if (base == 16) { + string = acpi_ut_bound_string_output(string, end, + upper ? 'X' : 'x'); + } + } + if (!(type & ACPI_FORMAT_LEFT)) { + while (--width >= 0) { + string = acpi_ut_bound_string_output(string, end, zero); + } + } + + while (i <= --precision) { + string = acpi_ut_bound_string_output(string, end, '0'); + } + while (--i >= 0) { + string = acpi_ut_bound_string_output(string, end, + reversed_string[i]); + } + while (--width >= 0) { + string = acpi_ut_bound_string_output(string, end, ' '); + } + + return (string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_vsnprintf + * + * PARAMETERS: string - String with boundary + * size - Boundary of the string + * format - Standard printf format + * args - Argument list + * + * RETURN: Number of bytes actually written. + * + * DESCRIPTION: Formatted output to a string using argument list pointer. + * + ******************************************************************************/ + +int +acpi_ut_vsnprintf(char *string, + acpi_size size, const char *format, va_list args) +{ + u8 base; + u8 type; + s32 width; + s32 precision; + char qualifier; + u64 number; + char *pos; + char *end; + char c; + const char *s; + const void *p; + s32 length; + int i; + + pos = string; + end = string + size; + + for (; *format; ++format) { + if (*format != '%') { + pos = acpi_ut_bound_string_output(pos, end, *format); + continue; + } + + type = 0; + base = 10; + + /* Process sign */ + + do { + ++format; + if (*format == '#') { + type |= ACPI_FORMAT_PREFIX; + } else if (*format == '0') { + type |= ACPI_FORMAT_ZERO; + } else if (*format == '+') { + type |= ACPI_FORMAT_SIGN_PLUS; + } else if (*format == ' ') { + type |= ACPI_FORMAT_SIGN_PLUS_SPACE; + } else if (*format == '-') { + type |= ACPI_FORMAT_LEFT; + } else { + break; + } + } while (1); + + /* Process width */ + + width = -1; + if (ACPI_IS_DIGIT(*format)) { + format = acpi_ut_scan_number(format, &number); + width = (s32) number; + } else if (*format == '*') { + ++format; + width = va_arg(args, int); + if (width < 0) { + width = -width; + type |= ACPI_FORMAT_LEFT; + } + } + + /* Process precision */ + + precision = -1; + if (*format == '.') { + ++format; + if (ACPI_IS_DIGIT(*format)) { + format = acpi_ut_scan_number(format, &number); + precision = (s32) number; + } else if (*format == '*') { + ++format; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + /* Process qualifier */ + + qualifier = -1; + if (*format == 'h' || *format == 'l' || *format == 'L') { + qualifier = *format; + ++format; + + if (qualifier == 'l' && *format == 'l') { + qualifier = 'L'; + ++format; + } + } + + switch (*format) { + case '%': + + pos = acpi_ut_bound_string_output(pos, end, '%'); + continue; + + case 'c': + + if (!(type & ACPI_FORMAT_LEFT)) { + while (--width > 0) { + pos = + acpi_ut_bound_string_output(pos, + end, + ' '); + } + } + + c = (char)va_arg(args, int); + pos = acpi_ut_bound_string_output(pos, end, c); + + while (--width > 0) { + pos = + acpi_ut_bound_string_output(pos, end, ' '); + } + continue; + + case 's': + + s = va_arg(args, char *); + if (!s) { + s = ""; + } + length = acpi_ut_bound_string_length(s, precision); + if (!(type & ACPI_FORMAT_LEFT)) { + while (length < width--) { + pos = + acpi_ut_bound_string_output(pos, + end, + ' '); + } + } + for (i = 0; i < length; ++i) { + pos = acpi_ut_bound_string_output(pos, end, *s); + ++s; + } + while (length < width--) { + pos = + acpi_ut_bound_string_output(pos, end, ' '); + } + continue; + + case 'o': + + base = 8; + break; + + case 'X': + + type |= ACPI_FORMAT_UPPER; + + case 'x': + + base = 16; + break; + + case 'd': + case 'i': + + type |= ACPI_FORMAT_SIGN; + + case 'u': + + break; + + case 'p': + + if (width == -1) { + width = 2 * sizeof(void *); + type |= ACPI_FORMAT_ZERO; + } + + p = va_arg(args, void *); + pos = acpi_ut_format_number(pos, end, + ACPI_TO_INTEGER(p), 16, + width, precision, type); + continue; + + default: + + pos = acpi_ut_bound_string_output(pos, end, '%'); + if (*format) { + pos = + acpi_ut_bound_string_output(pos, end, + *format); + } else { + --format; + } + continue; + } + + if (qualifier == 'L') { + number = va_arg(args, u64); + if (type & ACPI_FORMAT_SIGN) { + number = (s64) number; + } + } else if (qualifier == 'l') { + number = va_arg(args, unsigned long); + if (type & ACPI_FORMAT_SIGN) { + number = (s32) number; + } + } else if (qualifier == 'h') { + number = (u16)va_arg(args, int); + if (type & ACPI_FORMAT_SIGN) { + number = (s16) number; + } + } else { + number = va_arg(args, unsigned int); + if (type & ACPI_FORMAT_SIGN) { + number = (signed int)number; + } + } + + pos = acpi_ut_format_number(pos, end, number, base, + width, precision, type); + } + + if (size > 0) { + if (pos < end) { + *pos = '\0'; + } else { + end[-1] = '\0'; + } + } + + return (ACPI_PTR_DIFF(pos, string)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_snprintf + * + * PARAMETERS: string - String with boundary + * size - Boundary of the string + * Format, ... - Standard printf format + * + * RETURN: Number of bytes actually written. + * + * DESCRIPTION: Formatted output to a string. + * + ******************************************************************************/ + +int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...) +{ + va_list args; + int length; + + va_start(args, format); + length = acpi_ut_vsnprintf(string, size, format, args); + va_end(args); + + return (length); +} + +#ifdef ACPI_APPLICATION +/******************************************************************************* + * + * FUNCTION: acpi_ut_file_vprintf + * + * PARAMETERS: file - File descriptor + * format - Standard printf format + * args - Argument list + * + * RETURN: Number of bytes actually written. + * + * DESCRIPTION: Formatted output to a file using argument list pointer. + * + ******************************************************************************/ + +int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args) +{ + acpi_cpu_flags flags; + int length; + + flags = acpi_os_acquire_lock(acpi_gbl_print_lock); + length = acpi_ut_vsnprintf(acpi_gbl_print_buffer, + sizeof(acpi_gbl_print_buffer), format, args); + + (void)acpi_os_write_file(file, acpi_gbl_print_buffer, length, 1); + acpi_os_release_lock(acpi_gbl_print_lock, flags); + + return (length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_file_printf + * + * PARAMETERS: file - File descriptor + * Format, ... - Standard printf format + * + * RETURN: Number of bytes actually written. + * + * DESCRIPTION: Formatted output to a file. + * + ******************************************************************************/ + +int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...) +{ + va_list args; + int length; + + va_start(args, format); + length = acpi_ut_file_vprintf(file, format, args); + va_end(args); + + return (length); +} +#endif diff --git a/kernel/drivers/acpi/acpica/utresrc.c b/kernel/drivers/acpi/acpica/utresrc.c new file mode 100644 index 000000000..b3505dbc7 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utresrc.c @@ -0,0 +1,830 @@ +/******************************************************************************* + * + * Module Name: utresrc - Resource management utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acresrc.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utresrc") + +#if defined(ACPI_DEBUG_OUTPUT) || defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) +/* + * Strings used to decode resource descriptors. + * Used by both the disassembler and the debugger resource dump routines + */ +const char *acpi_gbl_bm_decode[] = { + "NotBusMaster", + "BusMaster" +}; + +const char *acpi_gbl_config_decode[] = { + "0 - Good Configuration", + "1 - Acceptable Configuration", + "2 - Suboptimal Configuration", + "3 - ***Invalid Configuration***", +}; + +const char *acpi_gbl_consume_decode[] = { + "ResourceProducer", + "ResourceConsumer" +}; + +const char *acpi_gbl_dec_decode[] = { + "PosDecode", + "SubDecode" +}; + +const char *acpi_gbl_he_decode[] = { + "Level", + "Edge" +}; + +const char *acpi_gbl_io_decode[] = { + "Decode10", + "Decode16" +}; + +const char *acpi_gbl_ll_decode[] = { + "ActiveHigh", + "ActiveLow", + "ActiveBoth", + "Reserved" +}; + +const char *acpi_gbl_max_decode[] = { + "MaxNotFixed", + "MaxFixed" +}; + +const char *acpi_gbl_mem_decode[] = { + "NonCacheable", + "Cacheable", + "WriteCombining", + "Prefetchable" +}; + +const char *acpi_gbl_min_decode[] = { + "MinNotFixed", + "MinFixed" +}; + +const char *acpi_gbl_mtp_decode[] = { + "AddressRangeMemory", + "AddressRangeReserved", + "AddressRangeACPI", + "AddressRangeNVS" +}; + +const char *acpi_gbl_rng_decode[] = { + "InvalidRanges", + "NonISAOnlyRanges", + "ISAOnlyRanges", + "EntireRange" +}; + +const char *acpi_gbl_rw_decode[] = { + "ReadOnly", + "ReadWrite" +}; + +const char *acpi_gbl_shr_decode[] = { + "Exclusive", + "Shared", + "ExclusiveAndWake", /* ACPI 5.0 */ + "SharedAndWake" /* ACPI 5.0 */ +}; + +const char *acpi_gbl_siz_decode[] = { + "Transfer8", + "Transfer8_16", + "Transfer16", + "InvalidSize" +}; + +const char *acpi_gbl_trs_decode[] = { + "DenseTranslation", + "SparseTranslation" +}; + +const char *acpi_gbl_ttp_decode[] = { + "TypeStatic", + "TypeTranslation" +}; + +const char *acpi_gbl_typ_decode[] = { + "Compatibility", + "TypeA", + "TypeB", + "TypeF" +}; + +const char *acpi_gbl_ppc_decode[] = { + "PullDefault", + "PullUp", + "PullDown", + "PullNone" +}; + +const char *acpi_gbl_ior_decode[] = { + "IoRestrictionNone", + "IoRestrictionInputOnly", + "IoRestrictionOutputOnly", + "IoRestrictionNoneAndPreserve" +}; + +const char *acpi_gbl_dts_decode[] = { + "Width8bit", + "Width16bit", + "Width32bit", + "Width64bit", + "Width128bit", + "Width256bit", +}; + +/* GPIO connection type */ + +const char *acpi_gbl_ct_decode[] = { + "Interrupt", + "I/O" +}; + +/* Serial bus type */ + +const char *acpi_gbl_sbt_decode[] = { + "/* UNKNOWN serial bus type */", + "I2C", + "SPI", + "UART" +}; + +/* I2C serial bus access mode */ + +const char *acpi_gbl_am_decode[] = { + "AddressingMode7Bit", + "AddressingMode10Bit" +}; + +/* I2C serial bus slave mode */ + +const char *acpi_gbl_sm_decode[] = { + "ControllerInitiated", + "DeviceInitiated" +}; + +/* SPI serial bus wire mode */ + +const char *acpi_gbl_wm_decode[] = { + "FourWireMode", + "ThreeWireMode" +}; + +/* SPI serial clock phase */ + +const char *acpi_gbl_cph_decode[] = { + "ClockPhaseFirst", + "ClockPhaseSecond" +}; + +/* SPI serial bus clock polarity */ + +const char *acpi_gbl_cpo_decode[] = { + "ClockPolarityLow", + "ClockPolarityHigh" +}; + +/* SPI serial bus device polarity */ + +const char *acpi_gbl_dp_decode[] = { + "PolarityLow", + "PolarityHigh" +}; + +/* UART serial bus endian */ + +const char *acpi_gbl_ed_decode[] = { + "LittleEndian", + "BigEndian" +}; + +/* UART serial bus bits per byte */ + +const char *acpi_gbl_bpb_decode[] = { + "DataBitsFive", + "DataBitsSix", + "DataBitsSeven", + "DataBitsEight", + "DataBitsNine", + "/* UNKNOWN Bits per byte */", + "/* UNKNOWN Bits per byte */", + "/* UNKNOWN Bits per byte */" +}; + +/* UART serial bus stop bits */ + +const char *acpi_gbl_sb_decode[] = { + "StopBitsZero", + "StopBitsOne", + "StopBitsOnePlusHalf", + "StopBitsTwo" +}; + +/* UART serial bus flow control */ + +const char *acpi_gbl_fc_decode[] = { + "FlowControlNone", + "FlowControlHardware", + "FlowControlXON", + "/* UNKNOWN flow control keyword */" +}; + +/* UART serial bus parity type */ + +const char *acpi_gbl_pt_decode[] = { + "ParityTypeNone", + "ParityTypeEven", + "ParityTypeOdd", + "ParityTypeMark", + "ParityTypeSpace", + "/* UNKNOWN parity keyword */", + "/* UNKNOWN parity keyword */", + "/* UNKNOWN parity keyword */" +}; + +#endif + +/* + * Base sizes of the raw AML resource descriptors, indexed by resource type. + * Zero indicates a reserved (and therefore invalid) resource type. + */ +const u8 acpi_gbl_resource_aml_sizes[] = { + /* Small descriptors */ + + 0, + 0, + 0, + 0, + ACPI_AML_SIZE_SMALL(struct aml_resource_irq), + ACPI_AML_SIZE_SMALL(struct aml_resource_dma), + ACPI_AML_SIZE_SMALL(struct aml_resource_start_dependent), + ACPI_AML_SIZE_SMALL(struct aml_resource_end_dependent), + ACPI_AML_SIZE_SMALL(struct aml_resource_io), + ACPI_AML_SIZE_SMALL(struct aml_resource_fixed_io), + ACPI_AML_SIZE_SMALL(struct aml_resource_fixed_dma), + 0, + 0, + 0, + ACPI_AML_SIZE_SMALL(struct aml_resource_vendor_small), + ACPI_AML_SIZE_SMALL(struct aml_resource_end_tag), + + /* Large descriptors */ + + 0, + ACPI_AML_SIZE_LARGE(struct aml_resource_memory24), + ACPI_AML_SIZE_LARGE(struct aml_resource_generic_register), + 0, + ACPI_AML_SIZE_LARGE(struct aml_resource_vendor_large), + ACPI_AML_SIZE_LARGE(struct aml_resource_memory32), + ACPI_AML_SIZE_LARGE(struct aml_resource_fixed_memory32), + ACPI_AML_SIZE_LARGE(struct aml_resource_address32), + ACPI_AML_SIZE_LARGE(struct aml_resource_address16), + ACPI_AML_SIZE_LARGE(struct aml_resource_extended_irq), + ACPI_AML_SIZE_LARGE(struct aml_resource_address64), + ACPI_AML_SIZE_LARGE(struct aml_resource_extended_address64), + ACPI_AML_SIZE_LARGE(struct aml_resource_gpio), + 0, + ACPI_AML_SIZE_LARGE(struct aml_resource_common_serialbus), +}; + +const u8 acpi_gbl_resource_aml_serial_bus_sizes[] = { + 0, + ACPI_AML_SIZE_LARGE(struct aml_resource_i2c_serialbus), + ACPI_AML_SIZE_LARGE(struct aml_resource_spi_serialbus), + ACPI_AML_SIZE_LARGE(struct aml_resource_uart_serialbus), +}; + +/* + * Resource types, used to validate the resource length field. + * The length of fixed-length types must match exactly, variable + * lengths must meet the minimum required length, etc. + * Zero indicates a reserved (and therefore invalid) resource type. + */ +static const u8 acpi_gbl_resource_types[] = { + /* Small descriptors */ + + 0, + 0, + 0, + 0, + ACPI_SMALL_VARIABLE_LENGTH, /* 04 IRQ */ + ACPI_FIXED_LENGTH, /* 05 DMA */ + ACPI_SMALL_VARIABLE_LENGTH, /* 06 start_dependent_functions */ + ACPI_FIXED_LENGTH, /* 07 end_dependent_functions */ + ACPI_FIXED_LENGTH, /* 08 IO */ + ACPI_FIXED_LENGTH, /* 09 fixed_IO */ + ACPI_FIXED_LENGTH, /* 0A fixed_DMA */ + 0, + 0, + 0, + ACPI_VARIABLE_LENGTH, /* 0E vendor_short */ + ACPI_FIXED_LENGTH, /* 0F end_tag */ + + /* Large descriptors */ + + 0, + ACPI_FIXED_LENGTH, /* 01 Memory24 */ + ACPI_FIXED_LENGTH, /* 02 generic_register */ + 0, + ACPI_VARIABLE_LENGTH, /* 04 vendor_long */ + ACPI_FIXED_LENGTH, /* 05 Memory32 */ + ACPI_FIXED_LENGTH, /* 06 memory32_fixed */ + ACPI_VARIABLE_LENGTH, /* 07 Dword* address */ + ACPI_VARIABLE_LENGTH, /* 08 Word* address */ + ACPI_VARIABLE_LENGTH, /* 09 extended_IRQ */ + ACPI_VARIABLE_LENGTH, /* 0A Qword* address */ + ACPI_FIXED_LENGTH, /* 0B Extended* address */ + ACPI_VARIABLE_LENGTH, /* 0C Gpio* */ + 0, + ACPI_VARIABLE_LENGTH /* 0E *serial_bus */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_walk_aml_resources + * + * PARAMETERS: walk_state - Current walk info + * PARAMETERS: aml - Pointer to the raw AML resource template + * aml_length - Length of the entire template + * user_function - Called once for each descriptor found. If + * NULL, a pointer to the end_tag is returned + * context - Passed to user_function + * + * RETURN: Status + * + * DESCRIPTION: Walk a raw AML resource list(buffer). User function called + * once for each resource found. + * + ******************************************************************************/ + +acpi_status +acpi_ut_walk_aml_resources(struct acpi_walk_state *walk_state, + u8 *aml, + acpi_size aml_length, + acpi_walk_aml_callback user_function, void **context) +{ + acpi_status status; + u8 *end_aml; + u8 resource_index; + u32 length; + u32 offset = 0; + u8 end_tag[2] = { 0x79, 0x00 }; + + ACPI_FUNCTION_TRACE(ut_walk_aml_resources); + + /* The absolute minimum resource template is one end_tag descriptor */ + + if (aml_length < sizeof(struct aml_resource_end_tag)) { + return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG); + } + + /* Point to the end of the resource template buffer */ + + end_aml = aml + aml_length; + + /* Walk the byte list, abort on any invalid descriptor type or length */ + + while (aml < end_aml) { + + /* Validate the Resource Type and Resource Length */ + + status = + acpi_ut_validate_resource(walk_state, aml, &resource_index); + if (ACPI_FAILURE(status)) { + /* + * Exit on failure. Cannot continue because the descriptor length + * may be bogus also. + */ + return_ACPI_STATUS(status); + } + + /* Get the length of this descriptor */ + + length = acpi_ut_get_descriptor_length(aml); + + /* Invoke the user function */ + + if (user_function) { + status = + user_function(aml, length, offset, resource_index, + context); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* An end_tag descriptor terminates this resource template */ + + if (acpi_ut_get_resource_type(aml) == + ACPI_RESOURCE_NAME_END_TAG) { + /* + * There must be at least one more byte in the buffer for + * the 2nd byte of the end_tag + */ + if ((aml + 1) >= end_aml) { + return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG); + } + + /* Return the pointer to the end_tag if requested */ + + if (!user_function) { + *context = aml; + } + + /* Normal exit */ + + return_ACPI_STATUS(AE_OK); + } + + aml += length; + offset += length; + } + + /* Did not find an end_tag descriptor */ + + if (user_function) { + + /* Insert an end_tag anyway. acpi_rs_get_list_length always leaves room */ + + (void)acpi_ut_validate_resource(walk_state, end_tag, + &resource_index); + status = + user_function(end_tag, 2, offset, resource_index, context); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + return_ACPI_STATUS(AE_AML_NO_RESOURCE_END_TAG); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_validate_resource + * + * PARAMETERS: walk_state - Current walk info + * aml - Pointer to the raw AML resource descriptor + * return_index - Where the resource index is returned. NULL + * if the index is not required. + * + * RETURN: Status, and optionally the Index into the global resource tables + * + * DESCRIPTION: Validate an AML resource descriptor by checking the Resource + * Type and Resource Length. Returns an index into the global + * resource information/dispatch tables for later use. + * + ******************************************************************************/ + +acpi_status +acpi_ut_validate_resource(struct acpi_walk_state *walk_state, + void *aml, u8 *return_index) +{ + union aml_resource *aml_resource; + u8 resource_type; + u8 resource_index; + acpi_rs_length resource_length; + acpi_rs_length minimum_resource_length; + + ACPI_FUNCTION_ENTRY(); + + /* + * 1) Validate the resource_type field (Byte 0) + */ + resource_type = ACPI_GET8(aml); + + /* + * Byte 0 contains the descriptor name (Resource Type) + * Examine the large/small bit in the resource header + */ + if (resource_type & ACPI_RESOURCE_NAME_LARGE) { + + /* Verify the large resource type (name) against the max */ + + if (resource_type > ACPI_RESOURCE_NAME_LARGE_MAX) { + goto invalid_resource; + } + + /* + * Large Resource Type -- bits 6:0 contain the name + * Translate range 0x80-0x8B to index range 0x10-0x1B + */ + resource_index = (u8) (resource_type - 0x70); + } else { + /* + * Small Resource Type -- bits 6:3 contain the name + * Shift range to index range 0x00-0x0F + */ + resource_index = (u8) + ((resource_type & ACPI_RESOURCE_NAME_SMALL_MASK) >> 3); + } + + /* + * Check validity of the resource type, via acpi_gbl_resource_types. Zero + * indicates an invalid resource. + */ + if (!acpi_gbl_resource_types[resource_index]) { + goto invalid_resource; + } + + /* + * Validate the resource_length field. This ensures that the length + * is at least reasonable, and guarantees that it is non-zero. + */ + resource_length = acpi_ut_get_resource_length(aml); + minimum_resource_length = acpi_gbl_resource_aml_sizes[resource_index]; + + /* Validate based upon the type of resource - fixed length or variable */ + + switch (acpi_gbl_resource_types[resource_index]) { + case ACPI_FIXED_LENGTH: + + /* Fixed length resource, length must match exactly */ + + if (resource_length != minimum_resource_length) { + goto bad_resource_length; + } + break; + + case ACPI_VARIABLE_LENGTH: + + /* Variable length resource, length must be at least the minimum */ + + if (resource_length < minimum_resource_length) { + goto bad_resource_length; + } + break; + + case ACPI_SMALL_VARIABLE_LENGTH: + + /* Small variable length resource, length can be (Min) or (Min-1) */ + + if ((resource_length > minimum_resource_length) || + (resource_length < (minimum_resource_length - 1))) { + goto bad_resource_length; + } + break; + + default: + + /* Shouldn't happen (because of validation earlier), but be sure */ + + goto invalid_resource; + } + + aml_resource = ACPI_CAST_PTR(union aml_resource, aml); + if (resource_type == ACPI_RESOURCE_NAME_SERIAL_BUS) { + + /* Validate the bus_type field */ + + if ((aml_resource->common_serial_bus.type == 0) || + (aml_resource->common_serial_bus.type > + AML_RESOURCE_MAX_SERIALBUSTYPE)) { + if (walk_state) { + ACPI_ERROR((AE_INFO, + "Invalid/unsupported SerialBus resource descriptor: BusType 0x%2.2X", + aml_resource->common_serial_bus. + type)); + } + return (AE_AML_INVALID_RESOURCE_TYPE); + } + } + + /* Optionally return the resource table index */ + + if (return_index) { + *return_index = resource_index; + } + + return (AE_OK); + +invalid_resource: + + if (walk_state) { + ACPI_ERROR((AE_INFO, + "Invalid/unsupported resource descriptor: Type 0x%2.2X", + resource_type)); + } + return (AE_AML_INVALID_RESOURCE_TYPE); + +bad_resource_length: + + if (walk_state) { + ACPI_ERROR((AE_INFO, + "Invalid resource descriptor length: Type " + "0x%2.2X, Length 0x%4.4X, MinLength 0x%4.4X", + resource_type, resource_length, + minimum_resource_length)); + } + return (AE_AML_BAD_RESOURCE_LENGTH); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_resource_type + * + * PARAMETERS: aml - Pointer to the raw AML resource descriptor + * + * RETURN: The Resource Type with no extraneous bits (except the + * Large/Small descriptor bit -- this is left alone) + * + * DESCRIPTION: Extract the Resource Type/Name from the first byte of + * a resource descriptor. + * + ******************************************************************************/ + +u8 acpi_ut_get_resource_type(void *aml) +{ + ACPI_FUNCTION_ENTRY(); + + /* + * Byte 0 contains the descriptor name (Resource Type) + * Examine the large/small bit in the resource header + */ + if (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_LARGE) { + + /* Large Resource Type -- bits 6:0 contain the name */ + + return (ACPI_GET8(aml)); + } else { + /* Small Resource Type -- bits 6:3 contain the name */ + + return ((u8) (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_SMALL_MASK)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_resource_length + * + * PARAMETERS: aml - Pointer to the raw AML resource descriptor + * + * RETURN: Byte Length + * + * DESCRIPTION: Get the "Resource Length" of a raw AML descriptor. By + * definition, this does not include the size of the descriptor + * header or the length field itself. + * + ******************************************************************************/ + +u16 acpi_ut_get_resource_length(void *aml) +{ + acpi_rs_length resource_length; + + ACPI_FUNCTION_ENTRY(); + + /* + * Byte 0 contains the descriptor name (Resource Type) + * Examine the large/small bit in the resource header + */ + if (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_LARGE) { + + /* Large Resource type -- bytes 1-2 contain the 16-bit length */ + + ACPI_MOVE_16_TO_16(&resource_length, ACPI_ADD_PTR(u8, aml, 1)); + + } else { + /* Small Resource type -- bits 2:0 of byte 0 contain the length */ + + resource_length = (u16) (ACPI_GET8(aml) & + ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK); + } + + return (resource_length); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_resource_header_length + * + * PARAMETERS: aml - Pointer to the raw AML resource descriptor + * + * RETURN: Length of the AML header (depends on large/small descriptor) + * + * DESCRIPTION: Get the length of the header for this resource. + * + ******************************************************************************/ + +u8 acpi_ut_get_resource_header_length(void *aml) +{ + ACPI_FUNCTION_ENTRY(); + + /* Examine the large/small bit in the resource header */ + + if (ACPI_GET8(aml) & ACPI_RESOURCE_NAME_LARGE) { + return (sizeof(struct aml_resource_large_header)); + } else { + return (sizeof(struct aml_resource_small_header)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_descriptor_length + * + * PARAMETERS: aml - Pointer to the raw AML resource descriptor + * + * RETURN: Byte length + * + * DESCRIPTION: Get the total byte length of a raw AML descriptor, including the + * length of the descriptor header and the length field itself. + * Used to walk descriptor lists. + * + ******************************************************************************/ + +u32 acpi_ut_get_descriptor_length(void *aml) +{ + ACPI_FUNCTION_ENTRY(); + + /* + * Get the Resource Length (does not include header length) and add + * the header length (depends on if this is a small or large resource) + */ + return (acpi_ut_get_resource_length(aml) + + acpi_ut_get_resource_header_length(aml)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_resource_end_tag + * + * PARAMETERS: obj_desc - The resource template buffer object + * end_tag - Where the pointer to the end_tag is returned + * + * RETURN: Status, pointer to the end tag + * + * DESCRIPTION: Find the end_tag resource descriptor in an AML resource template + * Note: allows a buffer length of zero. + * + ******************************************************************************/ + +acpi_status +acpi_ut_get_resource_end_tag(union acpi_operand_object *obj_desc, u8 **end_tag) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_get_resource_end_tag); + + /* Allow a buffer length of zero */ + + if (!obj_desc->buffer.length) { + *end_tag = obj_desc->buffer.pointer; + return_ACPI_STATUS(AE_OK); + } + + /* Validate the template and get a pointer to the end_tag */ + + status = acpi_ut_walk_aml_resources(NULL, obj_desc->buffer.pointer, + obj_desc->buffer.length, NULL, + (void **)end_tag); + + return_ACPI_STATUS(status); +} diff --git a/kernel/drivers/acpi/acpica/utstate.c b/kernel/drivers/acpi/acpica/utstate.c new file mode 100644 index 000000000..f201171c5 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utstate.c @@ -0,0 +1,308 @@ +/******************************************************************************* + * + * Module Name: utstate - state object support procedures + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utstate") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_push_generic_state + * + * PARAMETERS: list_head - Head of the state stack + * state - State object to push + * + * RETURN: None + * + * DESCRIPTION: Push a state object onto a state stack + * + ******************************************************************************/ +void +acpi_ut_push_generic_state(union acpi_generic_state **list_head, + union acpi_generic_state *state) +{ + ACPI_FUNCTION_ENTRY(); + + /* Push the state object onto the front of the list (stack) */ + + state->common.next = *list_head; + *list_head = state; + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_pop_generic_state + * + * PARAMETERS: list_head - Head of the state stack + * + * RETURN: The popped state object + * + * DESCRIPTION: Pop a state object from a state stack + * + ******************************************************************************/ + +union acpi_generic_state *acpi_ut_pop_generic_state(union acpi_generic_state + **list_head) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_ENTRY(); + + /* Remove the state object at the head of the list (stack) */ + + state = *list_head; + if (state) { + + /* Update the list head */ + + *list_head = state->common.next; + } + + return (state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_generic_state + * + * PARAMETERS: None + * + * RETURN: The new state object. NULL on failure. + * + * DESCRIPTION: Create a generic state object. Attempt to obtain one from + * the global state cache; If none available, create a new one. + * + ******************************************************************************/ + +union acpi_generic_state *acpi_ut_create_generic_state(void) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_ENTRY(); + + state = acpi_os_acquire_object(acpi_gbl_state_cache); + if (state) { + + /* Initialize */ + state->common.descriptor_type = ACPI_DESC_TYPE_STATE; + } + + return (state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_thread_state + * + * PARAMETERS: None + * + * RETURN: New Thread State. NULL on failure + * + * DESCRIPTION: Create a "Thread State" - a flavor of the generic state used + * to track per-thread info during method execution + * + ******************************************************************************/ + +struct acpi_thread_state *acpi_ut_create_thread_state(void) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_ENTRY(); + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state(); + if (!state) { + return (NULL); + } + + /* Init fields specific to the update struct */ + + state->common.descriptor_type = ACPI_DESC_TYPE_STATE_THREAD; + state->thread.thread_id = acpi_os_get_thread_id(); + + /* Check for invalid thread ID - zero is very bad, it will break things */ + + if (!state->thread.thread_id) { + ACPI_ERROR((AE_INFO, "Invalid zero ID from AcpiOsGetThreadId")); + state->thread.thread_id = (acpi_thread_id) 1; + } + + return ((struct acpi_thread_state *)state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_update_state + * + * PARAMETERS: object - Initial Object to be installed in the state + * action - Update action to be performed + * + * RETURN: New state object, null on failure + * + * DESCRIPTION: Create an "Update State" - a flavor of the generic state used + * to update reference counts and delete complex objects such + * as packages. + * + ******************************************************************************/ + +union acpi_generic_state *acpi_ut_create_update_state(union acpi_operand_object + *object, u16 action) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_ENTRY(); + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state(); + if (!state) { + return (NULL); + } + + /* Init fields specific to the update struct */ + + state->common.descriptor_type = ACPI_DESC_TYPE_STATE_UPDATE; + state->update.object = object; + state->update.value = action; + return (state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_pkg_state + * + * PARAMETERS: object - Initial Object to be installed in the state + * action - Update action to be performed + * + * RETURN: New state object, null on failure + * + * DESCRIPTION: Create a "Package State" + * + ******************************************************************************/ + +union acpi_generic_state *acpi_ut_create_pkg_state(void *internal_object, + void *external_object, + u16 index) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_ENTRY(); + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state(); + if (!state) { + return (NULL); + } + + /* Init fields specific to the update struct */ + + state->common.descriptor_type = ACPI_DESC_TYPE_STATE_PACKAGE; + state->pkg.source_object = (union acpi_operand_object *)internal_object; + state->pkg.dest_object = external_object; + state->pkg.index = index; + state->pkg.num_packages = 1; + return (state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_control_state + * + * PARAMETERS: None + * + * RETURN: New state object, null on failure + * + * DESCRIPTION: Create a "Control State" - a flavor of the generic state used + * to support nested IF/WHILE constructs in the AML. + * + ******************************************************************************/ + +union acpi_generic_state *acpi_ut_create_control_state(void) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_ENTRY(); + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state(); + if (!state) { + return (NULL); + } + + /* Init fields specific to the control struct */ + + state->common.descriptor_type = ACPI_DESC_TYPE_STATE_CONTROL; + state->common.state = ACPI_CONTROL_CONDITIONAL_EXECUTING; + return (state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_generic_state + * + * PARAMETERS: state - The state object to be deleted + * + * RETURN: None + * + * DESCRIPTION: Release a state object to the state cache. NULL state objects + * are ignored. + * + ******************************************************************************/ + +void acpi_ut_delete_generic_state(union acpi_generic_state *state) +{ + ACPI_FUNCTION_ENTRY(); + + /* Ignore null state */ + + if (state) { + (void)acpi_os_release_object(acpi_gbl_state_cache, state); + } + return; +} diff --git a/kernel/drivers/acpi/acpica/utstring.c b/kernel/drivers/acpi/acpica/utstring.c new file mode 100644 index 000000000..83b6c5249 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utstring.c @@ -0,0 +1,648 @@ +/******************************************************************************* + * + * Module Name: utstring - Common functions for strings and characters + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utstring") + +/* + * Non-ANSI C library functions - strlwr, strupr, stricmp, and a 64-bit + * version of strtoul. + */ +#ifdef ACPI_ASL_COMPILER +/******************************************************************************* + * + * FUNCTION: acpi_ut_strlwr (strlwr) + * + * PARAMETERS: src_string - The source string to convert + * + * RETURN: None + * + * DESCRIPTION: Convert string to lowercase + * + * NOTE: This is not a POSIX function, so it appears here, not in utclib.c + * + ******************************************************************************/ +void acpi_ut_strlwr(char *src_string) +{ + char *string; + + ACPI_FUNCTION_ENTRY(); + + if (!src_string) { + return; + } + + /* Walk entire string, lowercasing the letters */ + + for (string = src_string; *string; string++) { + *string = (char)ACPI_TOLOWER(*string); + } + + return; +} + +/****************************************************************************** + * + * FUNCTION: acpi_ut_stricmp (stricmp) + * + * PARAMETERS: string1 - first string to compare + * string2 - second string to compare + * + * RETURN: int that signifies string relationship. Zero means strings + * are equal. + * + * DESCRIPTION: Implementation of the non-ANSI stricmp function (compare + * strings with no case sensitivity) + * + ******************************************************************************/ + +int acpi_ut_stricmp(char *string1, char *string2) +{ + int c1; + int c2; + + do { + c1 = tolower((int)*string1); + c2 = tolower((int)*string2); + + string1++; + string2++; + } + while ((c1 == c2) && (c1)); + + return (c1 - c2); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strupr (strupr) + * + * PARAMETERS: src_string - The source string to convert + * + * RETURN: None + * + * DESCRIPTION: Convert string to uppercase + * + * NOTE: This is not a POSIX function, so it appears here, not in utclib.c + * + ******************************************************************************/ + +void acpi_ut_strupr(char *src_string) +{ + char *string; + + ACPI_FUNCTION_ENTRY(); + + if (!src_string) { + return; + } + + /* Walk entire string, uppercasing the letters */ + + for (string = src_string; *string; string++) { + *string = (char)ACPI_TOUPPER(*string); + } + + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strtoul64 + * + * PARAMETERS: string - Null terminated string + * base - Radix of the string: 16 or ACPI_ANY_BASE; + * ACPI_ANY_BASE means 'in behalf of to_integer' + * ret_integer - Where the converted integer is returned + * + * RETURN: Status and Converted value + * + * DESCRIPTION: Convert a string into an unsigned value. Performs either a + * 32-bit or 64-bit conversion, depending on the current mode + * of the interpreter. + * NOTE: Does not support Octal strings, not needed. + * + ******************************************************************************/ + +acpi_status acpi_ut_strtoul64(char *string, u32 base, u64 *ret_integer) +{ + u32 this_digit = 0; + u64 return_value = 0; + u64 quotient; + u64 dividend; + u32 to_integer_op = (base == ACPI_ANY_BASE); + u32 mode32 = (acpi_gbl_integer_byte_width == 4); + u8 valid_digits = 0; + u8 sign_of0x = 0; + u8 term = 0; + + ACPI_FUNCTION_TRACE_STR(ut_stroul64, string); + + switch (base) { + case ACPI_ANY_BASE: + case 16: + + break; + + default: + + /* Invalid Base */ + + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (!string) { + goto error_exit; + } + + /* Skip over any white space in the buffer */ + + while ((*string) && (ACPI_IS_SPACE(*string) || *string == '\t')) { + string++; + } + + if (to_integer_op) { + /* + * Base equal to ACPI_ANY_BASE means 'ToInteger operation case'. + * We need to determine if it is decimal or hexadecimal. + */ + if ((*string == '0') && (ACPI_TOLOWER(*(string + 1)) == 'x')) { + sign_of0x = 1; + base = 16; + + /* Skip over the leading '0x' */ + string += 2; + } else { + base = 10; + } + } + + /* Any string left? Check that '0x' is not followed by white space. */ + + if (!(*string) || ACPI_IS_SPACE(*string) || *string == '\t') { + if (to_integer_op) { + goto error_exit; + } else { + goto all_done; + } + } + + /* + * Perform a 32-bit or 64-bit conversion, depending upon the current + * execution mode of the interpreter + */ + dividend = (mode32) ? ACPI_UINT32_MAX : ACPI_UINT64_MAX; + + /* Main loop: convert the string to a 32- or 64-bit integer */ + + while (*string) { + if (ACPI_IS_DIGIT(*string)) { + + /* Convert ASCII 0-9 to Decimal value */ + + this_digit = ((u8)*string) - '0'; + } else if (base == 10) { + + /* Digit is out of range; possible in to_integer case only */ + + term = 1; + } else { + this_digit = (u8)ACPI_TOUPPER(*string); + if (ACPI_IS_XDIGIT((char)this_digit)) { + + /* Convert ASCII Hex char to value */ + + this_digit = this_digit - 'A' + 10; + } else { + term = 1; + } + } + + if (term) { + if (to_integer_op) { + goto error_exit; + } else { + break; + } + } else if ((valid_digits == 0) && (this_digit == 0) + && !sign_of0x) { + + /* Skip zeros */ + string++; + continue; + } + + valid_digits++; + + if (sign_of0x + && ((valid_digits > 16) + || ((valid_digits > 8) && mode32))) { + /* + * This is to_integer operation case. + * No any restrictions for string-to-integer conversion, + * see ACPI spec. + */ + goto error_exit; + } + + /* Divide the digit into the correct position */ + + (void)acpi_ut_short_divide((dividend - (u64)this_digit), + base, "ient, NULL); + + if (return_value > quotient) { + if (to_integer_op) { + goto error_exit; + } else { + break; + } + } + + return_value *= base; + return_value += this_digit; + string++; + } + + /* All done, normal exit */ + +all_done: + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(return_value))); + + *ret_integer = return_value; + return_ACPI_STATUS(AE_OK); + +error_exit: + /* Base was set/validated above */ + + if (base == 10) { + return_ACPI_STATUS(AE_BAD_DECIMAL_CONSTANT); + } else { + return_ACPI_STATUS(AE_BAD_HEX_CONSTANT); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_print_string + * + * PARAMETERS: string - Null terminated ASCII string + * max_length - Maximum output length. Used to constrain the + * length of strings during debug output only. + * + * RETURN: None + * + * DESCRIPTION: Dump an ASCII string with support for ACPI-defined escape + * sequences. + * + ******************************************************************************/ + +void acpi_ut_print_string(char *string, u16 max_length) +{ + u32 i; + + if (!string) { + acpi_os_printf("<\"NULL STRING PTR\">"); + return; + } + + acpi_os_printf("\""); + for (i = 0; (i < max_length) && string[i]; i++) { + + /* Escape sequences */ + + switch (string[i]) { + case 0x07: + + acpi_os_printf("\\a"); /* BELL */ + break; + + case 0x08: + + acpi_os_printf("\\b"); /* BACKSPACE */ + break; + + case 0x0C: + + acpi_os_printf("\\f"); /* FORMFEED */ + break; + + case 0x0A: + + acpi_os_printf("\\n"); /* LINEFEED */ + break; + + case 0x0D: + + acpi_os_printf("\\r"); /* CARRIAGE RETURN */ + break; + + case 0x09: + + acpi_os_printf("\\t"); /* HORIZONTAL TAB */ + break; + + case 0x0B: + + acpi_os_printf("\\v"); /* VERTICAL TAB */ + break; + + case '\'': /* Single Quote */ + case '\"': /* Double Quote */ + case '\\': /* Backslash */ + + acpi_os_printf("\\%c", (int)string[i]); + break; + + default: + + /* Check for printable character or hex escape */ + + if (ACPI_IS_PRINT(string[i])) { + /* This is a normal character */ + + acpi_os_printf("%c", (int)string[i]); + } else { + /* All others will be Hex escapes */ + + acpi_os_printf("\\x%2.2X", (s32) string[i]); + } + break; + } + } + acpi_os_printf("\""); + + if (i == max_length && string[i]) { + acpi_os_printf("..."); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_valid_acpi_char + * + * PARAMETERS: char - The character to be examined + * position - Byte position (0-3) + * + * RETURN: TRUE if the character is valid, FALSE otherwise + * + * DESCRIPTION: Check for a valid ACPI character. Must be one of: + * 1) Upper case alpha + * 2) numeric + * 3) underscore + * + * We allow a '!' as the last character because of the ASF! table + * + ******************************************************************************/ + +u8 acpi_ut_valid_acpi_char(char character, u32 position) +{ + + if (!((character >= 'A' && character <= 'Z') || + (character >= '0' && character <= '9') || (character == '_'))) { + + /* Allow a '!' in the last position */ + + if (character == '!' && position == 3) { + return (TRUE); + } + + return (FALSE); + } + + return (TRUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_valid_acpi_name + * + * PARAMETERS: name - The name to be examined. Does not have to + * be NULL terminated string. + * + * RETURN: TRUE if the name is valid, FALSE otherwise + * + * DESCRIPTION: Check for a valid ACPI name. Each character must be one of: + * 1) Upper case alpha + * 2) numeric + * 3) underscore + * + ******************************************************************************/ + +u8 acpi_ut_valid_acpi_name(char *name) +{ + u32 i; + + ACPI_FUNCTION_ENTRY(); + + for (i = 0; i < ACPI_NAME_SIZE; i++) { + if (!acpi_ut_valid_acpi_char(name[i], i)) { + return (FALSE); + } + } + + return (TRUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_repair_name + * + * PARAMETERS: name - The ACPI name to be repaired + * + * RETURN: Repaired version of the name + * + * DESCRIPTION: Repair an ACPI name: Change invalid characters to '*' and + * return the new name. NOTE: the Name parameter must reside in + * read/write memory, cannot be a const. + * + * An ACPI Name must consist of valid ACPI characters. We will repair the name + * if necessary because we don't want to abort because of this, but we want + * all namespace names to be printable. A warning message is appropriate. + * + * This issue came up because there are in fact machines that exhibit + * this problem, and we want to be able to enable ACPI support for them, + * even though there are a few bad names. + * + ******************************************************************************/ + +void acpi_ut_repair_name(char *name) +{ + u32 i; + u8 found_bad_char = FALSE; + u32 original_name; + + ACPI_FUNCTION_NAME(ut_repair_name); + + ACPI_MOVE_NAME(&original_name, name); + + /* Check each character in the name */ + + for (i = 0; i < ACPI_NAME_SIZE; i++) { + if (acpi_ut_valid_acpi_char(name[i], i)) { + continue; + } + + /* + * Replace a bad character with something printable, yet technically + * still invalid. This prevents any collisions with existing "good" + * names in the namespace. + */ + name[i] = '*'; + found_bad_char = TRUE; + } + + if (found_bad_char) { + + /* Report warning only if in strict mode or debug mode */ + + if (!acpi_gbl_enable_interpreter_slack) { + ACPI_WARNING((AE_INFO, + "Invalid character(s) in name (0x%.8X), repaired: [%4.4s]", + original_name, name)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Invalid character(s) in name (0x%.8X), repaired: [%4.4s]", + original_name, name)); + } + } +} + +#if defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP +/******************************************************************************* + * + * FUNCTION: ut_convert_backslashes + * + * PARAMETERS: pathname - File pathname string to be converted + * + * RETURN: Modifies the input Pathname + * + * DESCRIPTION: Convert all backslashes (0x5C) to forward slashes (0x2F) within + * the entire input file pathname string. + * + ******************************************************************************/ + +void ut_convert_backslashes(char *pathname) +{ + + if (!pathname) { + return; + } + + while (*pathname) { + if (*pathname == '\\') { + *pathname = '/'; + } + + pathname++; + } +} +#endif + +#if defined (ACPI_DEBUGGER) || defined (ACPI_APPLICATION) +/******************************************************************************* + * + * FUNCTION: acpi_ut_safe_strcpy, acpi_ut_safe_strcat, acpi_ut_safe_strncat + * + * PARAMETERS: Adds a "DestSize" parameter to each of the standard string + * functions. This is the size of the Destination buffer. + * + * RETURN: TRUE if the operation would overflow the destination buffer. + * + * DESCRIPTION: Safe versions of standard Clib string functions. Ensure that + * the result of the operation will not overflow the output string + * buffer. + * + * NOTE: These functions are typically only helpful for processing + * user input and command lines. For most ACPICA code, the + * required buffer length is precisely calculated before buffer + * allocation, so the use of these functions is unnecessary. + * + ******************************************************************************/ + +u8 acpi_ut_safe_strcpy(char *dest, acpi_size dest_size, char *source) +{ + + if (ACPI_STRLEN(source) >= dest_size) { + return (TRUE); + } + + ACPI_STRCPY(dest, source); + return (FALSE); +} + +u8 acpi_ut_safe_strcat(char *dest, acpi_size dest_size, char *source) +{ + + if ((ACPI_STRLEN(dest) + ACPI_STRLEN(source)) >= dest_size) { + return (TRUE); + } + + ACPI_STRCAT(dest, source); + return (FALSE); +} + +u8 +acpi_ut_safe_strncat(char *dest, + acpi_size dest_size, + char *source, acpi_size max_transfer_length) +{ + acpi_size actual_transfer_length; + + actual_transfer_length = + ACPI_MIN(max_transfer_length, ACPI_STRLEN(source)); + + if ((ACPI_STRLEN(dest) + actual_transfer_length) >= dest_size) { + return (TRUE); + } + + ACPI_STRNCAT(dest, source, max_transfer_length); + return (FALSE); +} +#endif diff --git a/kernel/drivers/acpi/acpica/uttrack.c b/kernel/drivers/acpi/acpica/uttrack.c new file mode 100644 index 000000000..130dd9f96 --- /dev/null +++ b/kernel/drivers/acpi/acpica/uttrack.c @@ -0,0 +1,722 @@ +/****************************************************************************** + * + * Module Name: uttrack - Memory allocation tracking routines (debug only) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * These procedures are used for tracking memory leaks in the subsystem, and + * they get compiled out when the ACPI_DBG_TRACK_ALLOCATIONS is not set. + * + * Each memory allocation is tracked via a doubly linked list. Each + * element contains the caller's component, module name, function name, and + * line number. acpi_ut_allocate and acpi_ut_allocate_zeroed call + * acpi_ut_track_allocation to add an element to the list; deletion + * occurs in the body of acpi_ut_free. + */ + +#include +#include "accommon.h" + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("uttrack") + +/* Local prototypes */ +static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct + acpi_debug_mem_block + *allocation); + +static acpi_status +acpi_ut_track_allocation(struct acpi_debug_mem_block *address, + acpi_size size, + u8 alloc_type, + u32 component, const char *module, u32 line); + +static acpi_status +acpi_ut_remove_allocation(struct acpi_debug_mem_block *address, + u32 component, const char *module, u32 line); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_list + * + * PARAMETERS: cache_name - Ascii name for the cache + * object_size - Size of each cached object + * return_cache - Where the new cache object is returned + * + * RETURN: Status + * + * DESCRIPTION: Create a local memory list for tracking purposed + * + ******************************************************************************/ + +acpi_status +acpi_ut_create_list(char *list_name, + u16 object_size, struct acpi_memory_list **return_cache) +{ + struct acpi_memory_list *cache; + + cache = acpi_os_allocate(sizeof(struct acpi_memory_list)); + if (!cache) { + return (AE_NO_MEMORY); + } + + ACPI_MEMSET(cache, 0, sizeof(struct acpi_memory_list)); + + cache->list_name = list_name; + cache->object_size = object_size; + + *return_cache = cache; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_allocate_and_track + * + * PARAMETERS: size - Size of the allocation + * component - Component type of caller + * module - Source file name of caller + * line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: The subsystem's equivalent of malloc. + * + ******************************************************************************/ + +void *acpi_ut_allocate_and_track(acpi_size size, + u32 component, const char *module, u32 line) +{ + struct acpi_debug_mem_block *allocation; + acpi_status status; + + /* Check for an inadvertent size of zero bytes */ + + if (!size) { + ACPI_WARNING((module, line, + "Attempt to allocate zero bytes, allocating 1 byte")); + size = 1; + } + + allocation = + acpi_os_allocate(size + sizeof(struct acpi_debug_mem_header)); + if (!allocation) { + + /* Report allocation error */ + + ACPI_WARNING((module, line, + "Could not allocate size %u", (u32)size)); + + return (NULL); + } + + status = acpi_ut_track_allocation(allocation, size, + ACPI_MEM_MALLOC, component, module, + line); + if (ACPI_FAILURE(status)) { + acpi_os_free(allocation); + return (NULL); + } + + acpi_gbl_global_list->total_allocated++; + acpi_gbl_global_list->total_size += (u32)size; + acpi_gbl_global_list->current_total_size += (u32)size; + if (acpi_gbl_global_list->current_total_size > + acpi_gbl_global_list->max_occupied) { + acpi_gbl_global_list->max_occupied = + acpi_gbl_global_list->current_total_size; + } + + return ((void *)&allocation->user_space); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_allocate_zeroed_and_track + * + * PARAMETERS: size - Size of the allocation + * component - Component type of caller + * module - Source file name of caller + * line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: Subsystem equivalent of calloc. + * + ******************************************************************************/ + +void *acpi_ut_allocate_zeroed_and_track(acpi_size size, + u32 component, + const char *module, u32 line) +{ + struct acpi_debug_mem_block *allocation; + acpi_status status; + + /* Check for an inadvertent size of zero bytes */ + + if (!size) { + ACPI_WARNING((module, line, + "Attempt to allocate zero bytes, allocating 1 byte")); + size = 1; + } + + allocation = + acpi_os_allocate_zeroed(size + + sizeof(struct acpi_debug_mem_header)); + if (!allocation) { + + /* Report allocation error */ + + ACPI_ERROR((module, line, + "Could not allocate size %u", (u32)size)); + return (NULL); + } + + status = acpi_ut_track_allocation(allocation, size, + ACPI_MEM_CALLOC, component, module, + line); + if (ACPI_FAILURE(status)) { + acpi_os_free(allocation); + return (NULL); + } + + acpi_gbl_global_list->total_allocated++; + acpi_gbl_global_list->total_size += (u32)size; + acpi_gbl_global_list->current_total_size += (u32)size; + if (acpi_gbl_global_list->current_total_size > + acpi_gbl_global_list->max_occupied) { + acpi_gbl_global_list->max_occupied = + acpi_gbl_global_list->current_total_size; + } + + return ((void *)&allocation->user_space); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_free_and_track + * + * PARAMETERS: allocation - Address of the memory to deallocate + * component - Component type of caller + * module - Source file name of caller + * line - Line number of caller + * + * RETURN: None + * + * DESCRIPTION: Frees the memory at Allocation + * + ******************************************************************************/ + +void +acpi_ut_free_and_track(void *allocation, + u32 component, const char *module, u32 line) +{ + struct acpi_debug_mem_block *debug_block; + acpi_status status; + + ACPI_FUNCTION_TRACE_PTR(ut_free, allocation); + + if (NULL == allocation) { + ACPI_ERROR((module, line, "Attempt to delete a NULL address")); + + return_VOID; + } + + debug_block = ACPI_CAST_PTR(struct acpi_debug_mem_block, + (((char *)allocation) - + sizeof(struct acpi_debug_mem_header))); + + acpi_gbl_global_list->total_freed++; + acpi_gbl_global_list->current_total_size -= debug_block->size; + + status = acpi_ut_remove_allocation(debug_block, + component, module, line); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Could not free memory")); + } + + acpi_os_free(debug_block); + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p freed (block %p)\n", + allocation, debug_block)); + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_find_allocation + * + * PARAMETERS: allocation - Address of allocated memory + * + * RETURN: Three cases: + * 1) List is empty, NULL is returned. + * 2) Element was found. Returns Allocation parameter. + * 3) Element was not found. Returns position where it should be + * inserted into the list. + * + * DESCRIPTION: Searches for an element in the global allocation tracking list. + * If the element is not found, returns the location within the + * list where the element should be inserted. + * + * Note: The list is ordered by larger-to-smaller addresses. + * + * This global list is used to detect memory leaks in ACPICA as + * well as other issues such as an attempt to release the same + * internal object more than once. Although expensive as far + * as cpu time, this list is much more helpful for finding these + * types of issues than using memory leak detectors outside of + * the ACPICA code. + * + ******************************************************************************/ + +static struct acpi_debug_mem_block *acpi_ut_find_allocation(struct + acpi_debug_mem_block + *allocation) +{ + struct acpi_debug_mem_block *element; + + element = acpi_gbl_global_list->list_head; + if (!element) { + return (NULL); + } + + /* + * Search for the address. + * + * Note: List is ordered by larger-to-smaller addresses, on the + * assumption that a new allocation usually has a larger address + * than previous allocations. + */ + while (element > allocation) { + + /* Check for end-of-list */ + + if (!element->next) { + return (element); + } + + element = element->next; + } + + if (element == allocation) { + return (element); + } + + return (element->previous); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_track_allocation + * + * PARAMETERS: allocation - Address of allocated memory + * size - Size of the allocation + * alloc_type - MEM_MALLOC or MEM_CALLOC + * component - Component type of caller + * module - Source file name of caller + * line - Line number of caller + * + * RETURN: Status + * + * DESCRIPTION: Inserts an element into the global allocation tracking list. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_track_allocation(struct acpi_debug_mem_block *allocation, + acpi_size size, + u8 alloc_type, + u32 component, const char *module, u32 line) +{ + struct acpi_memory_list *mem_list; + struct acpi_debug_mem_block *element; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_PTR(ut_track_allocation, allocation); + + if (acpi_gbl_disable_mem_tracking) { + return_ACPI_STATUS(AE_OK); + } + + mem_list = acpi_gbl_global_list; + status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Search the global list for this address to make sure it is not + * already present. This will catch several kinds of problems. + */ + element = acpi_ut_find_allocation(allocation); + if (element == allocation) { + ACPI_ERROR((AE_INFO, + "UtTrackAllocation: Allocation (%p) already present in global list!", + allocation)); + goto unlock_and_exit; + } + + /* Fill in the instance data */ + + allocation->size = (u32)size; + allocation->alloc_type = alloc_type; + allocation->component = component; + allocation->line = line; + + ACPI_STRNCPY(allocation->module, module, ACPI_MAX_MODULE_NAME); + allocation->module[ACPI_MAX_MODULE_NAME - 1] = 0; + + if (!element) { + + /* Insert at list head */ + + if (mem_list->list_head) { + ((struct acpi_debug_mem_block *)(mem_list->list_head))-> + previous = allocation; + } + + allocation->next = mem_list->list_head; + allocation->previous = NULL; + + mem_list->list_head = allocation; + } else { + /* Insert after element */ + + allocation->next = element->next; + allocation->previous = element; + + if (element->next) { + (element->next)->previous = allocation; + } + + element->next = allocation; + } + +unlock_and_exit: + status = acpi_ut_release_mutex(ACPI_MTX_MEMORY); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_allocation + * + * PARAMETERS: allocation - Address of allocated memory + * component - Component type of caller + * module - Source file name of caller + * line - Line number of caller + * + * RETURN: Status + * + * DESCRIPTION: Deletes an element from the global allocation tracking list. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_remove_allocation(struct acpi_debug_mem_block *allocation, + u32 component, const char *module, u32 line) +{ + struct acpi_memory_list *mem_list; + acpi_status status; + + ACPI_FUNCTION_NAME(ut_remove_allocation); + + if (acpi_gbl_disable_mem_tracking) { + return (AE_OK); + } + + mem_list = acpi_gbl_global_list; + if (NULL == mem_list->list_head) { + + /* No allocations! */ + + ACPI_ERROR((module, line, + "Empty allocation list, nothing to free!")); + + return (AE_OK); + } + + status = acpi_ut_acquire_mutex(ACPI_MTX_MEMORY); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Unlink */ + + if (allocation->previous) { + (allocation->previous)->next = allocation->next; + } else { + mem_list->list_head = allocation->next; + } + + if (allocation->next) { + (allocation->next)->previous = allocation->previous; + } + + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Freeing %p, size 0%X\n", + &allocation->user_space, allocation->size)); + + /* Mark the segment as deleted */ + + ACPI_MEMSET(&allocation->user_space, 0xEA, allocation->size); + + status = acpi_ut_release_mutex(ACPI_MTX_MEMORY); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dump_allocation_info + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Print some info about the outstanding allocations. + * + ******************************************************************************/ + +void acpi_ut_dump_allocation_info(void) +{ +/* + struct acpi_memory_list *mem_list; +*/ + + ACPI_FUNCTION_TRACE(ut_dump_allocation_info); + +/* + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Current allocations", + mem_list->current_count, + ROUND_UP_TO_1K (mem_list->current_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations", + mem_list->max_concurrent_count, + ROUND_UP_TO_1K (mem_list->max_concurrent_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects", + running_object_count, + ROUND_UP_TO_1K (running_object_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Total (all) allocations", + running_alloc_count, + ROUND_UP_TO_1K (running_alloc_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Current Nodes", + acpi_gbl_current_node_count, + ROUND_UP_TO_1K (acpi_gbl_current_node_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Max Nodes", + acpi_gbl_max_concurrent_node_count, + ROUND_UP_TO_1K ((acpi_gbl_max_concurrent_node_count * + sizeof (struct acpi_namespace_node))))); +*/ + return_VOID; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dump_allocations + * + * PARAMETERS: component - Component(s) to dump info for. + * module - Module to dump info for. NULL means all. + * + * RETURN: None + * + * DESCRIPTION: Print a list of all outstanding allocations. + * + ******************************************************************************/ + +void acpi_ut_dump_allocations(u32 component, const char *module) +{ + struct acpi_debug_mem_block *element; + union acpi_descriptor *descriptor; + u32 num_outstanding = 0; + u8 descriptor_type; + + ACPI_FUNCTION_TRACE(ut_dump_allocations); + + if (acpi_gbl_disable_mem_tracking) { + return_VOID; + } + + /* + * Walk the allocation list. + */ + if (ACPI_FAILURE(acpi_ut_acquire_mutex(ACPI_MTX_MEMORY))) { + return_VOID; + } + + element = acpi_gbl_global_list->list_head; + while (element) { + if ((element->component & component) && + ((module == NULL) + || (0 == ACPI_STRCMP(module, element->module)))) { + descriptor = + ACPI_CAST_PTR(union acpi_descriptor, + &element->user_space); + + if (element->size < + sizeof(struct acpi_common_descriptor)) { + acpi_os_printf("%p Length 0x%04X %9.9s-%u " + "[Not a Descriptor - too small]\n", + descriptor, element->size, + element->module, element->line); + } else { + /* Ignore allocated objects that are in a cache */ + + if (ACPI_GET_DESCRIPTOR_TYPE(descriptor) != + ACPI_DESC_TYPE_CACHED) { + acpi_os_printf + ("%p Length 0x%04X %9.9s-%u [%s] ", + descriptor, element->size, + element->module, element->line, + acpi_ut_get_descriptor_name + (descriptor)); + + /* Validate the descriptor type using Type field and length */ + + descriptor_type = 0; /* Not a valid descriptor type */ + + switch (ACPI_GET_DESCRIPTOR_TYPE + (descriptor)) { + case ACPI_DESC_TYPE_OPERAND: + + if (element->size == + sizeof(union + acpi_operand_object)) + { + descriptor_type = + ACPI_DESC_TYPE_OPERAND; + } + break; + + case ACPI_DESC_TYPE_PARSER: + + if (element->size == + sizeof(union + acpi_parse_object)) { + descriptor_type = + ACPI_DESC_TYPE_PARSER; + } + break; + + case ACPI_DESC_TYPE_NAMED: + + if (element->size == + sizeof(struct + acpi_namespace_node)) + { + descriptor_type = + ACPI_DESC_TYPE_NAMED; + } + break; + + default: + + break; + } + + /* Display additional info for the major descriptor types */ + + switch (descriptor_type) { + case ACPI_DESC_TYPE_OPERAND: + + acpi_os_printf + ("%12.12s RefCount 0x%04X\n", + acpi_ut_get_type_name + (descriptor->object.common. + type), + descriptor->object.common. + reference_count); + break; + + case ACPI_DESC_TYPE_PARSER: + + acpi_os_printf + ("AmlOpcode 0x%04hX\n", + descriptor->op.asl. + aml_opcode); + break; + + case ACPI_DESC_TYPE_NAMED: + + acpi_os_printf("%4.4s\n", + acpi_ut_get_node_name + (&descriptor-> + node)); + break; + + default: + + acpi_os_printf("\n"); + break; + } + } + } + + num_outstanding++; + } + + element = element->next; + } + + (void)acpi_ut_release_mutex(ACPI_MTX_MEMORY); + + /* Print summary */ + + if (!num_outstanding) { + ACPI_INFO((AE_INFO, "No outstanding allocations")); + } else { + ACPI_ERROR((AE_INFO, "%u(0x%X) Outstanding allocations", + num_outstanding, num_outstanding)); + } + + return_VOID; +} + +#endif /* ACPI_DBG_TRACK_ALLOCATIONS */ diff --git a/kernel/drivers/acpi/acpica/utuuid.c b/kernel/drivers/acpi/acpica/utuuid.c new file mode 100644 index 000000000..e6cab669b --- /dev/null +++ b/kernel/drivers/acpi/acpica/utuuid.c @@ -0,0 +1,98 @@ +/****************************************************************************** + * + * Module Name: utuuid -- UUID support functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_COMPILER +ACPI_MODULE_NAME("utuuid") + +#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP) +/* + * UUID support functions. + * + * This table is used to convert an input UUID ascii string to a 16 byte + * buffer and the reverse. The table maps a UUID buffer index 0-15 to + * the index within the 36-byte UUID string where the associated 2-byte + * hex value can be found. + * + * 36-byte UUID strings are of the form: + * aabbccdd-eeff-gghh-iijj-kkllmmnnoopp + * Where aa-pp are one byte hex numbers, made up of two hex digits + * + * Note: This table is basically the inverse of the string-to-offset table + * found in the ACPI spec in the description of the to_UUID macro. + */ +const u8 acpi_gbl_map_to_uuid_offset[UUID_BUFFER_LENGTH] = { + 6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34 +}; + +/******************************************************************************* + * + * FUNCTION: acpi_ut_convert_string_to_uuid + * + * PARAMETERS: in_string - 36-byte formatted UUID string + * uuid_buffer - Where the 16-byte UUID buffer is returned + * + * RETURN: None. Output data is returned in the uuid_buffer + * + * DESCRIPTION: Convert a 36-byte formatted UUID string to 16-byte UUID buffer + * + ******************************************************************************/ + +void acpi_ut_convert_string_to_uuid(char *in_string, u8 *uuid_buffer) +{ + u32 i; + + for (i = 0; i < UUID_BUFFER_LENGTH; i++) { + uuid_buffer[i] = + (acpi_ut_ascii_char_to_hex + (in_string[acpi_gbl_map_to_uuid_offset[i]]) << 4); + + uuid_buffer[i] |= + acpi_ut_ascii_char_to_hex(in_string + [acpi_gbl_map_to_uuid_offset[i] + + 1]); + } +} +#endif diff --git a/kernel/drivers/acpi/acpica/utxface.c b/kernel/drivers/acpi/acpica/utxface.c new file mode 100644 index 000000000..0929187bd --- /dev/null +++ b/kernel/drivers/acpi/acpica/utxface.c @@ -0,0 +1,583 @@ +/****************************************************************************** + * + * Module Name: utxface - External interfaces, miscellaneous utility functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utxface") + +/******************************************************************************* + * + * FUNCTION: acpi_terminate + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Shutdown the ACPICA subsystem and release all resources. + * + ******************************************************************************/ +acpi_status __init acpi_terminate(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_terminate); + + /* Just exit if subsystem is already shutdown */ + + if (acpi_gbl_shutdown) { + ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated")); + return_ACPI_STATUS(AE_OK); + } + + /* Subsystem appears active, go ahead and shut it down */ + + acpi_gbl_shutdown = TRUE; + acpi_gbl_startup_flags = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); + + /* Terminate the AML Debugger if present */ + + ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = TRUE); + + /* Shutdown and free all resources */ + + acpi_ut_subsystem_shutdown(); + + /* Free the mutex objects */ + + acpi_ut_mutex_terminate(); + +#ifdef ACPI_DEBUGGER + + /* Shut down the debugger */ + + acpi_db_terminate(); +#endif + + /* Now we can shutdown the OS-dependent layer */ + + status = acpi_os_terminate(); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_terminate) + +#ifndef ACPI_ASL_COMPILER +#ifdef ACPI_FUTURE_USAGE +/******************************************************************************* + * + * FUNCTION: acpi_subsystem_status + * + * PARAMETERS: None + * + * RETURN: Status of the ACPI subsystem + * + * DESCRIPTION: Other drivers that use the ACPI subsystem should call this + * before making any other calls, to ensure the subsystem + * initialized successfully. + * + ******************************************************************************/ +acpi_status acpi_subsystem_status(void) +{ + + if (acpi_gbl_startup_flags & ACPI_INITIALIZED_OK) { + return (AE_OK); + } else { + return (AE_ERROR); + } +} + +ACPI_EXPORT_SYMBOL(acpi_subsystem_status) + +/******************************************************************************* + * + * FUNCTION: acpi_get_system_info + * + * PARAMETERS: out_buffer - A buffer to receive the resources for the + * device + * + * RETURN: status - the status of the call + * + * DESCRIPTION: This function is called to get information about the current + * state of the ACPI subsystem. It will return system information + * in the out_buffer. + * + * If the function fails an appropriate status will be returned + * and the value of out_buffer is undefined. + * + ******************************************************************************/ +acpi_status acpi_get_system_info(struct acpi_buffer * out_buffer) +{ + struct acpi_system_info *info_ptr; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_get_system_info); + + /* Parameter validation */ + + status = acpi_ut_validate_buffer(out_buffer); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Validate/Allocate/Clear caller buffer */ + + status = + acpi_ut_initialize_buffer(out_buffer, + sizeof(struct acpi_system_info)); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Populate the return buffer + */ + info_ptr = (struct acpi_system_info *)out_buffer->pointer; + + info_ptr->acpi_ca_version = ACPI_CA_VERSION; + + /* System flags (ACPI capabilities) */ + + info_ptr->flags = ACPI_SYS_MODE_ACPI; + + /* Timer resolution - 24 or 32 bits */ + + if (acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER) { + info_ptr->timer_resolution = 24; + } else { + info_ptr->timer_resolution = 32; + } + + /* Clear the reserved fields */ + + info_ptr->reserved1 = 0; + info_ptr->reserved2 = 0; + + /* Current debug levels */ + + info_ptr->debug_layer = acpi_dbg_layer; + info_ptr->debug_level = acpi_dbg_level; + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_get_system_info) + +/******************************************************************************* + * + * FUNCTION: acpi_get_statistics + * + * PARAMETERS: stats - Where the statistics are returned + * + * RETURN: status - the status of the call + * + * DESCRIPTION: Get the contents of the various system counters + * + ******************************************************************************/ +acpi_status acpi_get_statistics(struct acpi_statistics *stats) +{ + ACPI_FUNCTION_TRACE(acpi_get_statistics); + + /* Parameter validation */ + + if (!stats) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Various interrupt-based event counters */ + + stats->sci_count = acpi_sci_count; + stats->gpe_count = acpi_gpe_count; + + ACPI_MEMCPY(stats->fixed_event_count, acpi_fixed_event_count, + sizeof(acpi_fixed_event_count)); + + /* Other counters */ + + stats->method_count = acpi_method_count; + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_get_statistics) + +/***************************************************************************** + * + * FUNCTION: acpi_install_initialization_handler + * + * PARAMETERS: handler - Callback procedure + * function - Not (currently) used, see below + * + * RETURN: Status + * + * DESCRIPTION: Install an initialization handler + * + * TBD: When a second function is added, must save the Function also. + * + ****************************************************************************/ +acpi_status +acpi_install_initialization_handler(acpi_init_handler handler, u32 function) +{ + + if (!handler) { + return (AE_BAD_PARAMETER); + } + + if (acpi_gbl_init_handler) { + return (AE_ALREADY_EXISTS); + } + + acpi_gbl_init_handler = handler; + return (AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_install_initialization_handler) +#endif /* ACPI_FUTURE_USAGE */ + +/***************************************************************************** + * + * FUNCTION: acpi_purge_cached_objects + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Empty all caches (delete the cached objects) + * + ****************************************************************************/ +acpi_status acpi_purge_cached_objects(void) +{ + ACPI_FUNCTION_TRACE(acpi_purge_cached_objects); + + (void)acpi_os_purge_cache(acpi_gbl_state_cache); + (void)acpi_os_purge_cache(acpi_gbl_operand_cache); + (void)acpi_os_purge_cache(acpi_gbl_ps_node_cache); + (void)acpi_os_purge_cache(acpi_gbl_ps_node_ext_cache); + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_purge_cached_objects) + +/***************************************************************************** + * + * FUNCTION: acpi_install_interface + * + * PARAMETERS: interface_name - The interface to install + * + * RETURN: Status + * + * DESCRIPTION: Install an _OSI interface to the global list + * + ****************************************************************************/ +acpi_status acpi_install_interface(acpi_string interface_name) +{ + acpi_status status; + struct acpi_interface_info *interface_info; + + /* Parameter validation */ + + if (!interface_name || (ACPI_STRLEN(interface_name) == 0)) { + return (AE_BAD_PARAMETER); + } + + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Check if the interface name is already in the global list */ + + interface_info = acpi_ut_get_interface(interface_name); + if (interface_info) { + /* + * The interface already exists in the list. This is OK if the + * interface has been marked invalid -- just clear the bit. + */ + if (interface_info->flags & ACPI_OSI_INVALID) { + interface_info->flags &= ~ACPI_OSI_INVALID; + status = AE_OK; + } else { + status = AE_ALREADY_EXISTS; + } + } else { + /* New interface name, install into the global list */ + + status = acpi_ut_install_interface(interface_name); + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_interface) + +/***************************************************************************** + * + * FUNCTION: acpi_remove_interface + * + * PARAMETERS: interface_name - The interface to remove + * + * RETURN: Status + * + * DESCRIPTION: Remove an _OSI interface from the global list + * + ****************************************************************************/ +acpi_status acpi_remove_interface(acpi_string interface_name) +{ + acpi_status status; + + /* Parameter validation */ + + if (!interface_name || (ACPI_STRLEN(interface_name) == 0)) { + return (AE_BAD_PARAMETER); + } + + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = acpi_ut_remove_interface(interface_name); + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_remove_interface) + +/***************************************************************************** + * + * FUNCTION: acpi_install_interface_handler + * + * PARAMETERS: handler - The _OSI interface handler to install + * NULL means "remove existing handler" + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for the predefined _OSI ACPI method. + * invoked during execution of the internal implementation of + * _OSI. A NULL handler simply removes any existing handler. + * + ****************************************************************************/ +acpi_status acpi_install_interface_handler(acpi_interface_handler handler) +{ + acpi_status status; + + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (handler && acpi_gbl_interface_handler) { + status = AE_ALREADY_EXISTS; + } else { + acpi_gbl_interface_handler = handler; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_interface_handler) + +/***************************************************************************** + * + * FUNCTION: acpi_update_interfaces + * + * PARAMETERS: action - Actions to be performed during the + * update + * + * RETURN: Status + * + * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor + * string or/and feature group strings. + * + ****************************************************************************/ +acpi_status acpi_update_interfaces(u8 action) +{ + acpi_status status; + + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = acpi_ut_update_interfaces(action); + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + +/***************************************************************************** + * + * FUNCTION: acpi_check_address_range + * + * PARAMETERS: space_id - Address space ID + * address - Start address + * length - Length + * warn - TRUE if warning on overlap desired + * + * RETURN: Count of the number of conflicts detected. + * + * DESCRIPTION: Check if the input address range overlaps any of the + * ASL operation region address ranges. + * + ****************************************************************************/ + +u32 +acpi_check_address_range(acpi_adr_space_type space_id, + acpi_physical_address address, + acpi_size length, u8 warn) +{ + u32 overlaps; + acpi_status status; + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return (0); + } + + overlaps = acpi_ut_check_address_range(space_id, address, + (u32)length, warn); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); + return (overlaps); +} + +ACPI_EXPORT_SYMBOL(acpi_check_address_range) +#endif /* !ACPI_ASL_COMPILER */ +/******************************************************************************* + * + * FUNCTION: acpi_decode_pld_buffer + * + * PARAMETERS: in_buffer - Buffer returned by _PLD method + * length - Length of the in_buffer + * return_buffer - Where the decode buffer is returned + * + * RETURN: Status and the decoded _PLD buffer. User must deallocate + * the buffer via ACPI_FREE. + * + * DESCRIPTION: Decode the bit-packed buffer returned by the _PLD method into + * a local struct that is much more useful to an ACPI driver. + * + ******************************************************************************/ +acpi_status +acpi_decode_pld_buffer(u8 *in_buffer, + acpi_size length, struct acpi_pld_info ** return_buffer) +{ + struct acpi_pld_info *pld_info; + u32 *buffer = ACPI_CAST_PTR(u32, in_buffer); + u32 dword; + + /* Parameter validation */ + + if (!in_buffer || !return_buffer || (length < 16)) { + return (AE_BAD_PARAMETER); + } + + pld_info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pld_info)); + if (!pld_info) { + return (AE_NO_MEMORY); + } + + /* First 32-bit DWord */ + + ACPI_MOVE_32_TO_32(&dword, &buffer[0]); + pld_info->revision = ACPI_PLD_GET_REVISION(&dword); + pld_info->ignore_color = ACPI_PLD_GET_IGNORE_COLOR(&dword); + pld_info->red = ACPI_PLD_GET_RED(&dword); + pld_info->green = ACPI_PLD_GET_GREEN(&dword); + pld_info->blue = ACPI_PLD_GET_BLUE(&dword); + + /* Second 32-bit DWord */ + + ACPI_MOVE_32_TO_32(&dword, &buffer[1]); + pld_info->width = ACPI_PLD_GET_WIDTH(&dword); + pld_info->height = ACPI_PLD_GET_HEIGHT(&dword); + + /* Third 32-bit DWord */ + + ACPI_MOVE_32_TO_32(&dword, &buffer[2]); + pld_info->user_visible = ACPI_PLD_GET_USER_VISIBLE(&dword); + pld_info->dock = ACPI_PLD_GET_DOCK(&dword); + pld_info->lid = ACPI_PLD_GET_LID(&dword); + pld_info->panel = ACPI_PLD_GET_PANEL(&dword); + pld_info->vertical_position = ACPI_PLD_GET_VERTICAL(&dword); + pld_info->horizontal_position = ACPI_PLD_GET_HORIZONTAL(&dword); + pld_info->shape = ACPI_PLD_GET_SHAPE(&dword); + pld_info->group_orientation = ACPI_PLD_GET_ORIENTATION(&dword); + pld_info->group_token = ACPI_PLD_GET_TOKEN(&dword); + pld_info->group_position = ACPI_PLD_GET_POSITION(&dword); + pld_info->bay = ACPI_PLD_GET_BAY(&dword); + + /* Fourth 32-bit DWord */ + + ACPI_MOVE_32_TO_32(&dword, &buffer[3]); + pld_info->ejectable = ACPI_PLD_GET_EJECTABLE(&dword); + pld_info->ospm_eject_required = ACPI_PLD_GET_OSPM_EJECT(&dword); + pld_info->cabinet_number = ACPI_PLD_GET_CABINET(&dword); + pld_info->card_cage_number = ACPI_PLD_GET_CARD_CAGE(&dword); + pld_info->reference = ACPI_PLD_GET_REFERENCE(&dword); + pld_info->rotation = ACPI_PLD_GET_ROTATION(&dword); + pld_info->order = ACPI_PLD_GET_ORDER(&dword); + + if (length >= ACPI_PLD_BUFFER_SIZE) { + + /* Fifth 32-bit DWord (Revision 2 of _PLD) */ + + ACPI_MOVE_32_TO_32(&dword, &buffer[4]); + pld_info->vertical_offset = ACPI_PLD_GET_VERT_OFFSET(&dword); + pld_info->horizontal_offset = ACPI_PLD_GET_HORIZ_OFFSET(&dword); + } + + *return_buffer = pld_info; + return (AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_decode_pld_buffer) diff --git a/kernel/drivers/acpi/acpica/utxferror.c b/kernel/drivers/acpi/acpica/utxferror.c new file mode 100644 index 000000000..306e785f9 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utxferror.c @@ -0,0 +1,253 @@ +/******************************************************************************* + * + * Module Name: utxferror - Various error/warning output functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utxferror") + +/* + * This module is used for the in-kernel ACPICA as well as the ACPICA + * tools/applications. + */ +#ifndef ACPI_NO_ERROR_MESSAGES /* Entire module */ +/******************************************************************************* + * + * FUNCTION: acpi_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Error" message with module/line/version info + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_error(const char *module_name, u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_error) + +/******************************************************************************* + * + * FUNCTION: acpi_exception + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * status - Status to be formatted + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Exception" message with module/line/version info + * and decoded acpi_status. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_exception(const char *module_name, + u32 line_number, acpi_status status, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ", + acpi_format_exception(status)); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_exception) + +/******************************************************************************* + * + * FUNCTION: acpi_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Warning" message with module/line/version info + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_warning(const char *module_name, u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_WARNING); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_warning) + +/******************************************************************************* + * + * FUNCTION: acpi_info + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print generic "ACPI:" information message. There is no + * module/line/version info in order to keep the message simple. + * + * TBD: module_name and line_number args are not needed, should be removed. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_info(const char *module_name, u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_INFO); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + acpi_os_printf("\n"); + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_info) + +/******************************************************************************* + * + * FUNCTION: acpi_bios_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Firmware Error" message with module/line/version + * info + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_bios_error(const char *module_name, + u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_BIOS_ERROR); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_bios_error) + +/******************************************************************************* + * + * FUNCTION: acpi_bios_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Print "ACPI Firmware Warning" message with module/line/version + * info + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_bios_warning(const char *module_name, + u32 line_number, const char *format, ...) +{ + va_list arg_list; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_BIOS_WARNING); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); + + ACPI_MSG_REDIRECT_END; +} + +ACPI_EXPORT_SYMBOL(acpi_bios_warning) +#endif /* ACPI_NO_ERROR_MESSAGES */ diff --git a/kernel/drivers/acpi/acpica/utxfinit.c b/kernel/drivers/acpi/acpica/utxfinit.c new file mode 100644 index 000000000..083a76891 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utxfinit.c @@ -0,0 +1,340 @@ +/****************************************************************************** + * + * Module Name: utxfinit - External interfaces for ACPICA initialization + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#define EXPORT_ACPI_INTERFACES + +#include +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "acdebug.h" +#include "actables.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utxfinit") + +/* For acpi_exec only */ +void ae_do_object_overrides(void); + +/******************************************************************************* + * + * FUNCTION: acpi_initialize_subsystem + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initializes all global variables. This is the first function + * called, so any early initialization belongs here. + * + ******************************************************************************/ + +acpi_status __init acpi_initialize_subsystem(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_initialize_subsystem); + + acpi_gbl_startup_flags = ACPI_SUBSYSTEM_INITIALIZE; + ACPI_DEBUG_EXEC(acpi_ut_init_stack_ptr_trace()); + + /* Initialize the OS-Dependent layer */ + + status = acpi_os_initialize(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "During OSL initialization")); + return_ACPI_STATUS(status); + } + + /* Initialize all globals used by the subsystem */ + + status = acpi_ut_init_globals(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During initialization of globals")); + return_ACPI_STATUS(status); + } + + /* Create the default mutex objects */ + + status = acpi_ut_mutex_initialize(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During Global Mutex creation")); + return_ACPI_STATUS(status); + } + + /* + * Initialize the namespace manager and + * the root of the namespace tree + */ + status = acpi_ns_root_initialize(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During Namespace initialization")); + return_ACPI_STATUS(status); + } + + /* Initialize the global OSI interfaces list with the static names */ + + status = acpi_ut_initialize_interfaces(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During OSI interfaces initialization")); + return_ACPI_STATUS(status); + } + + /* If configured, initialize the AML debugger */ + +#ifdef ACPI_DEBUGGER + status = acpi_db_initialize(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "During Debugger initialization")); + return_ACPI_STATUS(status); + } +#endif + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_subsystem) + +/******************************************************************************* + * + * FUNCTION: acpi_enable_subsystem + * + * PARAMETERS: flags - Init/enable Options + * + * RETURN: Status + * + * DESCRIPTION: Completes the subsystem initialization including hardware. + * Puts system into ACPI mode if it isn't already. + * + ******************************************************************************/ +acpi_status __init acpi_enable_subsystem(u32 flags) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(acpi_enable_subsystem); + +#if (!ACPI_REDUCED_HARDWARE) + + /* Enable ACPI mode */ + + if (!(flags & ACPI_NO_ACPI_ENABLE)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Init] Going into ACPI mode\n")); + + acpi_gbl_original_mode = acpi_hw_get_mode(); + + status = acpi_enable(); + if (ACPI_FAILURE(status)) { + ACPI_WARNING((AE_INFO, "AcpiEnable failed")); + return_ACPI_STATUS(status); + } + } + + /* + * Obtain a permanent mapping for the FACS. This is required for the + * Global Lock and the Firmware Waking Vector + */ + status = acpi_tb_initialize_facs(); + if (ACPI_FAILURE(status)) { + ACPI_WARNING((AE_INFO, "Could not map the FACS table")); + return_ACPI_STATUS(status); + } +#endif /* !ACPI_REDUCED_HARDWARE */ + + /* + * Install the default op_region handlers. These are installed unless + * other handlers have already been installed via the + * install_address_space_handler interface. + */ + if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Init] Installing default address space handlers\n")); + + status = acpi_ev_install_region_handlers(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } +#if (!ACPI_REDUCED_HARDWARE) + /* + * Initialize ACPI Event handling (Fixed and General Purpose) + * + * Note1: We must have the hardware and events initialized before we can + * execute any control methods safely. Any control method can require + * ACPI hardware support, so the hardware must be fully initialized before + * any method execution! + * + * Note2: Fixed events are initialized and enabled here. GPEs are + * initialized, but cannot be enabled until after the hardware is + * completely initialized (SCI and global_lock activated) and the various + * initialization control methods are run (_REG, _STA, _INI) on the + * entire namespace. + */ + if (!(flags & ACPI_NO_EVENT_INIT)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Init] Initializing ACPI events\n")); + + status = acpi_ev_initialize_events(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* + * Install the SCI handler and Global Lock handler. This completes the + * hardware initialization. + */ + if (!(flags & ACPI_NO_HANDLER_INIT)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Init] Installing SCI/GL handlers\n")); + + status = acpi_ev_install_xrupt_handlers(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } +#endif /* !ACPI_REDUCED_HARDWARE */ + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_enable_subsystem) + +/******************************************************************************* + * + * FUNCTION: acpi_initialize_objects + * + * PARAMETERS: flags - Init/enable Options + * + * RETURN: Status + * + * DESCRIPTION: Completes namespace initialization by initializing device + * objects and executing AML code for Regions, buffers, etc. + * + ******************************************************************************/ +acpi_status __init acpi_initialize_objects(u32 flags) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(acpi_initialize_objects); + + /* + * Run all _REG methods + * + * Note: Any objects accessed by the _REG methods will be automatically + * initialized, even if they contain executable AML (see the call to + * acpi_ns_initialize_objects below). + */ + if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Init] Executing _REG OpRegion methods\n")); + + status = acpi_ev_initialize_op_regions(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } +#ifdef ACPI_EXEC_APP + /* + * This call implements the "initialization file" option for acpi_exec. + * This is the precise point that we want to perform the overrides. + */ + ae_do_object_overrides(); +#endif + + /* + * Execute any module-level code that was detected during the table load + * phase. Although illegal since ACPI 2.0, there are many machines that + * contain this type of code. Each block of detected executable AML code + * outside of any control method is wrapped with a temporary control + * method object and placed on a global list. The methods on this list + * are executed below. + */ + acpi_ns_exec_module_code_list(); + + /* + * Initialize the objects that remain uninitialized. This runs the + * executable AML that may be part of the declaration of these objects: + * operation_regions, buffer_fields, Buffers, and Packages. + */ + if (!(flags & ACPI_NO_OBJECT_INIT)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Init] Completing Initialization of ACPI Objects\n")); + + status = acpi_ns_initialize_objects(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* + * Initialize all device objects in the namespace. This runs the device + * _STA and _INI methods. + */ + if (!(flags & ACPI_NO_DEVICE_INIT)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "[Init] Initializing ACPI Devices\n")); + + status = acpi_ns_initialize_devices(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } + + /* + * Empty the caches (delete the cached objects) on the assumption that + * the table load filled them up more than they will be at runtime -- + * thus wasting non-paged memory. + */ + status = acpi_purge_cached_objects(); + + acpi_gbl_startup_flags |= ACPI_INITIALIZED_OK; + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_objects) diff --git a/kernel/drivers/acpi/acpica/utxfmutex.c b/kernel/drivers/acpi/acpica/utxfmutex.c new file mode 100644 index 000000000..f2606af33 --- /dev/null +++ b/kernel/drivers/acpi/acpica/utxfmutex.c @@ -0,0 +1,187 @@ +/******************************************************************************* + * + * Module Name: utxfmutex - external AML mutex access functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utxfmutex") + +/* Local prototypes */ +static acpi_status +acpi_ut_get_mutex_object(acpi_handle handle, + acpi_string pathname, + union acpi_operand_object **ret_obj); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_mutex_object + * + * PARAMETERS: handle - Mutex or prefix handle (optional) + * pathname - Mutex pathname (optional) + * ret_obj - Where the mutex object is returned + * + * RETURN: Status + * + * DESCRIPTION: Get an AML mutex object. The mutex node is pointed to by + * Handle:Pathname. Either Handle or Pathname can be NULL, but + * not both. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_get_mutex_object(acpi_handle handle, + acpi_string pathname, + union acpi_operand_object **ret_obj) +{ + struct acpi_namespace_node *mutex_node; + union acpi_operand_object *mutex_obj; + acpi_status status; + + /* Parameter validation */ + + if (!ret_obj || (!handle && !pathname)) { + return (AE_BAD_PARAMETER); + } + + /* Get a the namespace node for the mutex */ + + mutex_node = handle; + if (pathname != NULL) { + status = acpi_get_handle(handle, pathname, + ACPI_CAST_PTR(acpi_handle, + &mutex_node)); + if (ACPI_FAILURE(status)) { + return (status); + } + } + + /* Ensure that we actually have a Mutex object */ + + if (!mutex_node || (mutex_node->type != ACPI_TYPE_MUTEX)) { + return (AE_TYPE); + } + + /* Get the low-level mutex object */ + + mutex_obj = acpi_ns_get_attached_object(mutex_node); + if (!mutex_obj) { + return (AE_NULL_OBJECT); + } + + *ret_obj = mutex_obj; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_acquire_mutex + * + * PARAMETERS: handle - Mutex or prefix handle (optional) + * pathname - Mutex pathname (optional) + * timeout - Max time to wait for the lock (millisec) + * + * RETURN: Status + * + * DESCRIPTION: Acquire an AML mutex. This is a device driver interface to + * AML mutex objects, and allows for transaction locking between + * drivers and AML code. The mutex node is pointed to by + * Handle:Pathname. Either Handle or Pathname can be NULL, but + * not both. + * + ******************************************************************************/ + +acpi_status +acpi_acquire_mutex(acpi_handle handle, acpi_string pathname, u16 timeout) +{ + acpi_status status; + union acpi_operand_object *mutex_obj; + + /* Get the low-level mutex associated with Handle:Pathname */ + + status = acpi_ut_get_mutex_object(handle, pathname, &mutex_obj); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Acquire the OS mutex */ + + status = acpi_os_acquire_mutex(mutex_obj->mutex.os_mutex, timeout); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_release_mutex + * + * PARAMETERS: handle - Mutex or prefix handle (optional) + * pathname - Mutex pathname (optional) + * + * RETURN: Status + * + * DESCRIPTION: Release an AML mutex. This is a device driver interface to + * AML mutex objects, and allows for transaction locking between + * drivers and AML code. The mutex node is pointed to by + * Handle:Pathname. Either Handle or Pathname can be NULL, but + * not both. + * + ******************************************************************************/ + +acpi_status acpi_release_mutex(acpi_handle handle, acpi_string pathname) +{ + acpi_status status; + union acpi_operand_object *mutex_obj; + + /* Get the low-level mutex associated with Handle:Pathname */ + + status = acpi_ut_get_mutex_object(handle, pathname, &mutex_obj); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Release the OS mutex */ + + acpi_os_release_mutex(mutex_obj->mutex.os_mutex); + return (AE_OK); +} diff --git a/kernel/drivers/acpi/apei/Kconfig b/kernel/drivers/acpi/apei/Kconfig new file mode 100644 index 000000000..b0140c8fc --- /dev/null +++ b/kernel/drivers/acpi/apei/Kconfig @@ -0,0 +1,64 @@ +config HAVE_ACPI_APEI + bool + +config HAVE_ACPI_APEI_NMI + bool + +config ACPI_APEI + bool "ACPI Platform Error Interface (APEI)" + select MISC_FILESYSTEMS + select PSTORE + select UEFI_CPER + depends on HAVE_ACPI_APEI + help + APEI allows to report errors (for example from the chipset) + to the operating system. This improves NMI handling + especially. In addition it supports error serialization and + error injection. + +config ACPI_APEI_GHES + bool "APEI Generic Hardware Error Source" + depends on ACPI_APEI + select ACPI_HED + select IRQ_WORK + select GENERIC_ALLOCATOR + help + Generic Hardware Error Source provides a way to report + platform hardware errors (such as that from chipset). It + works in so called "Firmware First" mode, that is, hardware + errors are reported to firmware firstly, then reported to + Linux by firmware. This way, some non-standard hardware + error registers or non-standard hardware link can be checked + by firmware to produce more valuable hardware error + information for Linux. + +config ACPI_APEI_PCIEAER + bool "APEI PCIe AER logging/recovering support" + depends on ACPI_APEI && PCIEAER + help + PCIe AER errors may be reported via APEI firmware first mode. + Turn on this option to enable the corresponding support. + +config ACPI_APEI_MEMORY_FAILURE + bool "APEI memory error recovering support" + depends on ACPI_APEI && MEMORY_FAILURE + help + Memory errors may be reported via APEI firmware first mode. + Turn on this option to enable the memory recovering support. + +config ACPI_APEI_EINJ + tristate "APEI Error INJection (EINJ)" + depends on ACPI_APEI && DEBUG_FS + help + EINJ provides a hardware error injection mechanism, it is + mainly used for debugging and testing the other parts of + APEI and some other RAS features. + +config ACPI_APEI_ERST_DEBUG + tristate "APEI Error Record Serialization Table (ERST) Debug Support" + depends on ACPI_APEI + help + ERST is a way provided by APEI to save and retrieve hardware + error information to and from a persistent store. Enable this + if you want to debugging and testing the ERST kernel support + and firmware implementation. diff --git a/kernel/drivers/acpi/apei/Makefile b/kernel/drivers/acpi/apei/Makefile new file mode 100644 index 000000000..5d575a955 --- /dev/null +++ b/kernel/drivers/acpi/apei/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_ACPI_APEI) += apei.o +obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o +obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o +obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o + +apei-y := apei-base.o hest.o erst.o diff --git a/kernel/drivers/acpi/apei/apei-base.c b/kernel/drivers/acpi/apei/apei-base.c new file mode 100644 index 000000000..a85ac07f3 --- /dev/null +++ b/kernel/drivers/acpi/apei/apei-base.c @@ -0,0 +1,805 @@ +/* + * apei-base.c - ACPI Platform Error Interface (APEI) supporting + * infrastructure + * + * APEI allows to report errors (for example from the chipset) to the + * the operating system. This improves NMI handling especially. In + * addition it supports error serialization and error injection. + * + * For more information about APEI, please refer to ACPI Specification + * version 4.0, chapter 17. + * + * This file has Common functions used by more than one APEI table, + * including framework of interpreter for ERST and EINJ; resource + * management for APEI registers. + * + * Copyright (C) 2009, Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apei-internal.h" + +#define APEI_PFX "APEI: " + +/* + * APEI ERST (Error Record Serialization Table) and EINJ (Error + * INJection) interpreter framework. + */ + +#define APEI_EXEC_PRESERVE_REGISTER 0x1 + +void apei_exec_ctx_init(struct apei_exec_context *ctx, + struct apei_exec_ins_type *ins_table, + u32 instructions, + struct acpi_whea_header *action_table, + u32 entries) +{ + ctx->ins_table = ins_table; + ctx->instructions = instructions; + ctx->action_table = action_table; + ctx->entries = entries; +} +EXPORT_SYMBOL_GPL(apei_exec_ctx_init); + +int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val) +{ + int rc; + + rc = apei_read(val, &entry->register_region); + if (rc) + return rc; + *val >>= entry->register_region.bit_offset; + *val &= entry->mask; + + return 0; +} + +int apei_exec_read_register(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + u64 val = 0; + + rc = __apei_exec_read_register(entry, &val); + if (rc) + return rc; + ctx->value = val; + + return 0; +} +EXPORT_SYMBOL_GPL(apei_exec_read_register); + +int apei_exec_read_register_value(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + + rc = apei_exec_read_register(ctx, entry); + if (rc) + return rc; + ctx->value = (ctx->value == entry->value); + + return 0; +} +EXPORT_SYMBOL_GPL(apei_exec_read_register_value); + +int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val) +{ + int rc; + + val &= entry->mask; + val <<= entry->register_region.bit_offset; + if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { + u64 valr = 0; + rc = apei_read(&valr, &entry->register_region); + if (rc) + return rc; + valr &= ~(entry->mask << entry->register_region.bit_offset); + val |= valr; + } + rc = apei_write(val, &entry->register_region); + + return rc; +} + +int apei_exec_write_register(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + return __apei_exec_write_register(entry, ctx->value); +} +EXPORT_SYMBOL_GPL(apei_exec_write_register); + +int apei_exec_write_register_value(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + + ctx->value = entry->value; + rc = apei_exec_write_register(ctx, entry); + + return rc; +} +EXPORT_SYMBOL_GPL(apei_exec_write_register_value); + +int apei_exec_noop(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + return 0; +} +EXPORT_SYMBOL_GPL(apei_exec_noop); + +/* + * Interpret the specified action. Go through whole action table, + * execute all instructions belong to the action. + */ +int __apei_exec_run(struct apei_exec_context *ctx, u8 action, + bool optional) +{ + int rc = -ENOENT; + u32 i, ip; + struct acpi_whea_header *entry; + apei_exec_ins_func_t run; + + ctx->ip = 0; + + /* + * "ip" is the instruction pointer of current instruction, + * "ctx->ip" specifies the next instruction to executed, + * instruction "run" function may change the "ctx->ip" to + * implement "goto" semantics. + */ +rewind: + ip = 0; + for (i = 0; i < ctx->entries; i++) { + entry = &ctx->action_table[i]; + if (entry->action != action) + continue; + if (ip == ctx->ip) { + if (entry->instruction >= ctx->instructions || + !ctx->ins_table[entry->instruction].run) { + pr_warning(FW_WARN APEI_PFX + "Invalid action table, unknown instruction type: %d\n", + entry->instruction); + return -EINVAL; + } + run = ctx->ins_table[entry->instruction].run; + rc = run(ctx, entry); + if (rc < 0) + return rc; + else if (rc != APEI_EXEC_SET_IP) + ctx->ip++; + } + ip++; + if (ctx->ip < ip) + goto rewind; + } + + return !optional && rc < 0 ? rc : 0; +} +EXPORT_SYMBOL_GPL(__apei_exec_run); + +typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx, + struct acpi_whea_header *entry, + void *data); + +static int apei_exec_for_each_entry(struct apei_exec_context *ctx, + apei_exec_entry_func_t func, + void *data, + int *end) +{ + u8 ins; + int i, rc; + struct acpi_whea_header *entry; + struct apei_exec_ins_type *ins_table = ctx->ins_table; + + for (i = 0; i < ctx->entries; i++) { + entry = ctx->action_table + i; + ins = entry->instruction; + if (end) + *end = i; + if (ins >= ctx->instructions || !ins_table[ins].run) { + pr_warning(FW_WARN APEI_PFX + "Invalid action table, unknown instruction type: %d\n", + ins); + return -EINVAL; + } + rc = func(ctx, entry, data); + if (rc) + return rc; + } + + return 0; +} + +static int pre_map_gar_callback(struct apei_exec_context *ctx, + struct acpi_whea_header *entry, + void *data) +{ + u8 ins = entry->instruction; + + if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) + return apei_map_generic_address(&entry->register_region); + + return 0; +} + +/* + * Pre-map all GARs in action table to make it possible to access them + * in NMI handler. + */ +int apei_exec_pre_map_gars(struct apei_exec_context *ctx) +{ + int rc, end; + + rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback, + NULL, &end); + if (rc) { + struct apei_exec_context ctx_unmap; + memcpy(&ctx_unmap, ctx, sizeof(*ctx)); + ctx_unmap.entries = end; + apei_exec_post_unmap_gars(&ctx_unmap); + } + + return rc; +} +EXPORT_SYMBOL_GPL(apei_exec_pre_map_gars); + +static int post_unmap_gar_callback(struct apei_exec_context *ctx, + struct acpi_whea_header *entry, + void *data) +{ + u8 ins = entry->instruction; + + if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) + apei_unmap_generic_address(&entry->register_region); + + return 0; +} + +/* Post-unmap all GAR in action table. */ +int apei_exec_post_unmap_gars(struct apei_exec_context *ctx) +{ + return apei_exec_for_each_entry(ctx, post_unmap_gar_callback, + NULL, NULL); +} +EXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars); + +/* + * Resource management for GARs in APEI + */ +struct apei_res { + struct list_head list; + unsigned long start; + unsigned long end; +}; + +/* Collect all resources requested, to avoid conflict */ +struct apei_resources apei_resources_all = { + .iomem = LIST_HEAD_INIT(apei_resources_all.iomem), + .ioport = LIST_HEAD_INIT(apei_resources_all.ioport), +}; + +static int apei_res_add(struct list_head *res_list, + unsigned long start, unsigned long size) +{ + struct apei_res *res, *resn, *res_ins = NULL; + unsigned long end = start + size; + + if (end <= start) + return 0; +repeat: + list_for_each_entry_safe(res, resn, res_list, list) { + if (res->start > end || res->end < start) + continue; + else if (end <= res->end && start >= res->start) { + kfree(res_ins); + return 0; + } + list_del(&res->list); + res->start = start = min(res->start, start); + res->end = end = max(res->end, end); + kfree(res_ins); + res_ins = res; + goto repeat; + } + + if (res_ins) + list_add(&res_ins->list, res_list); + else { + res_ins = kmalloc(sizeof(*res), GFP_KERNEL); + if (!res_ins) + return -ENOMEM; + res_ins->start = start; + res_ins->end = end; + list_add(&res_ins->list, res_list); + } + + return 0; +} + +static int apei_res_sub(struct list_head *res_list1, + struct list_head *res_list2) +{ + struct apei_res *res1, *resn1, *res2, *res; + res1 = list_entry(res_list1->next, struct apei_res, list); + resn1 = list_entry(res1->list.next, struct apei_res, list); + while (&res1->list != res_list1) { + list_for_each_entry(res2, res_list2, list) { + if (res1->start >= res2->end || + res1->end <= res2->start) + continue; + else if (res1->end <= res2->end && + res1->start >= res2->start) { + list_del(&res1->list); + kfree(res1); + break; + } else if (res1->end > res2->end && + res1->start < res2->start) { + res = kmalloc(sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + res->start = res2->end; + res->end = res1->end; + res1->end = res2->start; + list_add(&res->list, &res1->list); + resn1 = res; + } else { + if (res1->start < res2->start) + res1->end = res2->start; + else + res1->start = res2->end; + } + } + res1 = resn1; + resn1 = list_entry(resn1->list.next, struct apei_res, list); + } + + return 0; +} + +static void apei_res_clean(struct list_head *res_list) +{ + struct apei_res *res, *resn; + + list_for_each_entry_safe(res, resn, res_list, list) { + list_del(&res->list); + kfree(res); + } +} + +void apei_resources_fini(struct apei_resources *resources) +{ + apei_res_clean(&resources->iomem); + apei_res_clean(&resources->ioport); +} +EXPORT_SYMBOL_GPL(apei_resources_fini); + +static int apei_resources_merge(struct apei_resources *resources1, + struct apei_resources *resources2) +{ + int rc; + struct apei_res *res; + + list_for_each_entry(res, &resources2->iomem, list) { + rc = apei_res_add(&resources1->iomem, res->start, + res->end - res->start); + if (rc) + return rc; + } + list_for_each_entry(res, &resources2->ioport, list) { + rc = apei_res_add(&resources1->ioport, res->start, + res->end - res->start); + if (rc) + return rc; + } + + return 0; +} + +int apei_resources_add(struct apei_resources *resources, + unsigned long start, unsigned long size, + bool iomem) +{ + if (iomem) + return apei_res_add(&resources->iomem, start, size); + else + return apei_res_add(&resources->ioport, start, size); +} +EXPORT_SYMBOL_GPL(apei_resources_add); + +/* + * EINJ has two groups of GARs (EINJ table entry and trigger table + * entry), so common resources are subtracted from the trigger table + * resources before the second requesting. + */ +int apei_resources_sub(struct apei_resources *resources1, + struct apei_resources *resources2) +{ + int rc; + + rc = apei_res_sub(&resources1->iomem, &resources2->iomem); + if (rc) + return rc; + return apei_res_sub(&resources1->ioport, &resources2->ioport); +} +EXPORT_SYMBOL_GPL(apei_resources_sub); + +static int apei_get_res_callback(__u64 start, __u64 size, void *data) +{ + struct apei_resources *resources = data; + return apei_res_add(&resources->iomem, start, size); +} + +static int apei_get_nvs_resources(struct apei_resources *resources) +{ + return acpi_nvs_for_each_region(apei_get_res_callback, resources); +} + +int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, + void *data), void *data); +static int apei_get_arch_resources(struct apei_resources *resources) + +{ + return arch_apei_filter_addr(apei_get_res_callback, resources); +} + +/* + * IO memory/port resource management mechanism is used to check + * whether memory/port area used by GARs conflicts with normal memory + * or IO memory/port of devices. + */ +int apei_resources_request(struct apei_resources *resources, + const char *desc) +{ + struct apei_res *res, *res_bak = NULL; + struct resource *r; + struct apei_resources nvs_resources, arch_res; + int rc; + + rc = apei_resources_sub(resources, &apei_resources_all); + if (rc) + return rc; + + /* + * Some firmware uses ACPI NVS region, that has been marked as + * busy, so exclude it from APEI resources to avoid false + * conflict. + */ + apei_resources_init(&nvs_resources); + rc = apei_get_nvs_resources(&nvs_resources); + if (rc) + goto nvs_res_fini; + rc = apei_resources_sub(resources, &nvs_resources); + if (rc) + goto nvs_res_fini; + + if (arch_apei_filter_addr) { + apei_resources_init(&arch_res); + rc = apei_get_arch_resources(&arch_res); + if (rc) + goto arch_res_fini; + rc = apei_resources_sub(resources, &arch_res); + if (rc) + goto arch_res_fini; + } + + rc = -EINVAL; + list_for_each_entry(res, &resources->iomem, list) { + r = request_mem_region(res->start, res->end - res->start, + desc); + if (!r) { + pr_err(APEI_PFX + "Can not request [mem %#010llx-%#010llx] for %s registers\n", + (unsigned long long)res->start, + (unsigned long long)res->end - 1, desc); + res_bak = res; + goto err_unmap_iomem; + } + } + + list_for_each_entry(res, &resources->ioport, list) { + r = request_region(res->start, res->end - res->start, desc); + if (!r) { + pr_err(APEI_PFX + "Can not request [io %#06llx-%#06llx] for %s registers\n", + (unsigned long long)res->start, + (unsigned long long)res->end - 1, desc); + res_bak = res; + goto err_unmap_ioport; + } + } + + rc = apei_resources_merge(&apei_resources_all, resources); + if (rc) { + pr_err(APEI_PFX "Fail to merge resources!\n"); + goto err_unmap_ioport; + } + + return 0; +err_unmap_ioport: + list_for_each_entry(res, &resources->ioport, list) { + if (res == res_bak) + break; + release_region(res->start, res->end - res->start); + } + res_bak = NULL; +err_unmap_iomem: + list_for_each_entry(res, &resources->iomem, list) { + if (res == res_bak) + break; + release_mem_region(res->start, res->end - res->start); + } +arch_res_fini: + apei_resources_fini(&arch_res); +nvs_res_fini: + apei_resources_fini(&nvs_resources); + return rc; +} +EXPORT_SYMBOL_GPL(apei_resources_request); + +void apei_resources_release(struct apei_resources *resources) +{ + int rc; + struct apei_res *res; + + list_for_each_entry(res, &resources->iomem, list) + release_mem_region(res->start, res->end - res->start); + list_for_each_entry(res, &resources->ioport, list) + release_region(res->start, res->end - res->start); + + rc = apei_resources_sub(&apei_resources_all, resources); + if (rc) + pr_err(APEI_PFX "Fail to sub resources!\n"); +} +EXPORT_SYMBOL_GPL(apei_resources_release); + +static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, + u32 *access_bit_width) +{ + u32 bit_width, bit_offset, access_size_code, space_id; + + bit_width = reg->bit_width; + bit_offset = reg->bit_offset; + access_size_code = reg->access_width; + space_id = reg->space_id; + *paddr = get_unaligned(®->address); + if (!*paddr) { + pr_warning(FW_BUG APEI_PFX + "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n", + *paddr, bit_width, bit_offset, access_size_code, + space_id); + return -EINVAL; + } + + if (access_size_code < 1 || access_size_code > 4) { + pr_warning(FW_BUG APEI_PFX + "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n", + *paddr, bit_width, bit_offset, access_size_code, + space_id); + return -EINVAL; + } + *access_bit_width = 1UL << (access_size_code + 2); + + /* Fixup common BIOS bug */ + if (bit_width == 32 && bit_offset == 0 && (*paddr & 0x03) == 0 && + *access_bit_width < 32) + *access_bit_width = 32; + else if (bit_width == 64 && bit_offset == 0 && (*paddr & 0x07) == 0 && + *access_bit_width < 64) + *access_bit_width = 64; + + if ((bit_width + bit_offset) > *access_bit_width) { + pr_warning(FW_BUG APEI_PFX + "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n", + *paddr, bit_width, bit_offset, access_size_code, + space_id); + return -EINVAL; + } + + if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY && + space_id != ACPI_ADR_SPACE_SYSTEM_IO) { + pr_warning(FW_BUG APEI_PFX + "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n", + *paddr, bit_width, bit_offset, access_size_code, + space_id); + return -EINVAL; + } + + return 0; +} + +int apei_map_generic_address(struct acpi_generic_address *reg) +{ + int rc; + u32 access_bit_width; + u64 address; + + rc = apei_check_gar(reg, &address, &access_bit_width); + if (rc) + return rc; + return acpi_os_map_generic_address(reg); +} +EXPORT_SYMBOL_GPL(apei_map_generic_address); + +/* read GAR in interrupt (including NMI) or process context */ +int apei_read(u64 *val, struct acpi_generic_address *reg) +{ + int rc; + u32 access_bit_width; + u64 address; + acpi_status status; + + rc = apei_check_gar(reg, &address, &access_bit_width); + if (rc) + return rc; + + *val = 0; + switch(reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + status = acpi_os_read_memory((acpi_physical_address) address, + val, access_bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + case ACPI_ADR_SPACE_SYSTEM_IO: + status = acpi_os_read_port(address, (u32 *)val, + access_bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apei_read); + +/* write GAR in interrupt (including NMI) or process context */ +int apei_write(u64 val, struct acpi_generic_address *reg) +{ + int rc; + u32 access_bit_width; + u64 address; + acpi_status status; + + rc = apei_check_gar(reg, &address, &access_bit_width); + if (rc) + return rc; + + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + status = acpi_os_write_memory((acpi_physical_address) address, + val, access_bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + case ACPI_ADR_SPACE_SYSTEM_IO: + status = acpi_os_write_port(address, val, access_bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apei_write); + +static int collect_res_callback(struct apei_exec_context *ctx, + struct acpi_whea_header *entry, + void *data) +{ + struct apei_resources *resources = data; + struct acpi_generic_address *reg = &entry->register_region; + u8 ins = entry->instruction; + u32 access_bit_width; + u64 paddr; + int rc; + + if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)) + return 0; + + rc = apei_check_gar(reg, &paddr, &access_bit_width); + if (rc) + return rc; + + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + return apei_res_add(&resources->iomem, paddr, + access_bit_width / 8); + case ACPI_ADR_SPACE_SYSTEM_IO: + return apei_res_add(&resources->ioport, paddr, + access_bit_width / 8); + default: + return -EINVAL; + } +} + +/* + * Same register may be used by multiple instructions in GARs, so + * resources are collected before requesting. + */ +int apei_exec_collect_resources(struct apei_exec_context *ctx, + struct apei_resources *resources) +{ + return apei_exec_for_each_entry(ctx, collect_res_callback, + resources, NULL); +} +EXPORT_SYMBOL_GPL(apei_exec_collect_resources); + +struct dentry *apei_get_debugfs_dir(void) +{ + static struct dentry *dapei; + + if (!dapei) + dapei = debugfs_create_dir("apei", NULL); + + return dapei; +} +EXPORT_SYMBOL_GPL(apei_get_debugfs_dir); + +int __weak arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, + void *data) +{ + return 1; +} +EXPORT_SYMBOL_GPL(arch_apei_enable_cmcff); + +void __weak arch_apei_report_mem_error(int sev, + struct cper_sec_mem_err *mem_err) +{ +} +EXPORT_SYMBOL_GPL(arch_apei_report_mem_error); + +int apei_osc_setup(void) +{ + static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c"; + acpi_handle handle; + u32 capbuf[3]; + struct acpi_osc_context context = { + .uuid_str = whea_uuid_str, + .rev = 1, + .cap.length = sizeof(capbuf), + .cap.pointer = capbuf, + }; + + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_DWORD] = 1; + capbuf[OSC_CONTROL_DWORD] = 0; + + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)) + || ACPI_FAILURE(acpi_run_osc(handle, &context))) + return -EIO; + else { + kfree(context.ret.pointer); + return 0; + } +} +EXPORT_SYMBOL_GPL(apei_osc_setup); diff --git a/kernel/drivers/acpi/apei/apei-internal.h b/kernel/drivers/acpi/apei/apei-internal.h new file mode 100644 index 000000000..16129c78b --- /dev/null +++ b/kernel/drivers/acpi/apei/apei-internal.h @@ -0,0 +1,143 @@ +/* + * apei-internal.h - ACPI Platform Error Interface internal + * definations. + */ + +#ifndef APEI_INTERNAL_H +#define APEI_INTERNAL_H + +#include +#include + +struct apei_exec_context; + +typedef int (*apei_exec_ins_func_t)(struct apei_exec_context *ctx, + struct acpi_whea_header *entry); + +#define APEI_EXEC_INS_ACCESS_REGISTER 0x0001 + +struct apei_exec_ins_type { + u32 flags; + apei_exec_ins_func_t run; +}; + +struct apei_exec_context { + u32 ip; + u64 value; + u64 var1; + u64 var2; + u64 src_base; + u64 dst_base; + struct apei_exec_ins_type *ins_table; + u32 instructions; + struct acpi_whea_header *action_table; + u32 entries; +}; + +void apei_exec_ctx_init(struct apei_exec_context *ctx, + struct apei_exec_ins_type *ins_table, + u32 instructions, + struct acpi_whea_header *action_table, + u32 entries); + +static inline void apei_exec_ctx_set_input(struct apei_exec_context *ctx, + u64 input) +{ + ctx->value = input; +} + +static inline u64 apei_exec_ctx_get_output(struct apei_exec_context *ctx) +{ + return ctx->value; +} + +int __apei_exec_run(struct apei_exec_context *ctx, u8 action, bool optional); + +static inline int apei_exec_run(struct apei_exec_context *ctx, u8 action) +{ + return __apei_exec_run(ctx, action, 0); +} + +/* It is optional whether the firmware provides the action */ +static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 action) +{ + return __apei_exec_run(ctx, action, 1); +} + +/* Common instruction implementation */ + +/* IP has been set in instruction function */ +#define APEI_EXEC_SET_IP 1 + +int apei_map_generic_address(struct acpi_generic_address *reg); + +static inline void apei_unmap_generic_address(struct acpi_generic_address *reg) +{ + acpi_os_unmap_generic_address(reg); +} + +int apei_read(u64 *val, struct acpi_generic_address *reg); +int apei_write(u64 val, struct acpi_generic_address *reg); + +int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); +int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); +int apei_exec_read_register(struct apei_exec_context *ctx, + struct acpi_whea_header *entry); +int apei_exec_read_register_value(struct apei_exec_context *ctx, + struct acpi_whea_header *entry); +int apei_exec_write_register(struct apei_exec_context *ctx, + struct acpi_whea_header *entry); +int apei_exec_write_register_value(struct apei_exec_context *ctx, + struct acpi_whea_header *entry); +int apei_exec_noop(struct apei_exec_context *ctx, + struct acpi_whea_header *entry); +int apei_exec_pre_map_gars(struct apei_exec_context *ctx); +int apei_exec_post_unmap_gars(struct apei_exec_context *ctx); + +struct apei_resources { + struct list_head iomem; + struct list_head ioport; +}; + +static inline void apei_resources_init(struct apei_resources *resources) +{ + INIT_LIST_HEAD(&resources->iomem); + INIT_LIST_HEAD(&resources->ioport); +} + +void apei_resources_fini(struct apei_resources *resources); +int apei_resources_add(struct apei_resources *resources, + unsigned long start, unsigned long size, + bool iomem); +int apei_resources_sub(struct apei_resources *resources1, + struct apei_resources *resources2); +int apei_resources_request(struct apei_resources *resources, + const char *desc); +void apei_resources_release(struct apei_resources *resources); +int apei_exec_collect_resources(struct apei_exec_context *ctx, + struct apei_resources *resources); + +struct dentry; +struct dentry *apei_get_debugfs_dir(void); + +#define apei_estatus_for_each_section(estatus, section) \ + for (section = (struct acpi_hest_generic_data *)(estatus + 1); \ + (void *)section - (void *)estatus < estatus->data_length; \ + section = (void *)(section+1) + section->error_data_length) + +static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus) +{ + if (estatus->raw_data_length) + return estatus->raw_data_offset + \ + estatus->raw_data_length; + else + return sizeof(*estatus) + estatus->data_length; +} + +void cper_estatus_print(const char *pfx, + const struct acpi_hest_generic_status *estatus); +int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus); +int cper_estatus_check(const struct acpi_hest_generic_status *estatus); + +int apei_osc_setup(void); +#endif diff --git a/kernel/drivers/acpi/apei/einj.c b/kernel/drivers/acpi/apei/einj.c new file mode 100644 index 000000000..a095d4f85 --- /dev/null +++ b/kernel/drivers/acpi/apei/einj.c @@ -0,0 +1,833 @@ +/* + * APEI Error INJection support + * + * EINJ provides a hardware error injection mechanism, this is useful + * for debugging and testing of other APEI and RAS features. + * + * For more information about EINJ, please refer to ACPI Specification + * version 4.0, section 17.5. + * + * Copyright 2009-2010 Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apei-internal.h" + +#define EINJ_PFX "EINJ: " + +#define SPIN_UNIT 100 /* 100ns */ +/* Firmware should respond within 1 milliseconds */ +#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) +#define ACPI5_VENDOR_BIT BIT(31) +#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ + ACPI_EINJ_MEMORY_UNCORRECTABLE | \ + ACPI_EINJ_MEMORY_FATAL) + +/* + * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action. + */ +static int acpi5; + +struct set_error_type_with_address { + u32 type; + u32 vendor_extension; + u32 flags; + u32 apicid; + u64 memory_address; + u64 memory_address_range; + u32 pcie_sbdf; +}; +enum { + SETWA_FLAGS_APICID = 1, + SETWA_FLAGS_MEM = 2, + SETWA_FLAGS_PCIE_SBDF = 4, +}; + +/* + * Vendor extensions for platform specific operations + */ +struct vendor_error_type_extension { + u32 length; + u32 pcie_sbdf; + u16 vendor_id; + u16 device_id; + u8 rev_id; + u8 reserved[3]; +}; + +static u32 notrigger; + +static u32 vendor_flags; +static struct debugfs_blob_wrapper vendor_blob; +static char vendor_dev[64]; + +/* + * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the + * EINJ table through an unpublished extension. Use with caution as + * most will ignore the parameter and make their own choice of address + * for error injection. This extension is used only if + * param_extension module parameter is specified. + */ +struct einj_parameter { + u64 type; + u64 reserved1; + u64 reserved2; + u64 param1; + u64 param2; +}; + +#define EINJ_OP_BUSY 0x1 +#define EINJ_STATUS_SUCCESS 0x0 +#define EINJ_STATUS_FAIL 0x1 +#define EINJ_STATUS_INVAL 0x2 + +#define EINJ_TAB_ENTRY(tab) \ + ((struct acpi_whea_header *)((char *)(tab) + \ + sizeof(struct acpi_table_einj))) + +static bool param_extension; +module_param(param_extension, bool, 0); + +static struct acpi_table_einj *einj_tab; + +static struct apei_resources einj_resources; + +static struct apei_exec_ins_type einj_ins_type[] = { + [ACPI_EINJ_READ_REGISTER] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_read_register, + }, + [ACPI_EINJ_READ_REGISTER_VALUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_read_register_value, + }, + [ACPI_EINJ_WRITE_REGISTER] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_write_register, + }, + [ACPI_EINJ_WRITE_REGISTER_VALUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_write_register_value, + }, + [ACPI_EINJ_NOOP] = { + .flags = 0, + .run = apei_exec_noop, + }, +}; + +/* + * Prevent EINJ interpreter to run simultaneously, because the + * corresponding firmware implementation may not work properly when + * invoked simultaneously. + */ +static DEFINE_MUTEX(einj_mutex); + +static void *einj_param; + +static void einj_exec_ctx_init(struct apei_exec_context *ctx) +{ + apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type), + EINJ_TAB_ENTRY(einj_tab), einj_tab->entries); +} + +static int __einj_get_available_error_type(u32 *type) +{ + struct apei_exec_context ctx; + int rc; + + einj_exec_ctx_init(&ctx); + rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE); + if (rc) + return rc; + *type = apei_exec_ctx_get_output(&ctx); + + return 0; +} + +/* Get error injection capabilities of the platform */ +static int einj_get_available_error_type(u32 *type) +{ + int rc; + + mutex_lock(&einj_mutex); + rc = __einj_get_available_error_type(type); + mutex_unlock(&einj_mutex); + + return rc; +} + +static int einj_timedout(u64 *t) +{ + if ((s64)*t < SPIN_UNIT) { + pr_warning(FW_WARN EINJ_PFX + "Firmware does not respond in time\n"); + return 1; + } + *t -= SPIN_UNIT; + ndelay(SPIN_UNIT); + touch_nmi_watchdog(); + return 0; +} + +static void check_vendor_extension(u64 paddr, + struct set_error_type_with_address *v5param) +{ + int offset = v5param->vendor_extension; + struct vendor_error_type_extension *v; + u32 sbdf; + + if (!offset) + return; + v = acpi_os_map_iomem(paddr + offset, sizeof(*v)); + if (!v) + return; + sbdf = v->pcie_sbdf; + sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n", + sbdf >> 24, (sbdf >> 16) & 0xff, + (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, + v->vendor_id, v->device_id, v->rev_id); + acpi_os_unmap_iomem(v, sizeof(*v)); +} + +static void *einj_get_parameter_address(void) +{ + int i; + u64 pa_v4 = 0, pa_v5 = 0; + struct acpi_whea_header *entry; + + entry = EINJ_TAB_ENTRY(einj_tab); + for (i = 0; i < einj_tab->entries; i++) { + if (entry->action == ACPI_EINJ_SET_ERROR_TYPE && + entry->instruction == ACPI_EINJ_WRITE_REGISTER && + entry->register_region.space_id == + ACPI_ADR_SPACE_SYSTEM_MEMORY) + pa_v4 = get_unaligned(&entry->register_region.address); + if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS && + entry->instruction == ACPI_EINJ_WRITE_REGISTER && + entry->register_region.space_id == + ACPI_ADR_SPACE_SYSTEM_MEMORY) + pa_v5 = get_unaligned(&entry->register_region.address); + entry++; + } + if (pa_v5) { + struct set_error_type_with_address *v5param; + + v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param)); + if (v5param) { + acpi5 = 1; + check_vendor_extension(pa_v5, v5param); + return v5param; + } + } + if (param_extension && pa_v4) { + struct einj_parameter *v4param; + + v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param)); + if (!v4param) + return NULL; + if (v4param->reserved1 || v4param->reserved2) { + acpi_os_unmap_iomem(v4param, sizeof(*v4param)); + return NULL; + } + return v4param; + } + + return NULL; +} + +/* do sanity check to trigger table */ +static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) +{ + if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger)) + return -EINVAL; + if (trigger_tab->table_size > PAGE_SIZE || + trigger_tab->table_size < trigger_tab->header_size) + return -EINVAL; + if (trigger_tab->entry_count != + (trigger_tab->table_size - trigger_tab->header_size) / + sizeof(struct acpi_einj_entry)) + return -EINVAL; + + return 0; +} + +static struct acpi_generic_address *einj_get_trigger_parameter_region( + struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2) +{ + int i; + struct acpi_whea_header *entry; + + entry = (struct acpi_whea_header *) + ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); + for (i = 0; i < trigger_tab->entry_count; i++) { + if (entry->action == ACPI_EINJ_TRIGGER_ERROR && + entry->instruction == ACPI_EINJ_WRITE_REGISTER_VALUE && + entry->register_region.space_id == + ACPI_ADR_SPACE_SYSTEM_MEMORY && + (entry->register_region.address & param2) == (param1 & param2)) + return &entry->register_region; + entry++; + } + + return NULL; +} +/* Execute instructions in trigger error action table */ +static int __einj_error_trigger(u64 trigger_paddr, u32 type, + u64 param1, u64 param2) +{ + struct acpi_einj_trigger *trigger_tab = NULL; + struct apei_exec_context trigger_ctx; + struct apei_resources trigger_resources; + struct acpi_whea_header *trigger_entry; + struct resource *r; + u32 table_size; + int rc = -EIO; + struct acpi_generic_address *trigger_param_region = NULL; + + r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), + "APEI EINJ Trigger Table"); + if (!r) { + pr_err(EINJ_PFX + "Can not request [mem %#010llx-%#010llx] for Trigger table\n", + (unsigned long long)trigger_paddr, + (unsigned long long)trigger_paddr + + sizeof(*trigger_tab) - 1); + goto out; + } + trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); + if (!trigger_tab) { + pr_err(EINJ_PFX "Failed to map trigger table!\n"); + goto out_rel_header; + } + rc = einj_check_trigger_header(trigger_tab); + if (rc) { + pr_warning(FW_BUG EINJ_PFX + "The trigger error action table is invalid\n"); + goto out_rel_header; + } + + /* No action structures in the TRIGGER_ERROR table, nothing to do */ + if (!trigger_tab->entry_count) + goto out_rel_header; + + rc = -EIO; + table_size = trigger_tab->table_size; + r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), + table_size - sizeof(*trigger_tab), + "APEI EINJ Trigger Table"); + if (!r) { + pr_err(EINJ_PFX +"Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", + (unsigned long long)trigger_paddr + sizeof(*trigger_tab), + (unsigned long long)trigger_paddr + table_size - 1); + goto out_rel_header; + } + iounmap(trigger_tab); + trigger_tab = ioremap_cache(trigger_paddr, table_size); + if (!trigger_tab) { + pr_err(EINJ_PFX "Failed to map trigger table!\n"); + goto out_rel_entry; + } + trigger_entry = (struct acpi_whea_header *) + ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); + apei_resources_init(&trigger_resources); + apei_exec_ctx_init(&trigger_ctx, einj_ins_type, + ARRAY_SIZE(einj_ins_type), + trigger_entry, trigger_tab->entry_count); + rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources); + if (rc) + goto out_fini; + rc = apei_resources_sub(&trigger_resources, &einj_resources); + if (rc) + goto out_fini; + /* + * Some firmware will access target address specified in + * param1 to trigger the error when injecting memory error. + * This will cause resource conflict with regular memory. So + * remove it from trigger table resources. + */ + if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) { + struct apei_resources addr_resources; + apei_resources_init(&addr_resources); + trigger_param_region = einj_get_trigger_parameter_region( + trigger_tab, param1, param2); + if (trigger_param_region) { + rc = apei_resources_add(&addr_resources, + trigger_param_region->address, + trigger_param_region->bit_width/8, true); + if (rc) + goto out_fini; + rc = apei_resources_sub(&trigger_resources, + &addr_resources); + } + apei_resources_fini(&addr_resources); + if (rc) + goto out_fini; + } + rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); + if (rc) + goto out_fini; + rc = apei_exec_pre_map_gars(&trigger_ctx); + if (rc) + goto out_release; + + rc = apei_exec_run(&trigger_ctx, ACPI_EINJ_TRIGGER_ERROR); + + apei_exec_post_unmap_gars(&trigger_ctx); +out_release: + apei_resources_release(&trigger_resources); +out_fini: + apei_resources_fini(&trigger_resources); +out_rel_entry: + release_mem_region(trigger_paddr + sizeof(*trigger_tab), + table_size - sizeof(*trigger_tab)); +out_rel_header: + release_mem_region(trigger_paddr, sizeof(*trigger_tab)); +out: + if (trigger_tab) + iounmap(trigger_tab); + + return rc; +} + +static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, + u64 param3, u64 param4) +{ + struct apei_exec_context ctx; + u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; + int rc; + + einj_exec_ctx_init(&ctx); + + rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION); + if (rc) + return rc; + apei_exec_ctx_set_input(&ctx, type); + if (acpi5) { + struct set_error_type_with_address *v5param = einj_param; + + v5param->type = type; + if (type & ACPI5_VENDOR_BIT) { + switch (vendor_flags) { + case SETWA_FLAGS_APICID: + v5param->apicid = param1; + break; + case SETWA_FLAGS_MEM: + v5param->memory_address = param1; + v5param->memory_address_range = param2; + break; + case SETWA_FLAGS_PCIE_SBDF: + v5param->pcie_sbdf = param1; + break; + } + v5param->flags = vendor_flags; + } else if (flags) { + v5param->flags = flags; + v5param->memory_address = param1; + v5param->memory_address_range = param2; + v5param->apicid = param3; + v5param->pcie_sbdf = param4; + } else { + switch (type) { + case ACPI_EINJ_PROCESSOR_CORRECTABLE: + case ACPI_EINJ_PROCESSOR_UNCORRECTABLE: + case ACPI_EINJ_PROCESSOR_FATAL: + v5param->apicid = param1; + v5param->flags = SETWA_FLAGS_APICID; + break; + case ACPI_EINJ_MEMORY_CORRECTABLE: + case ACPI_EINJ_MEMORY_UNCORRECTABLE: + case ACPI_EINJ_MEMORY_FATAL: + v5param->memory_address = param1; + v5param->memory_address_range = param2; + v5param->flags = SETWA_FLAGS_MEM; + break; + case ACPI_EINJ_PCIX_CORRECTABLE: + case ACPI_EINJ_PCIX_UNCORRECTABLE: + case ACPI_EINJ_PCIX_FATAL: + v5param->pcie_sbdf = param1; + v5param->flags = SETWA_FLAGS_PCIE_SBDF; + break; + } + } + } else { + rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); + if (rc) + return rc; + if (einj_param) { + struct einj_parameter *v4param = einj_param; + v4param->param1 = param1; + v4param->param2 = param2; + } + } + rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); + if (rc) + return rc; + for (;;) { + rc = apei_exec_run(&ctx, ACPI_EINJ_CHECK_BUSY_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + if (!(val & EINJ_OP_BUSY)) + break; + if (einj_timedout(&timeout)) + return -EIO; + } + rc = apei_exec_run(&ctx, ACPI_EINJ_GET_COMMAND_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + if (val != EINJ_STATUS_SUCCESS) + return -EBUSY; + + rc = apei_exec_run(&ctx, ACPI_EINJ_GET_TRIGGER_TABLE); + if (rc) + return rc; + trigger_paddr = apei_exec_ctx_get_output(&ctx); + if (notrigger == 0) { + rc = __einj_error_trigger(trigger_paddr, type, param1, param2); + if (rc) + return rc; + } + rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); + + return rc; +} + +/* Inject the specified hardware error */ +static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, + u64 param3, u64 param4) +{ + int rc; + unsigned long pfn; + + /* If user manually set "flags", make sure it is legal */ + if (flags && (flags & + ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF))) + return -EINVAL; + + /* + * We need extra sanity checks for memory errors. + * Other types leap directly to injection. + */ + + /* ensure param1/param2 existed */ + if (!(param_extension || acpi5)) + goto inject; + + /* ensure injection is memory related */ + if (type & ACPI5_VENDOR_BIT) { + if (vendor_flags != SETWA_FLAGS_MEM) + goto inject; + } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) + goto inject; + + /* + * Disallow crazy address masks that give BIOS leeway to pick + * injection address almost anywhere. Insist on page or + * better granularity and that target address is normal RAM. + */ + pfn = PFN_DOWN(param1 & param2); + if (!page_is_ram(pfn) || ((param2 & PAGE_MASK) != PAGE_MASK)) + return -EINVAL; + +inject: + mutex_lock(&einj_mutex); + rc = __einj_error_inject(type, flags, param1, param2, param3, param4); + mutex_unlock(&einj_mutex); + + return rc; +} + +static u32 error_type; +static u32 error_flags; +static u64 error_param1; +static u64 error_param2; +static u64 error_param3; +static u64 error_param4; +static struct dentry *einj_debug_dir; + +static int available_error_type_show(struct seq_file *m, void *v) +{ + int rc; + u32 available_error_type = 0; + + rc = einj_get_available_error_type(&available_error_type); + if (rc) + return rc; + if (available_error_type & 0x0001) + seq_printf(m, "0x00000001\tProcessor Correctable\n"); + if (available_error_type & 0x0002) + seq_printf(m, "0x00000002\tProcessor Uncorrectable non-fatal\n"); + if (available_error_type & 0x0004) + seq_printf(m, "0x00000004\tProcessor Uncorrectable fatal\n"); + if (available_error_type & 0x0008) + seq_printf(m, "0x00000008\tMemory Correctable\n"); + if (available_error_type & 0x0010) + seq_printf(m, "0x00000010\tMemory Uncorrectable non-fatal\n"); + if (available_error_type & 0x0020) + seq_printf(m, "0x00000020\tMemory Uncorrectable fatal\n"); + if (available_error_type & 0x0040) + seq_printf(m, "0x00000040\tPCI Express Correctable\n"); + if (available_error_type & 0x0080) + seq_printf(m, "0x00000080\tPCI Express Uncorrectable non-fatal\n"); + if (available_error_type & 0x0100) + seq_printf(m, "0x00000100\tPCI Express Uncorrectable fatal\n"); + if (available_error_type & 0x0200) + seq_printf(m, "0x00000200\tPlatform Correctable\n"); + if (available_error_type & 0x0400) + seq_printf(m, "0x00000400\tPlatform Uncorrectable non-fatal\n"); + if (available_error_type & 0x0800) + seq_printf(m, "0x00000800\tPlatform Uncorrectable fatal\n"); + + return 0; +} + +static int available_error_type_open(struct inode *inode, struct file *file) +{ + return single_open(file, available_error_type_show, NULL); +} + +static const struct file_operations available_error_type_fops = { + .open = available_error_type_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int error_type_get(void *data, u64 *val) +{ + *val = error_type; + + return 0; +} + +static int error_type_set(void *data, u64 val) +{ + int rc; + u32 available_error_type = 0; + u32 tval, vendor; + + /* + * Vendor defined types have 0x80000000 bit set, and + * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE + */ + vendor = val & ACPI5_VENDOR_BIT; + tval = val & 0x7fffffff; + + /* Only one error type can be specified */ + if (tval & (tval - 1)) + return -EINVAL; + if (!vendor) { + rc = einj_get_available_error_type(&available_error_type); + if (rc) + return rc; + if (!(val & available_error_type)) + return -EINVAL; + } + error_type = val; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(error_type_fops, error_type_get, + error_type_set, "0x%llx\n"); + +static int error_inject_set(void *data, u64 val) +{ + if (!error_type) + return -EINVAL; + + return einj_error_inject(error_type, error_flags, error_param1, error_param2, + error_param3, error_param4); +} + +DEFINE_SIMPLE_ATTRIBUTE(error_inject_fops, NULL, + error_inject_set, "%llu\n"); + +static int einj_check_table(struct acpi_table_einj *einj_tab) +{ + if ((einj_tab->header_length != + (sizeof(struct acpi_table_einj) - sizeof(einj_tab->header))) + && (einj_tab->header_length != sizeof(struct acpi_table_einj))) + return -EINVAL; + if (einj_tab->header.length < sizeof(struct acpi_table_einj)) + return -EINVAL; + if (einj_tab->entries != + (einj_tab->header.length - sizeof(struct acpi_table_einj)) / + sizeof(struct acpi_einj_entry)) + return -EINVAL; + + return 0; +} + +static int __init einj_init(void) +{ + int rc; + acpi_status status; + struct dentry *fentry; + struct apei_exec_context ctx; + + if (acpi_disabled) + return -ENODEV; + + status = acpi_get_table(ACPI_SIG_EINJ, 0, + (struct acpi_table_header **)&einj_tab); + if (status == AE_NOT_FOUND) + return -ENODEV; + else if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + pr_err(EINJ_PFX "Failed to get table, %s\n", msg); + return -EINVAL; + } + + rc = einj_check_table(einj_tab); + if (rc) { + pr_warning(FW_BUG EINJ_PFX "EINJ table is invalid\n"); + return -EINVAL; + } + + rc = -ENOMEM; + einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir()); + if (!einj_debug_dir) + goto err_cleanup; + fentry = debugfs_create_file("available_error_type", S_IRUSR, + einj_debug_dir, NULL, + &available_error_type_fops); + if (!fentry) + goto err_cleanup; + fentry = debugfs_create_file("error_type", S_IRUSR | S_IWUSR, + einj_debug_dir, NULL, &error_type_fops); + if (!fentry) + goto err_cleanup; + fentry = debugfs_create_file("error_inject", S_IWUSR, + einj_debug_dir, NULL, &error_inject_fops); + if (!fentry) + goto err_cleanup; + + apei_resources_init(&einj_resources); + einj_exec_ctx_init(&ctx); + rc = apei_exec_collect_resources(&ctx, &einj_resources); + if (rc) + goto err_fini; + rc = apei_resources_request(&einj_resources, "APEI EINJ"); + if (rc) + goto err_fini; + rc = apei_exec_pre_map_gars(&ctx); + if (rc) + goto err_release; + + rc = -ENOMEM; + einj_param = einj_get_parameter_address(); + if ((param_extension || acpi5) && einj_param) { + fentry = debugfs_create_x32("flags", S_IRUSR | S_IWUSR, + einj_debug_dir, &error_flags); + if (!fentry) + goto err_unmap; + fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR, + einj_debug_dir, &error_param1); + if (!fentry) + goto err_unmap; + fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR, + einj_debug_dir, &error_param2); + if (!fentry) + goto err_unmap; + fentry = debugfs_create_x64("param3", S_IRUSR | S_IWUSR, + einj_debug_dir, &error_param3); + if (!fentry) + goto err_unmap; + fentry = debugfs_create_x64("param4", S_IRUSR | S_IWUSR, + einj_debug_dir, &error_param4); + if (!fentry) + goto err_unmap; + + fentry = debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR, + einj_debug_dir, ¬rigger); + if (!fentry) + goto err_unmap; + } + + if (vendor_dev[0]) { + vendor_blob.data = vendor_dev; + vendor_blob.size = strlen(vendor_dev); + fentry = debugfs_create_blob("vendor", S_IRUSR, + einj_debug_dir, &vendor_blob); + if (!fentry) + goto err_unmap; + fentry = debugfs_create_x32("vendor_flags", S_IRUSR | S_IWUSR, + einj_debug_dir, &vendor_flags); + if (!fentry) + goto err_unmap; + } + + pr_info(EINJ_PFX "Error INJection is initialized.\n"); + + return 0; + +err_unmap: + if (einj_param) { + acpi_size size = (acpi5) ? + sizeof(struct set_error_type_with_address) : + sizeof(struct einj_parameter); + + acpi_os_unmap_iomem(einj_param, size); + } + apei_exec_post_unmap_gars(&ctx); +err_release: + apei_resources_release(&einj_resources); +err_fini: + apei_resources_fini(&einj_resources); +err_cleanup: + debugfs_remove_recursive(einj_debug_dir); + + return rc; +} + +static void __exit einj_exit(void) +{ + struct apei_exec_context ctx; + + if (einj_param) { + acpi_size size = (acpi5) ? + sizeof(struct set_error_type_with_address) : + sizeof(struct einj_parameter); + + acpi_os_unmap_iomem(einj_param, size); + } + einj_exec_ctx_init(&ctx); + apei_exec_post_unmap_gars(&ctx); + apei_resources_release(&einj_resources); + apei_resources_fini(&einj_resources); + debugfs_remove_recursive(einj_debug_dir); +} + +module_init(einj_init); +module_exit(einj_exit); + +MODULE_AUTHOR("Huang Ying"); +MODULE_DESCRIPTION("APEI Error INJection support"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/apei/erst-dbg.c b/kernel/drivers/acpi/apei/erst-dbg.c new file mode 100644 index 000000000..04ab5c9d3 --- /dev/null +++ b/kernel/drivers/acpi/apei/erst-dbg.c @@ -0,0 +1,243 @@ +/* + * APEI Error Record Serialization Table debug support + * + * ERST is a way provided by APEI to save and retrieve hardware error + * information to and from a persistent store. This file provide the + * debugging/testing support for ERST kernel support and firmware + * implementation. + * + * Copyright 2010 Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "apei-internal.h" + +#define ERST_DBG_PFX "ERST DBG: " + +#define ERST_DBG_RECORD_LEN_MAX 0x4000 + +static void *erst_dbg_buf; +static unsigned int erst_dbg_buf_len; + +/* Prevent erst_dbg_read/write from being invoked concurrently */ +static DEFINE_MUTEX(erst_dbg_mutex); + +static int erst_dbg_open(struct inode *inode, struct file *file) +{ + int rc, *pos; + + if (erst_disable) + return -ENODEV; + + pos = (int *)&file->private_data; + + rc = erst_get_record_id_begin(pos); + if (rc) + return rc; + + return nonseekable_open(inode, file); +} + +static int erst_dbg_release(struct inode *inode, struct file *file) +{ + erst_get_record_id_end(); + + return 0; +} + +static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + int rc; + u64 record_id; + u32 record_count; + + switch (cmd) { + case APEI_ERST_CLEAR_RECORD: + rc = copy_from_user(&record_id, (void __user *)arg, + sizeof(record_id)); + if (rc) + return -EFAULT; + return erst_clear(record_id); + case APEI_ERST_GET_RECORD_COUNT: + rc = erst_get_record_count(); + if (rc < 0) + return rc; + record_count = rc; + rc = put_user(record_count, (u32 __user *)arg); + if (rc) + return rc; + return 0; + default: + return -ENOTTY; + } +} + +static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf, + size_t usize, loff_t *off) +{ + int rc, *pos; + ssize_t len = 0; + u64 id; + + if (*off) + return -EINVAL; + + if (mutex_lock_interruptible(&erst_dbg_mutex) != 0) + return -EINTR; + + pos = (int *)&filp->private_data; + +retry_next: + rc = erst_get_record_id_next(pos, &id); + if (rc) + goto out; + /* no more record */ + if (id == APEI_ERST_INVALID_RECORD_ID) { + /* + * If the persistent store is empty initially, the function + * 'erst_read' below will return "-ENOENT" value. This causes + * 'retry_next' label is entered again. The returned value + * should be zero indicating the read operation is EOF. + */ + len = 0; + + goto out; + } +retry: + rc = len = erst_read(id, erst_dbg_buf, erst_dbg_buf_len); + /* The record may be cleared by others, try read next record */ + if (rc == -ENOENT) + goto retry_next; + if (rc < 0) + goto out; + if (len > ERST_DBG_RECORD_LEN_MAX) { + pr_warning(ERST_DBG_PFX + "Record (ID: 0x%llx) length is too long: %zd\n", + id, len); + rc = -EIO; + goto out; + } + if (len > erst_dbg_buf_len) { + void *p; + rc = -ENOMEM; + p = kmalloc(len, GFP_KERNEL); + if (!p) + goto out; + kfree(erst_dbg_buf); + erst_dbg_buf = p; + erst_dbg_buf_len = len; + goto retry; + } + + rc = -EINVAL; + if (len > usize) + goto out; + + rc = -EFAULT; + if (copy_to_user(ubuf, erst_dbg_buf, len)) + goto out; + rc = 0; +out: + mutex_unlock(&erst_dbg_mutex); + return rc ? rc : len; +} + +static ssize_t erst_dbg_write(struct file *filp, const char __user *ubuf, + size_t usize, loff_t *off) +{ + int rc; + struct cper_record_header *rcd; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (usize > ERST_DBG_RECORD_LEN_MAX) { + pr_err(ERST_DBG_PFX "Too long record to be written\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&erst_dbg_mutex)) + return -EINTR; + if (usize > erst_dbg_buf_len) { + void *p; + rc = -ENOMEM; + p = kmalloc(usize, GFP_KERNEL); + if (!p) + goto out; + kfree(erst_dbg_buf); + erst_dbg_buf = p; + erst_dbg_buf_len = usize; + } + rc = copy_from_user(erst_dbg_buf, ubuf, usize); + if (rc) { + rc = -EFAULT; + goto out; + } + rcd = erst_dbg_buf; + rc = -EINVAL; + if (rcd->record_length != usize) + goto out; + + rc = erst_write(erst_dbg_buf); + +out: + mutex_unlock(&erst_dbg_mutex); + return rc < 0 ? rc : usize; +} + +static const struct file_operations erst_dbg_ops = { + .owner = THIS_MODULE, + .open = erst_dbg_open, + .release = erst_dbg_release, + .read = erst_dbg_read, + .write = erst_dbg_write, + .unlocked_ioctl = erst_dbg_ioctl, + .llseek = no_llseek, +}; + +static struct miscdevice erst_dbg_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "erst_dbg", + .fops = &erst_dbg_ops, +}; + +static __init int erst_dbg_init(void) +{ + if (erst_disable) { + pr_info(ERST_DBG_PFX "ERST support is disabled.\n"); + return -ENODEV; + } + return misc_register(&erst_dbg_dev); +} + +static __exit void erst_dbg_exit(void) +{ + misc_deregister(&erst_dbg_dev); + kfree(erst_dbg_buf); +} + +module_init(erst_dbg_init); +module_exit(erst_dbg_exit); + +MODULE_AUTHOR("Huang Ying"); +MODULE_DESCRIPTION("APEI Error Record Serialization Table debug support"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/apei/erst.c b/kernel/drivers/acpi/apei/erst.c new file mode 100644 index 000000000..ed65e9c4b --- /dev/null +++ b/kernel/drivers/acpi/apei/erst.c @@ -0,0 +1,1230 @@ +/* + * APEI Error Record Serialization Table support + * + * ERST is a way provided by APEI to save and retrieve hardware error + * information to and from a persistent store. + * + * For more information about ERST, please refer to ACPI Specification + * version 4.0, section 17.4. + * + * Copyright 2010 Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apei-internal.h" + +#undef pr_fmt +#define pr_fmt(fmt) "ERST: " fmt + +/* ERST command status */ +#define ERST_STATUS_SUCCESS 0x0 +#define ERST_STATUS_NOT_ENOUGH_SPACE 0x1 +#define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2 +#define ERST_STATUS_FAILED 0x3 +#define ERST_STATUS_RECORD_STORE_EMPTY 0x4 +#define ERST_STATUS_RECORD_NOT_FOUND 0x5 + +#define ERST_TAB_ENTRY(tab) \ + ((struct acpi_whea_header *)((char *)(tab) + \ + sizeof(struct acpi_table_erst))) + +#define SPIN_UNIT 100 /* 100ns */ +/* Firmware should respond within 1 milliseconds */ +#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) +#define FIRMWARE_MAX_STALL 50 /* 50us */ + +int erst_disable; +EXPORT_SYMBOL_GPL(erst_disable); + +static struct acpi_table_erst *erst_tab; + +/* ERST Error Log Address Range atrributes */ +#define ERST_RANGE_RESERVED 0x0001 +#define ERST_RANGE_NVRAM 0x0002 +#define ERST_RANGE_SLOW 0x0004 + +/* + * ERST Error Log Address Range, used as buffer for reading/writing + * error records. + */ +static struct erst_erange { + u64 base; + u64 size; + void __iomem *vaddr; + u32 attr; +} erst_erange; + +/* + * Prevent ERST interpreter to run simultaneously, because the + * corresponding firmware implementation may not work properly when + * invoked simultaneously. + * + * It is used to provide exclusive accessing for ERST Error Log + * Address Range too. + */ +static DEFINE_RAW_SPINLOCK(erst_lock); + +static inline int erst_errno(int command_status) +{ + switch (command_status) { + case ERST_STATUS_SUCCESS: + return 0; + case ERST_STATUS_HARDWARE_NOT_AVAILABLE: + return -ENODEV; + case ERST_STATUS_NOT_ENOUGH_SPACE: + return -ENOSPC; + case ERST_STATUS_RECORD_STORE_EMPTY: + case ERST_STATUS_RECORD_NOT_FOUND: + return -ENOENT; + default: + return -EINVAL; + } +} + +static int erst_timedout(u64 *t, u64 spin_unit) +{ + if ((s64)*t < spin_unit) { + pr_warn(FW_WARN "Firmware does not respond in time.\n"); + return 1; + } + *t -= spin_unit; + ndelay(spin_unit); + touch_nmi_watchdog(); + return 0; +} + +static int erst_exec_load_var1(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + return __apei_exec_read_register(entry, &ctx->var1); +} + +static int erst_exec_load_var2(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + return __apei_exec_read_register(entry, &ctx->var2); +} + +static int erst_exec_store_var1(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + return __apei_exec_write_register(entry, ctx->var1); +} + +static int erst_exec_add(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + ctx->var1 += ctx->var2; + return 0; +} + +static int erst_exec_subtract(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + ctx->var1 -= ctx->var2; + return 0; +} + +static int erst_exec_add_value(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + u64 val; + + rc = __apei_exec_read_register(entry, &val); + if (rc) + return rc; + val += ctx->value; + rc = __apei_exec_write_register(entry, val); + return rc; +} + +static int erst_exec_subtract_value(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + u64 val; + + rc = __apei_exec_read_register(entry, &val); + if (rc) + return rc; + val -= ctx->value; + rc = __apei_exec_write_register(entry, val); + return rc; +} + +static int erst_exec_stall(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + u64 stall_time; + + if (ctx->value > FIRMWARE_MAX_STALL) { + if (!in_nmi()) + pr_warn(FW_WARN + "Too long stall time for stall instruction: 0x%llx.\n", + ctx->value); + stall_time = FIRMWARE_MAX_STALL; + } else + stall_time = ctx->value; + udelay(stall_time); + return 0; +} + +static int erst_exec_stall_while_true(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + u64 val; + u64 timeout = FIRMWARE_TIMEOUT; + u64 stall_time; + + if (ctx->var1 > FIRMWARE_MAX_STALL) { + if (!in_nmi()) + pr_warn(FW_WARN + "Too long stall time for stall while true instruction: 0x%llx.\n", + ctx->var1); + stall_time = FIRMWARE_MAX_STALL; + } else + stall_time = ctx->var1; + + for (;;) { + rc = __apei_exec_read_register(entry, &val); + if (rc) + return rc; + if (val != ctx->value) + break; + if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC)) + return -EIO; + } + return 0; +} + +static int erst_exec_skip_next_instruction_if_true( + struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + u64 val; + + rc = __apei_exec_read_register(entry, &val); + if (rc) + return rc; + if (val == ctx->value) { + ctx->ip += 2; + return APEI_EXEC_SET_IP; + } + + return 0; +} + +static int erst_exec_goto(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + ctx->ip = ctx->value; + return APEI_EXEC_SET_IP; +} + +static int erst_exec_set_src_address_base(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + return __apei_exec_read_register(entry, &ctx->src_base); +} + +static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + return __apei_exec_read_register(entry, &ctx->dst_base); +} + +static int erst_exec_move_data(struct apei_exec_context *ctx, + struct acpi_whea_header *entry) +{ + int rc; + u64 offset; + void *src, *dst; + + /* ioremap does not work in interrupt context */ + if (in_interrupt()) { + pr_warn("MOVE_DATA can not be used in interrupt context.\n"); + return -EBUSY; + } + + rc = __apei_exec_read_register(entry, &offset); + if (rc) + return rc; + + src = ioremap(ctx->src_base + offset, ctx->var2); + if (!src) + return -ENOMEM; + dst = ioremap(ctx->dst_base + offset, ctx->var2); + if (!dst) { + iounmap(src); + return -ENOMEM; + } + + memmove(dst, src, ctx->var2); + + iounmap(src); + iounmap(dst); + + return 0; +} + +static struct apei_exec_ins_type erst_ins_type[] = { + [ACPI_ERST_READ_REGISTER] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_read_register, + }, + [ACPI_ERST_READ_REGISTER_VALUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_read_register_value, + }, + [ACPI_ERST_WRITE_REGISTER] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_write_register, + }, + [ACPI_ERST_WRITE_REGISTER_VALUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = apei_exec_write_register_value, + }, + [ACPI_ERST_NOOP] = { + .flags = 0, + .run = apei_exec_noop, + }, + [ACPI_ERST_LOAD_VAR1] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_load_var1, + }, + [ACPI_ERST_LOAD_VAR2] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_load_var2, + }, + [ACPI_ERST_STORE_VAR1] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_store_var1, + }, + [ACPI_ERST_ADD] = { + .flags = 0, + .run = erst_exec_add, + }, + [ACPI_ERST_SUBTRACT] = { + .flags = 0, + .run = erst_exec_subtract, + }, + [ACPI_ERST_ADD_VALUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_add_value, + }, + [ACPI_ERST_SUBTRACT_VALUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_subtract_value, + }, + [ACPI_ERST_STALL] = { + .flags = 0, + .run = erst_exec_stall, + }, + [ACPI_ERST_STALL_WHILE_TRUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_stall_while_true, + }, + [ACPI_ERST_SKIP_NEXT_IF_TRUE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_skip_next_instruction_if_true, + }, + [ACPI_ERST_GOTO] = { + .flags = 0, + .run = erst_exec_goto, + }, + [ACPI_ERST_SET_SRC_ADDRESS_BASE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_set_src_address_base, + }, + [ACPI_ERST_SET_DST_ADDRESS_BASE] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_set_dst_address_base, + }, + [ACPI_ERST_MOVE_DATA] = { + .flags = APEI_EXEC_INS_ACCESS_REGISTER, + .run = erst_exec_move_data, + }, +}; + +static inline void erst_exec_ctx_init(struct apei_exec_context *ctx) +{ + apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type), + ERST_TAB_ENTRY(erst_tab), erst_tab->entries); +} + +static int erst_get_erange(struct erst_erange *range) +{ + struct apei_exec_context ctx; + int rc; + + erst_exec_ctx_init(&ctx); + rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE); + if (rc) + return rc; + range->base = apei_exec_ctx_get_output(&ctx); + rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH); + if (rc) + return rc; + range->size = apei_exec_ctx_get_output(&ctx); + rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES); + if (rc) + return rc; + range->attr = apei_exec_ctx_get_output(&ctx); + + return 0; +} + +static ssize_t __erst_get_record_count(void) +{ + struct apei_exec_context ctx; + int rc; + + erst_exec_ctx_init(&ctx); + rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT); + if (rc) + return rc; + return apei_exec_ctx_get_output(&ctx); +} + +ssize_t erst_get_record_count(void) +{ + ssize_t count; + unsigned long flags; + + if (erst_disable) + return -ENODEV; + + raw_spin_lock_irqsave(&erst_lock, flags); + count = __erst_get_record_count(); + raw_spin_unlock_irqrestore(&erst_lock, flags); + + return count; +} +EXPORT_SYMBOL_GPL(erst_get_record_count); + +#define ERST_RECORD_ID_CACHE_SIZE_MIN 16 +#define ERST_RECORD_ID_CACHE_SIZE_MAX 1024 + +struct erst_record_id_cache { + struct mutex lock; + u64 *entries; + int len; + int size; + int refcount; +}; + +static struct erst_record_id_cache erst_record_id_cache = { + .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock), + .refcount = 0, +}; + +static int __erst_get_next_record_id(u64 *record_id) +{ + struct apei_exec_context ctx; + int rc; + + erst_exec_ctx_init(&ctx); + rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID); + if (rc) + return rc; + *record_id = apei_exec_ctx_get_output(&ctx); + + return 0; +} + +int erst_get_record_id_begin(int *pos) +{ + int rc; + + if (erst_disable) + return -ENODEV; + + rc = mutex_lock_interruptible(&erst_record_id_cache.lock); + if (rc) + return rc; + erst_record_id_cache.refcount++; + mutex_unlock(&erst_record_id_cache.lock); + + *pos = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(erst_get_record_id_begin); + +/* erst_record_id_cache.lock must be held by caller */ +static int __erst_record_id_cache_add_one(void) +{ + u64 id, prev_id, first_id; + int i, rc; + u64 *entries; + unsigned long flags; + + id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID; +retry: + raw_spin_lock_irqsave(&erst_lock, flags); + rc = __erst_get_next_record_id(&id); + raw_spin_unlock_irqrestore(&erst_lock, flags); + if (rc == -ENOENT) + return 0; + if (rc) + return rc; + if (id == APEI_ERST_INVALID_RECORD_ID) + return 0; + /* can not skip current ID, or loop back to first ID */ + if (id == prev_id || id == first_id) + return 0; + if (first_id == APEI_ERST_INVALID_RECORD_ID) + first_id = id; + prev_id = id; + + entries = erst_record_id_cache.entries; + for (i = 0; i < erst_record_id_cache.len; i++) { + if (entries[i] == id) + break; + } + /* record id already in cache, try next */ + if (i < erst_record_id_cache.len) + goto retry; + if (erst_record_id_cache.len >= erst_record_id_cache.size) { + int new_size, alloc_size; + u64 *new_entries; + + new_size = erst_record_id_cache.size * 2; + new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN, + ERST_RECORD_ID_CACHE_SIZE_MAX); + if (new_size <= erst_record_id_cache.size) { + if (printk_ratelimit()) + pr_warn(FW_WARN "too many record IDs!\n"); + return 0; + } + alloc_size = new_size * sizeof(entries[0]); + if (alloc_size < PAGE_SIZE) + new_entries = kmalloc(alloc_size, GFP_KERNEL); + else + new_entries = vmalloc(alloc_size); + if (!new_entries) + return -ENOMEM; + memcpy(new_entries, entries, + erst_record_id_cache.len * sizeof(entries[0])); + if (erst_record_id_cache.size < PAGE_SIZE) + kfree(entries); + else + vfree(entries); + erst_record_id_cache.entries = entries = new_entries; + erst_record_id_cache.size = new_size; + } + entries[i] = id; + erst_record_id_cache.len++; + + return 1; +} + +/* + * Get the record ID of an existing error record on the persistent + * storage. If there is no error record on the persistent storage, the + * returned record_id is APEI_ERST_INVALID_RECORD_ID. + */ +int erst_get_record_id_next(int *pos, u64 *record_id) +{ + int rc = 0; + u64 *entries; + + if (erst_disable) + return -ENODEV; + + /* must be enclosed by erst_get_record_id_begin/end */ + BUG_ON(!erst_record_id_cache.refcount); + BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len); + + mutex_lock(&erst_record_id_cache.lock); + entries = erst_record_id_cache.entries; + for (; *pos < erst_record_id_cache.len; (*pos)++) + if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID) + break; + /* found next record id in cache */ + if (*pos < erst_record_id_cache.len) { + *record_id = entries[*pos]; + (*pos)++; + goto out_unlock; + } + + /* Try to add one more record ID to cache */ + rc = __erst_record_id_cache_add_one(); + if (rc < 0) + goto out_unlock; + /* successfully add one new ID */ + if (rc == 1) { + *record_id = erst_record_id_cache.entries[*pos]; + (*pos)++; + rc = 0; + } else { + *pos = -1; + *record_id = APEI_ERST_INVALID_RECORD_ID; + } +out_unlock: + mutex_unlock(&erst_record_id_cache.lock); + + return rc; +} +EXPORT_SYMBOL_GPL(erst_get_record_id_next); + +/* erst_record_id_cache.lock must be held by caller */ +static void __erst_record_id_cache_compact(void) +{ + int i, wpos = 0; + u64 *entries; + + if (erst_record_id_cache.refcount) + return; + + entries = erst_record_id_cache.entries; + for (i = 0; i < erst_record_id_cache.len; i++) { + if (entries[i] == APEI_ERST_INVALID_RECORD_ID) + continue; + if (wpos != i) + entries[wpos] = entries[i]; + wpos++; + } + erst_record_id_cache.len = wpos; +} + +void erst_get_record_id_end(void) +{ + /* + * erst_disable != 0 should be detected by invoker via the + * return value of erst_get_record_id_begin/next, so this + * function should not be called for erst_disable != 0. + */ + BUG_ON(erst_disable); + + mutex_lock(&erst_record_id_cache.lock); + erst_record_id_cache.refcount--; + BUG_ON(erst_record_id_cache.refcount < 0); + __erst_record_id_cache_compact(); + mutex_unlock(&erst_record_id_cache.lock); +} +EXPORT_SYMBOL_GPL(erst_get_record_id_end); + +static int __erst_write_to_storage(u64 offset) +{ + struct apei_exec_context ctx; + u64 timeout = FIRMWARE_TIMEOUT; + u64 val; + int rc; + + erst_exec_ctx_init(&ctx); + rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE); + if (rc) + return rc; + apei_exec_ctx_set_input(&ctx, offset); + rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); + if (rc) + return rc; + rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); + if (rc) + return rc; + for (;;) { + rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + if (!val) + break; + if (erst_timedout(&timeout, SPIN_UNIT)) + return -EIO; + } + rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); + if (rc) + return rc; + + return erst_errno(val); +} + +static int __erst_read_from_storage(u64 record_id, u64 offset) +{ + struct apei_exec_context ctx; + u64 timeout = FIRMWARE_TIMEOUT; + u64 val; + int rc; + + erst_exec_ctx_init(&ctx); + rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ); + if (rc) + return rc; + apei_exec_ctx_set_input(&ctx, offset); + rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); + if (rc) + return rc; + apei_exec_ctx_set_input(&ctx, record_id); + rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); + if (rc) + return rc; + rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); + if (rc) + return rc; + for (;;) { + rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + if (!val) + break; + if (erst_timedout(&timeout, SPIN_UNIT)) + return -EIO; + }; + rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); + if (rc) + return rc; + + return erst_errno(val); +} + +static int __erst_clear_from_storage(u64 record_id) +{ + struct apei_exec_context ctx; + u64 timeout = FIRMWARE_TIMEOUT; + u64 val; + int rc; + + erst_exec_ctx_init(&ctx); + rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR); + if (rc) + return rc; + apei_exec_ctx_set_input(&ctx, record_id); + rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); + if (rc) + return rc; + rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); + if (rc) + return rc; + for (;;) { + rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + if (!val) + break; + if (erst_timedout(&timeout, SPIN_UNIT)) + return -EIO; + } + rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); + if (rc) + return rc; + val = apei_exec_ctx_get_output(&ctx); + rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); + if (rc) + return rc; + + return erst_errno(val); +} + +/* NVRAM ERST Error Log Address Range is not supported yet */ +static void pr_unimpl_nvram(void) +{ + if (printk_ratelimit()) + pr_warn("NVRAM ERST Log Address Range not implemented yet.\n"); +} + +static int __erst_write_to_nvram(const struct cper_record_header *record) +{ + /* do not print message, because printk is not safe for NMI */ + return -ENOSYS; +} + +static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset) +{ + pr_unimpl_nvram(); + return -ENOSYS; +} + +static int __erst_clear_from_nvram(u64 record_id) +{ + pr_unimpl_nvram(); + return -ENOSYS; +} + +int erst_write(const struct cper_record_header *record) +{ + int rc; + unsigned long flags; + struct cper_record_header *rcd_erange; + + if (erst_disable) + return -ENODEV; + + if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE)) + return -EINVAL; + + if (erst_erange.attr & ERST_RANGE_NVRAM) { + if (!raw_spin_trylock_irqsave(&erst_lock, flags)) + return -EBUSY; + rc = __erst_write_to_nvram(record); + raw_spin_unlock_irqrestore(&erst_lock, flags); + return rc; + } + + if (record->record_length > erst_erange.size) + return -EINVAL; + + if (!raw_spin_trylock_irqsave(&erst_lock, flags)) + return -EBUSY; + memcpy(erst_erange.vaddr, record, record->record_length); + rcd_erange = erst_erange.vaddr; + /* signature for serialization system */ + memcpy(&rcd_erange->persistence_information, "ER", 2); + + rc = __erst_write_to_storage(0); + raw_spin_unlock_irqrestore(&erst_lock, flags); + + return rc; +} +EXPORT_SYMBOL_GPL(erst_write); + +static int __erst_read_to_erange(u64 record_id, u64 *offset) +{ + int rc; + + if (erst_erange.attr & ERST_RANGE_NVRAM) + return __erst_read_to_erange_from_nvram( + record_id, offset); + + rc = __erst_read_from_storage(record_id, 0); + if (rc) + return rc; + *offset = 0; + + return 0; +} + +static ssize_t __erst_read(u64 record_id, struct cper_record_header *record, + size_t buflen) +{ + int rc; + u64 offset, len = 0; + struct cper_record_header *rcd_tmp; + + rc = __erst_read_to_erange(record_id, &offset); + if (rc) + return rc; + rcd_tmp = erst_erange.vaddr + offset; + len = rcd_tmp->record_length; + if (len <= buflen) + memcpy(record, rcd_tmp, len); + + return len; +} + +/* + * If return value > buflen, the buffer size is not big enough, + * else if return value < 0, something goes wrong, + * else everything is OK, and return value is record length + */ +ssize_t erst_read(u64 record_id, struct cper_record_header *record, + size_t buflen) +{ + ssize_t len; + unsigned long flags; + + if (erst_disable) + return -ENODEV; + + raw_spin_lock_irqsave(&erst_lock, flags); + len = __erst_read(record_id, record, buflen); + raw_spin_unlock_irqrestore(&erst_lock, flags); + return len; +} +EXPORT_SYMBOL_GPL(erst_read); + +int erst_clear(u64 record_id) +{ + int rc, i; + unsigned long flags; + u64 *entries; + + if (erst_disable) + return -ENODEV; + + rc = mutex_lock_interruptible(&erst_record_id_cache.lock); + if (rc) + return rc; + raw_spin_lock_irqsave(&erst_lock, flags); + if (erst_erange.attr & ERST_RANGE_NVRAM) + rc = __erst_clear_from_nvram(record_id); + else + rc = __erst_clear_from_storage(record_id); + raw_spin_unlock_irqrestore(&erst_lock, flags); + if (rc) + goto out; + entries = erst_record_id_cache.entries; + for (i = 0; i < erst_record_id_cache.len; i++) { + if (entries[i] == record_id) + entries[i] = APEI_ERST_INVALID_RECORD_ID; + } + __erst_record_id_cache_compact(); +out: + mutex_unlock(&erst_record_id_cache.lock); + return rc; +} +EXPORT_SYMBOL_GPL(erst_clear); + +static int __init setup_erst_disable(char *str) +{ + erst_disable = 1; + return 0; +} + +__setup("erst_disable", setup_erst_disable); + +static int erst_check_table(struct acpi_table_erst *erst_tab) +{ + if ((erst_tab->header_length != + (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) + && (erst_tab->header_length != sizeof(struct acpi_table_erst))) + return -EINVAL; + if (erst_tab->header.length < sizeof(struct acpi_table_erst)) + return -EINVAL; + if (erst_tab->entries != + (erst_tab->header.length - sizeof(struct acpi_table_erst)) / + sizeof(struct acpi_erst_entry)) + return -EINVAL; + + return 0; +} + +static int erst_open_pstore(struct pstore_info *psi); +static int erst_close_pstore(struct pstore_info *psi); +static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, + struct timespec *time, char **buf, + bool *compressed, struct pstore_info *psi); +static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, + u64 *id, unsigned int part, int count, bool compressed, + size_t size, struct pstore_info *psi); +static int erst_clearer(enum pstore_type_id type, u64 id, int count, + struct timespec time, struct pstore_info *psi); + +static struct pstore_info erst_info = { + .owner = THIS_MODULE, + .name = "erst", + .flags = PSTORE_FLAGS_FRAGILE, + .open = erst_open_pstore, + .close = erst_close_pstore, + .read = erst_reader, + .write = erst_writer, + .erase = erst_clearer +}; + +#define CPER_CREATOR_PSTORE \ + UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ + 0x64, 0x90, 0xb8, 0x9d) +#define CPER_SECTION_TYPE_DMESG \ + UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ + 0x94, 0x19, 0xeb, 0x12) +#define CPER_SECTION_TYPE_DMESG_Z \ + UUID_LE(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \ + 0x34, 0xdd, 0xfa, 0xc6) +#define CPER_SECTION_TYPE_MCE \ + UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ + 0x04, 0x4a, 0x38, 0xfc) + +struct cper_pstore_record { + struct cper_record_header hdr; + struct cper_section_descriptor sec_hdr; + char data[]; +} __packed; + +static int reader_pos; + +static int erst_open_pstore(struct pstore_info *psi) +{ + int rc; + + if (erst_disable) + return -ENODEV; + + rc = erst_get_record_id_begin(&reader_pos); + + return rc; +} + +static int erst_close_pstore(struct pstore_info *psi) +{ + erst_get_record_id_end(); + + return 0; +} + +static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, + struct timespec *time, char **buf, + bool *compressed, struct pstore_info *psi) +{ + int rc; + ssize_t len = 0; + u64 record_id; + struct cper_pstore_record *rcd; + size_t rcd_len = sizeof(*rcd) + erst_info.bufsize; + + if (erst_disable) + return -ENODEV; + + rcd = kmalloc(rcd_len, GFP_KERNEL); + if (!rcd) { + rc = -ENOMEM; + goto out; + } +skip: + rc = erst_get_record_id_next(&reader_pos, &record_id); + if (rc) + goto out; + + /* no more record */ + if (record_id == APEI_ERST_INVALID_RECORD_ID) { + rc = -EINVAL; + goto out; + } + + len = erst_read(record_id, &rcd->hdr, rcd_len); + /* The record may be cleared by others, try read next record */ + if (len == -ENOENT) + goto skip; + else if (len < sizeof(*rcd)) { + rc = -EIO; + goto out; + } + if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) + goto skip; + + *buf = kmalloc(len, GFP_KERNEL); + if (*buf == NULL) { + rc = -ENOMEM; + goto out; + } + memcpy(*buf, rcd->data, len - sizeof(*rcd)); + *id = record_id; + *compressed = false; + if (uuid_le_cmp(rcd->sec_hdr.section_type, + CPER_SECTION_TYPE_DMESG_Z) == 0) { + *type = PSTORE_TYPE_DMESG; + *compressed = true; + } else if (uuid_le_cmp(rcd->sec_hdr.section_type, + CPER_SECTION_TYPE_DMESG) == 0) + *type = PSTORE_TYPE_DMESG; + else if (uuid_le_cmp(rcd->sec_hdr.section_type, + CPER_SECTION_TYPE_MCE) == 0) + *type = PSTORE_TYPE_MCE; + else + *type = PSTORE_TYPE_UNKNOWN; + + if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) + time->tv_sec = rcd->hdr.timestamp; + else + time->tv_sec = 0; + time->tv_nsec = 0; + +out: + kfree(rcd); + return (rc < 0) ? rc : (len - sizeof(*rcd)); +} + +static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, + u64 *id, unsigned int part, int count, bool compressed, + size_t size, struct pstore_info *psi) +{ + struct cper_pstore_record *rcd = (struct cper_pstore_record *) + (erst_info.buf - sizeof(*rcd)); + int ret; + + memset(rcd, 0, sizeof(*rcd)); + memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); + rcd->hdr.revision = CPER_RECORD_REV; + rcd->hdr.signature_end = CPER_SIG_END; + rcd->hdr.section_count = 1; + rcd->hdr.error_severity = CPER_SEV_FATAL; + /* timestamp valid. platform_id, partition_id are invalid */ + rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; + rcd->hdr.timestamp = get_seconds(); + rcd->hdr.record_length = sizeof(*rcd) + size; + rcd->hdr.creator_id = CPER_CREATOR_PSTORE; + rcd->hdr.notification_type = CPER_NOTIFY_MCE; + rcd->hdr.record_id = cper_next_record_id(); + rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; + + rcd->sec_hdr.section_offset = sizeof(*rcd); + rcd->sec_hdr.section_length = size; + rcd->sec_hdr.revision = CPER_SEC_REV; + /* fru_id and fru_text is invalid */ + rcd->sec_hdr.validation_bits = 0; + rcd->sec_hdr.flags = CPER_SEC_PRIMARY; + switch (type) { + case PSTORE_TYPE_DMESG: + if (compressed) + rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; + else + rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; + break; + case PSTORE_TYPE_MCE: + rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; + break; + default: + return -EINVAL; + } + rcd->sec_hdr.section_severity = CPER_SEV_FATAL; + + ret = erst_write(&rcd->hdr); + *id = rcd->hdr.record_id; + + return ret; +} + +static int erst_clearer(enum pstore_type_id type, u64 id, int count, + struct timespec time, struct pstore_info *psi) +{ + return erst_clear(id); +} + +static int __init erst_init(void) +{ + int rc = 0; + acpi_status status; + struct apei_exec_context ctx; + struct apei_resources erst_resources; + struct resource *r; + char *buf; + + if (acpi_disabled) + goto err; + + if (erst_disable) { + pr_info( + "Error Record Serialization Table (ERST) support is disabled.\n"); + goto err; + } + + status = acpi_get_table(ACPI_SIG_ERST, 0, + (struct acpi_table_header **)&erst_tab); + if (status == AE_NOT_FOUND) + goto err; + else if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + pr_err("Failed to get table, %s\n", msg); + rc = -EINVAL; + goto err; + } + + rc = erst_check_table(erst_tab); + if (rc) { + pr_err(FW_BUG "ERST table is invalid.\n"); + goto err; + } + + apei_resources_init(&erst_resources); + erst_exec_ctx_init(&ctx); + rc = apei_exec_collect_resources(&ctx, &erst_resources); + if (rc) + goto err_fini; + rc = apei_resources_request(&erst_resources, "APEI ERST"); + if (rc) + goto err_fini; + rc = apei_exec_pre_map_gars(&ctx); + if (rc) + goto err_release; + rc = erst_get_erange(&erst_erange); + if (rc) { + if (rc == -ENODEV) + pr_info( + "The corresponding hardware device or firmware implementation " + "is not available.\n"); + else + pr_err("Failed to get Error Log Address Range.\n"); + goto err_unmap_reg; + } + + r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST"); + if (!r) { + pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n", + (unsigned long long)erst_erange.base, + (unsigned long long)erst_erange.base + erst_erange.size - 1); + rc = -EIO; + goto err_unmap_reg; + } + rc = -ENOMEM; + erst_erange.vaddr = ioremap_cache(erst_erange.base, + erst_erange.size); + if (!erst_erange.vaddr) + goto err_release_erange; + + pr_info( + "Error Record Serialization Table (ERST) support is initialized.\n"); + + buf = kmalloc(erst_erange.size, GFP_KERNEL); + spin_lock_init(&erst_info.buf_lock); + if (buf) { + erst_info.buf = buf + sizeof(struct cper_pstore_record); + erst_info.bufsize = erst_erange.size - + sizeof(struct cper_pstore_record); + rc = pstore_register(&erst_info); + if (rc) { + if (rc != -EPERM) + pr_info( + "Could not register with persistent store.\n"); + erst_info.buf = NULL; + erst_info.bufsize = 0; + kfree(buf); + } + } else + pr_err( + "Failed to allocate %lld bytes for persistent store error log.\n", + erst_erange.size); + + return 0; + +err_release_erange: + release_mem_region(erst_erange.base, erst_erange.size); +err_unmap_reg: + apei_exec_post_unmap_gars(&ctx); +err_release: + apei_resources_release(&erst_resources); +err_fini: + apei_resources_fini(&erst_resources); +err: + erst_disable = 1; + return rc; +} + +device_initcall(erst_init); diff --git a/kernel/drivers/acpi/apei/ghes.c b/kernel/drivers/acpi/apei/ghes.c new file mode 100644 index 000000000..e82d0976a --- /dev/null +++ b/kernel/drivers/acpi/apei/ghes.c @@ -0,0 +1,1163 @@ +/* + * APEI Generic Hardware Error Source support + * + * Generic Hardware Error Source provides a way to report platform + * hardware errors (such as that from chipset). It works in so called + * "Firmware First" mode, that is, hardware errors are reported to + * firmware firstly, then reported to Linux by firmware. This way, + * some non-standard hardware error registers or non-standard hardware + * link can be checked by firmware to produce more hardware error + * information for Linux. + * + * For more information about Generic Hardware Error Source, please + * refer to ACPI Specification version 4.0, section 17.3.2.6 + * + * Copyright 2010,2011 Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "apei-internal.h" + +#define GHES_PFX "GHES: " + +#define GHES_ESTATUS_MAX_SIZE 65536 +#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536 + +#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3 + +/* This is just an estimation for memory pool allocation */ +#define GHES_ESTATUS_CACHE_AVG_SIZE 512 + +#define GHES_ESTATUS_CACHES_SIZE 4 + +#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL +/* Prevent too many caches are allocated because of RCU */ +#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) + +#define GHES_ESTATUS_CACHE_LEN(estatus_len) \ + (sizeof(struct ghes_estatus_cache) + (estatus_len)) +#define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ + ((struct acpi_hest_generic_status *) \ + ((struct ghes_estatus_cache *)(estatus_cache) + 1)) + +#define GHES_ESTATUS_NODE_LEN(estatus_len) \ + (sizeof(struct ghes_estatus_node) + (estatus_len)) +#define GHES_ESTATUS_FROM_NODE(estatus_node) \ + ((struct acpi_hest_generic_status *) \ + ((struct ghes_estatus_node *)(estatus_node) + 1)) + +bool ghes_disable; +module_param_named(disable, ghes_disable, bool, 0); + +/* + * All error sources notified with SCI shares one notifier function, + * so they need to be linked and checked one by one. This is applied + * to NMI too. + * + * RCU is used for these lists, so ghes_list_mutex is only used for + * list changing, not for traversing. + */ +static LIST_HEAD(ghes_sci); +static DEFINE_MUTEX(ghes_list_mutex); + +/* + * Because the memory area used to transfer hardware error information + * from BIOS to Linux can be determined only in NMI, IRQ or timer + * handler, but general ioremap can not be used in atomic context, so + * a special version of atomic ioremap is implemented for that. + */ + +/* + * Two virtual pages are used, one for IRQ/PROCESS context, the other for + * NMI context (optionally). + */ +#ifdef CONFIG_HAVE_ACPI_APEI_NMI +#define GHES_IOREMAP_PAGES 2 +#else +#define GHES_IOREMAP_PAGES 1 +#endif +#define GHES_IOREMAP_IRQ_PAGE(base) (base) +#define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE) + +/* virtual memory area for atomic ioremap */ +static struct vm_struct *ghes_ioremap_area; +/* + * These 2 spinlock is used to prevent atomic ioremap virtual memory + * area from being mapped simultaneously. + */ +static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); +static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); + +static struct gen_pool *ghes_estatus_pool; +static unsigned long ghes_estatus_pool_size_request; + +static struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE]; +static atomic_t ghes_estatus_cache_alloced; + +static int ghes_ioremap_init(void) +{ + ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, + VM_IOREMAP, VMALLOC_START, VMALLOC_END); + if (!ghes_ioremap_area) { + pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); + return -ENOMEM; + } + + return 0; +} + +static void ghes_ioremap_exit(void) +{ + free_vm_area(ghes_ioremap_area); +} + +static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) +{ + unsigned long vaddr; + + vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); + ioremap_page_range(vaddr, vaddr + PAGE_SIZE, + pfn << PAGE_SHIFT, PAGE_KERNEL); + + return (void __iomem *)vaddr; +} + +static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) +{ + unsigned long vaddr; + + vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); + ioremap_page_range(vaddr, vaddr + PAGE_SIZE, + pfn << PAGE_SHIFT, PAGE_KERNEL); + + return (void __iomem *)vaddr; +} + +static void ghes_iounmap_nmi(void __iomem *vaddr_ptr) +{ + unsigned long vaddr = (unsigned long __force)vaddr_ptr; + void *base = ghes_ioremap_area->addr; + + BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); + unmap_kernel_range_noflush(vaddr, PAGE_SIZE); + arch_apei_flush_tlb_one(vaddr); +} + +static void ghes_iounmap_irq(void __iomem *vaddr_ptr) +{ + unsigned long vaddr = (unsigned long __force)vaddr_ptr; + void *base = ghes_ioremap_area->addr; + + BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); + unmap_kernel_range_noflush(vaddr, PAGE_SIZE); + arch_apei_flush_tlb_one(vaddr); +} + +static int ghes_estatus_pool_init(void) +{ + ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1); + if (!ghes_estatus_pool) + return -ENOMEM; + return 0; +} + +static void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool, + struct gen_pool_chunk *chunk, + void *data) +{ + free_page(chunk->start_addr); +} + +static void ghes_estatus_pool_exit(void) +{ + gen_pool_for_each_chunk(ghes_estatus_pool, + ghes_estatus_pool_free_chunk_page, NULL); + gen_pool_destroy(ghes_estatus_pool); +} + +static int ghes_estatus_pool_expand(unsigned long len) +{ + unsigned long i, pages, size, addr; + int ret; + + ghes_estatus_pool_size_request += PAGE_ALIGN(len); + size = gen_pool_size(ghes_estatus_pool); + if (size >= ghes_estatus_pool_size_request) + return 0; + pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE; + for (i = 0; i < pages; i++) { + addr = __get_free_page(GFP_KERNEL); + if (!addr) + return -ENOMEM; + ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1); + if (ret) + return ret; + } + + return 0; +} + +static struct ghes *ghes_new(struct acpi_hest_generic *generic) +{ + struct ghes *ghes; + unsigned int error_block_length; + int rc; + + ghes = kzalloc(sizeof(*ghes), GFP_KERNEL); + if (!ghes) + return ERR_PTR(-ENOMEM); + ghes->generic = generic; + rc = apei_map_generic_address(&generic->error_status_address); + if (rc) + goto err_free; + error_block_length = generic->error_block_length; + if (error_block_length > GHES_ESTATUS_MAX_SIZE) { + pr_warning(FW_WARN GHES_PFX + "Error status block length is too long: %u for " + "generic hardware error source: %d.\n", + error_block_length, generic->header.source_id); + error_block_length = GHES_ESTATUS_MAX_SIZE; + } + ghes->estatus = kmalloc(error_block_length, GFP_KERNEL); + if (!ghes->estatus) { + rc = -ENOMEM; + goto err_unmap; + } + + return ghes; + +err_unmap: + apei_unmap_generic_address(&generic->error_status_address); +err_free: + kfree(ghes); + return ERR_PTR(rc); +} + +static void ghes_fini(struct ghes *ghes) +{ + kfree(ghes->estatus); + apei_unmap_generic_address(&ghes->generic->error_status_address); +} + +static inline int ghes_severity(int severity) +{ + switch (severity) { + case CPER_SEV_INFORMATIONAL: + return GHES_SEV_NO; + case CPER_SEV_CORRECTED: + return GHES_SEV_CORRECTED; + case CPER_SEV_RECOVERABLE: + return GHES_SEV_RECOVERABLE; + case CPER_SEV_FATAL: + return GHES_SEV_PANIC; + default: + /* Unknown, go panic */ + return GHES_SEV_PANIC; + } +} + +static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, + int from_phys) +{ + void __iomem *vaddr; + unsigned long flags = 0; + int in_nmi = in_nmi(); + u64 offset; + u32 trunk; + + while (len > 0) { + offset = paddr - (paddr & PAGE_MASK); + if (in_nmi) { + raw_spin_lock(&ghes_ioremap_lock_nmi); + vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); + } else { + spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); + vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); + } + trunk = PAGE_SIZE - offset; + trunk = min(trunk, len); + if (from_phys) + memcpy_fromio(buffer, vaddr + offset, trunk); + else + memcpy_toio(vaddr + offset, buffer, trunk); + len -= trunk; + paddr += trunk; + buffer += trunk; + if (in_nmi) { + ghes_iounmap_nmi(vaddr); + raw_spin_unlock(&ghes_ioremap_lock_nmi); + } else { + ghes_iounmap_irq(vaddr); + spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); + } + } +} + +static int ghes_read_estatus(struct ghes *ghes, int silent) +{ + struct acpi_hest_generic *g = ghes->generic; + u64 buf_paddr; + u32 len; + int rc; + + rc = apei_read(&buf_paddr, &g->error_status_address); + if (rc) { + if (!silent && printk_ratelimit()) + pr_warning(FW_WARN GHES_PFX +"Failed to read error status block address for hardware error source: %d.\n", + g->header.source_id); + return -EIO; + } + if (!buf_paddr) + return -ENOENT; + + ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, + sizeof(*ghes->estatus), 1); + if (!ghes->estatus->block_status) + return -ENOENT; + + ghes->buffer_paddr = buf_paddr; + ghes->flags |= GHES_TO_CLEAR; + + rc = -EIO; + len = cper_estatus_len(ghes->estatus); + if (len < sizeof(*ghes->estatus)) + goto err_read_block; + if (len > ghes->generic->error_block_length) + goto err_read_block; + if (cper_estatus_check_header(ghes->estatus)) + goto err_read_block; + ghes_copy_tofrom_phys(ghes->estatus + 1, + buf_paddr + sizeof(*ghes->estatus), + len - sizeof(*ghes->estatus), 1); + if (cper_estatus_check(ghes->estatus)) + goto err_read_block; + rc = 0; + +err_read_block: + if (rc && !silent && printk_ratelimit()) + pr_warning(FW_WARN GHES_PFX + "Failed to read error status block!\n"); + return rc; +} + +static void ghes_clear_estatus(struct ghes *ghes) +{ + ghes->estatus->block_status = 0; + if (!(ghes->flags & GHES_TO_CLEAR)) + return; + ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr, + sizeof(ghes->estatus->block_status), 0); + ghes->flags &= ~GHES_TO_CLEAR; +} + +static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) +{ +#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE + unsigned long pfn; + int flags = -1; + int sec_sev = ghes_severity(gdata->error_severity); + struct cper_sec_mem_err *mem_err; + mem_err = (struct cper_sec_mem_err *)(gdata + 1); + + if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) + return; + + pfn = mem_err->physical_addr >> PAGE_SHIFT; + if (!pfn_valid(pfn)) { + pr_warn_ratelimited(FW_WARN GHES_PFX + "Invalid address in generic error data: %#llx\n", + mem_err->physical_addr); + return; + } + + /* iff following two events can be handled properly by now */ + if (sec_sev == GHES_SEV_CORRECTED && + (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) + flags = MF_SOFT_OFFLINE; + if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) + flags = 0; + + if (flags != -1) + memory_failure_queue(pfn, 0, flags); +#endif +} + +static void ghes_do_proc(struct ghes *ghes, + const struct acpi_hest_generic_status *estatus) +{ + int sev, sec_sev; + struct acpi_hest_generic_data *gdata; + + sev = ghes_severity(estatus->error_severity); + apei_estatus_for_each_section(estatus, gdata) { + sec_sev = ghes_severity(gdata->error_severity); + if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, + CPER_SEC_PLATFORM_MEM)) { + struct cper_sec_mem_err *mem_err; + mem_err = (struct cper_sec_mem_err *)(gdata+1); + ghes_edac_report_mem_error(ghes, sev, mem_err); + + arch_apei_report_mem_error(sev, mem_err); + ghes_handle_memory_failure(gdata, sev); + } +#ifdef CONFIG_ACPI_APEI_PCIEAER + else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, + CPER_SEC_PCIE)) { + struct cper_sec_pcie *pcie_err; + pcie_err = (struct cper_sec_pcie *)(gdata+1); + if (sev == GHES_SEV_RECOVERABLE && + sec_sev == GHES_SEV_RECOVERABLE && + pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && + pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { + unsigned int devfn; + int aer_severity; + + devfn = PCI_DEVFN(pcie_err->device_id.device, + pcie_err->device_id.function); + aer_severity = cper_severity_to_aer(sev); + + /* + * If firmware reset the component to contain + * the error, we must reinitialize it before + * use, so treat it as a fatal AER error. + */ + if (gdata->flags & CPER_SEC_RESET) + aer_severity = AER_FATAL; + + aer_recover_queue(pcie_err->device_id.segment, + pcie_err->device_id.bus, + devfn, aer_severity, + (struct aer_capability_regs *) + pcie_err->aer_info); + } + + } +#endif + } +} + +static void __ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus) +{ + static atomic_t seqno; + unsigned int curr_seqno; + char pfx_seq[64]; + + if (pfx == NULL) { + if (ghes_severity(estatus->error_severity) <= + GHES_SEV_CORRECTED) + pfx = KERN_WARNING; + else + pfx = KERN_ERR; + } + curr_seqno = atomic_inc_return(&seqno); + snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); + printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", + pfx_seq, generic->header.source_id); + cper_estatus_print(pfx_seq, estatus); +} + +static int ghes_print_estatus(const char *pfx, + const struct acpi_hest_generic *generic, + const struct acpi_hest_generic_status *estatus) +{ + /* Not more than 2 messages every 5 seconds */ + static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2); + static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2); + struct ratelimit_state *ratelimit; + + if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED) + ratelimit = &ratelimit_corrected; + else + ratelimit = &ratelimit_uncorrected; + if (__ratelimit(ratelimit)) { + __ghes_print_estatus(pfx, generic, estatus); + return 1; + } + return 0; +} + +/* + * GHES error status reporting throttle, to report more kinds of + * errors, instead of just most frequently occurred errors. + */ +static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus) +{ + u32 len; + int i, cached = 0; + unsigned long long now; + struct ghes_estatus_cache *cache; + struct acpi_hest_generic_status *cache_estatus; + + len = cper_estatus_len(estatus); + rcu_read_lock(); + for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { + cache = rcu_dereference(ghes_estatus_caches[i]); + if (cache == NULL) + continue; + if (len != cache->estatus_len) + continue; + cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); + if (memcmp(estatus, cache_estatus, len)) + continue; + atomic_inc(&cache->count); + now = sched_clock(); + if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC) + cached = 1; + break; + } + rcu_read_unlock(); + return cached; +} + +static struct ghes_estatus_cache *ghes_estatus_cache_alloc( + struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus) +{ + int alloced; + u32 len, cache_len; + struct ghes_estatus_cache *cache; + struct acpi_hest_generic_status *cache_estatus; + + alloced = atomic_add_return(1, &ghes_estatus_cache_alloced); + if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) { + atomic_dec(&ghes_estatus_cache_alloced); + return NULL; + } + len = cper_estatus_len(estatus); + cache_len = GHES_ESTATUS_CACHE_LEN(len); + cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len); + if (!cache) { + atomic_dec(&ghes_estatus_cache_alloced); + return NULL; + } + cache_estatus = GHES_ESTATUS_FROM_CACHE(cache); + memcpy(cache_estatus, estatus, len); + cache->estatus_len = len; + atomic_set(&cache->count, 0); + cache->generic = generic; + cache->time_in = sched_clock(); + return cache; +} + +static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache) +{ + u32 len; + + len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache)); + len = GHES_ESTATUS_CACHE_LEN(len); + gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len); + atomic_dec(&ghes_estatus_cache_alloced); +} + +static void ghes_estatus_cache_rcu_free(struct rcu_head *head) +{ + struct ghes_estatus_cache *cache; + + cache = container_of(head, struct ghes_estatus_cache, rcu); + ghes_estatus_cache_free(cache); +} + +static void ghes_estatus_cache_add( + struct acpi_hest_generic *generic, + struct acpi_hest_generic_status *estatus) +{ + int i, slot = -1, count; + unsigned long long now, duration, period, max_period = 0; + struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache; + + new_cache = ghes_estatus_cache_alloc(generic, estatus); + if (new_cache == NULL) + return; + rcu_read_lock(); + now = sched_clock(); + for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) { + cache = rcu_dereference(ghes_estatus_caches[i]); + if (cache == NULL) { + slot = i; + slot_cache = NULL; + break; + } + duration = now - cache->time_in; + if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) { + slot = i; + slot_cache = cache; + break; + } + count = atomic_read(&cache->count); + period = duration; + do_div(period, (count + 1)); + if (period > max_period) { + max_period = period; + slot = i; + slot_cache = cache; + } + } + /* new_cache must be put into array after its contents are written */ + smp_wmb(); + if (slot != -1 && cmpxchg(ghes_estatus_caches + slot, + slot_cache, new_cache) == slot_cache) { + if (slot_cache) + call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free); + } else + ghes_estatus_cache_free(new_cache); + rcu_read_unlock(); +} + +static int ghes_proc(struct ghes *ghes) +{ + int rc; + + rc = ghes_read_estatus(ghes, 0); + if (rc) + goto out; + if (!ghes_estatus_cached(ghes->estatus)) { + if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus)) + ghes_estatus_cache_add(ghes->generic, ghes->estatus); + } + ghes_do_proc(ghes, ghes->estatus); +out: + ghes_clear_estatus(ghes); + return 0; +} + +static void ghes_add_timer(struct ghes *ghes) +{ + struct acpi_hest_generic *g = ghes->generic; + unsigned long expire; + + if (!g->notify.poll_interval) { + pr_warning(FW_WARN GHES_PFX "Poll interval is 0 for generic hardware error source: %d, disabled.\n", + g->header.source_id); + return; + } + expire = jiffies + msecs_to_jiffies(g->notify.poll_interval); + ghes->timer.expires = round_jiffies_relative(expire); + add_timer(&ghes->timer); +} + +static void ghes_poll_func(unsigned long data) +{ + struct ghes *ghes = (void *)data; + + ghes_proc(ghes); + if (!(ghes->flags & GHES_EXITING)) + ghes_add_timer(ghes); +} + +static irqreturn_t ghes_irq_func(int irq, void *data) +{ + struct ghes *ghes = data; + int rc; + + rc = ghes_proc(ghes); + if (rc) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int ghes_notify_sci(struct notifier_block *this, + unsigned long event, void *data) +{ + struct ghes *ghes; + int ret = NOTIFY_DONE; + + rcu_read_lock(); + list_for_each_entry_rcu(ghes, &ghes_sci, list) { + if (!ghes_proc(ghes)) + ret = NOTIFY_OK; + } + rcu_read_unlock(); + + return ret; +} + +static struct notifier_block ghes_notifier_sci = { + .notifier_call = ghes_notify_sci, +}; + +#ifdef CONFIG_HAVE_ACPI_APEI_NMI +/* + * printk is not safe in NMI context. So in NMI handler, we allocate + * required memory from lock-less memory allocator + * (ghes_estatus_pool), save estatus into it, put them into lock-less + * list (ghes_estatus_llist), then delay printk into IRQ context via + * irq_work (ghes_proc_irq_work). ghes_estatus_size_request record + * required pool size by all NMI error source. + */ +static struct llist_head ghes_estatus_llist; +static struct irq_work ghes_proc_irq_work; + +/* + * NMI may be triggered on any CPU, so ghes_nmi_lock is used for + * mutual exclusion. + */ +static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); + +static LIST_HEAD(ghes_nmi); + +static int ghes_panic_timeout __read_mostly = 30; + +static void ghes_proc_in_irq(struct irq_work *irq_work) +{ + struct llist_node *llnode, *next; + struct ghes_estatus_node *estatus_node; + struct acpi_hest_generic *generic; + struct acpi_hest_generic_status *estatus; + u32 len, node_len; + + llnode = llist_del_all(&ghes_estatus_llist); + /* + * Because the time order of estatus in list is reversed, + * revert it back to proper order. + */ + llnode = llist_reverse_order(llnode); + while (llnode) { + next = llnode->next; + estatus_node = llist_entry(llnode, struct ghes_estatus_node, + llnode); + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); + len = cper_estatus_len(estatus); + node_len = GHES_ESTATUS_NODE_LEN(len); + ghes_do_proc(estatus_node->ghes, estatus); + if (!ghes_estatus_cached(estatus)) { + generic = estatus_node->generic; + if (ghes_print_estatus(NULL, generic, estatus)) + ghes_estatus_cache_add(generic, estatus); + } + gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node, + node_len); + llnode = next; + } +} + +static void ghes_print_queued_estatus(void) +{ + struct llist_node *llnode; + struct ghes_estatus_node *estatus_node; + struct acpi_hest_generic *generic; + struct acpi_hest_generic_status *estatus; + u32 len, node_len; + + llnode = llist_del_all(&ghes_estatus_llist); + /* + * Because the time order of estatus in list is reversed, + * revert it back to proper order. + */ + llnode = llist_reverse_order(llnode); + while (llnode) { + estatus_node = llist_entry(llnode, struct ghes_estatus_node, + llnode); + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); + len = cper_estatus_len(estatus); + node_len = GHES_ESTATUS_NODE_LEN(len); + generic = estatus_node->generic; + ghes_print_estatus(NULL, generic, estatus); + llnode = llnode->next; + } +} + +static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) +{ + struct ghes *ghes, *ghes_global = NULL; + int sev, sev_global = -1; + int ret = NMI_DONE; + + raw_spin_lock(&ghes_nmi_lock); + list_for_each_entry_rcu(ghes, &ghes_nmi, list) { + if (ghes_read_estatus(ghes, 1)) { + ghes_clear_estatus(ghes); + continue; + } + sev = ghes_severity(ghes->estatus->error_severity); + if (sev > sev_global) { + sev_global = sev; + ghes_global = ghes; + } + ret = NMI_HANDLED; + } + + if (ret == NMI_DONE) + goto out; + + if (sev_global >= GHES_SEV_PANIC) { + oops_begin(); + ghes_print_queued_estatus(); + __ghes_print_estatus(KERN_EMERG, ghes_global->generic, + ghes_global->estatus); + /* reboot to log the error! */ + if (panic_timeout == 0) + panic_timeout = ghes_panic_timeout; + panic("Fatal hardware error!"); + } + + list_for_each_entry_rcu(ghes, &ghes_nmi, list) { +#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + u32 len, node_len; + struct ghes_estatus_node *estatus_node; + struct acpi_hest_generic_status *estatus; +#endif + if (!(ghes->flags & GHES_TO_CLEAR)) + continue; +#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + if (ghes_estatus_cached(ghes->estatus)) + goto next; + /* Save estatus for further processing in IRQ context */ + len = cper_estatus_len(ghes->estatus); + node_len = GHES_ESTATUS_NODE_LEN(len); + estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, + node_len); + if (estatus_node) { + estatus_node->ghes = ghes; + estatus_node->generic = ghes->generic; + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); + memcpy(estatus, ghes->estatus, len); + llist_add(&estatus_node->llnode, &ghes_estatus_llist); + } +next: +#endif + ghes_clear_estatus(ghes); + } +#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + irq_work_queue(&ghes_proc_irq_work); +#endif + +out: + raw_spin_unlock(&ghes_nmi_lock); + return ret; +} + +static unsigned long ghes_esource_prealloc_size( + const struct acpi_hest_generic *generic) +{ + unsigned long block_length, prealloc_records, prealloc_size; + + block_length = min_t(unsigned long, generic->error_block_length, + GHES_ESTATUS_MAX_SIZE); + prealloc_records = max_t(unsigned long, + generic->records_to_preallocate, 1); + prealloc_size = min_t(unsigned long, block_length * prealloc_records, + GHES_ESOURCE_PREALLOC_MAX_SIZE); + + return prealloc_size; +} + +static void ghes_estatus_pool_shrink(unsigned long len) +{ + ghes_estatus_pool_size_request -= PAGE_ALIGN(len); +} + +static void ghes_nmi_add(struct ghes *ghes) +{ + unsigned long len; + + len = ghes_esource_prealloc_size(ghes->generic); + ghes_estatus_pool_expand(len); + mutex_lock(&ghes_list_mutex); + if (list_empty(&ghes_nmi)) + register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); + list_add_rcu(&ghes->list, &ghes_nmi); + mutex_unlock(&ghes_list_mutex); +} + +static void ghes_nmi_remove(struct ghes *ghes) +{ + unsigned long len; + + mutex_lock(&ghes_list_mutex); + list_del_rcu(&ghes->list); + if (list_empty(&ghes_nmi)) + unregister_nmi_handler(NMI_LOCAL, "ghes"); + mutex_unlock(&ghes_list_mutex); + /* + * To synchronize with NMI handler, ghes can only be + * freed after NMI handler finishes. + */ + synchronize_rcu(); + len = ghes_esource_prealloc_size(ghes->generic); + ghes_estatus_pool_shrink(len); +} + +static void ghes_nmi_init_cxt(void) +{ + init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); +} +#else /* CONFIG_HAVE_ACPI_APEI_NMI */ +static inline void ghes_nmi_add(struct ghes *ghes) +{ + pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n", + ghes->generic->header.source_id); + BUG(); +} + +static inline void ghes_nmi_remove(struct ghes *ghes) +{ + pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n", + ghes->generic->header.source_id); + BUG(); +} + +static inline void ghes_nmi_init_cxt(void) +{ +} +#endif /* CONFIG_HAVE_ACPI_APEI_NMI */ + +static int ghes_probe(struct platform_device *ghes_dev) +{ + struct acpi_hest_generic *generic; + struct ghes *ghes = NULL; + + int rc = -EINVAL; + + generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data; + if (!generic->enabled) + return -ENODEV; + + switch (generic->notify.type) { + case ACPI_HEST_NOTIFY_POLLED: + case ACPI_HEST_NOTIFY_EXTERNAL: + case ACPI_HEST_NOTIFY_SCI: + break; + case ACPI_HEST_NOTIFY_NMI: + if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) { + pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n", + generic->header.source_id); + goto err; + } + break; + case ACPI_HEST_NOTIFY_LOCAL: + pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", + generic->header.source_id); + goto err; + default: + pr_warning(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n", + generic->notify.type, generic->header.source_id); + goto err; + } + + rc = -EIO; + if (generic->error_block_length < + sizeof(struct acpi_hest_generic_status)) { + pr_warning(FW_BUG GHES_PFX "Invalid error block length: %u for generic hardware error source: %d\n", + generic->error_block_length, + generic->header.source_id); + goto err; + } + ghes = ghes_new(generic); + if (IS_ERR(ghes)) { + rc = PTR_ERR(ghes); + ghes = NULL; + goto err; + } + + rc = ghes_edac_register(ghes, &ghes_dev->dev); + if (rc < 0) + goto err; + + switch (generic->notify.type) { + case ACPI_HEST_NOTIFY_POLLED: + ghes->timer.function = ghes_poll_func; + ghes->timer.data = (unsigned long)ghes; + init_timer_deferrable(&ghes->timer); + ghes_add_timer(ghes); + break; + case ACPI_HEST_NOTIFY_EXTERNAL: + /* External interrupt vector is GSI */ + rc = acpi_gsi_to_irq(generic->notify.vector, &ghes->irq); + if (rc) { + pr_err(GHES_PFX "Failed to map GSI to IRQ for generic hardware error source: %d\n", + generic->header.source_id); + goto err_edac_unreg; + } + rc = request_irq(ghes->irq, ghes_irq_func, 0, "GHES IRQ", ghes); + if (rc) { + pr_err(GHES_PFX "Failed to register IRQ for generic hardware error source: %d\n", + generic->header.source_id); + goto err_edac_unreg; + } + break; + case ACPI_HEST_NOTIFY_SCI: + mutex_lock(&ghes_list_mutex); + if (list_empty(&ghes_sci)) + register_acpi_hed_notifier(&ghes_notifier_sci); + list_add_rcu(&ghes->list, &ghes_sci); + mutex_unlock(&ghes_list_mutex); + break; + case ACPI_HEST_NOTIFY_NMI: + ghes_nmi_add(ghes); + break; + default: + BUG(); + } + platform_set_drvdata(ghes_dev, ghes); + + return 0; +err_edac_unreg: + ghes_edac_unregister(ghes); +err: + if (ghes) { + ghes_fini(ghes); + kfree(ghes); + } + return rc; +} + +static int ghes_remove(struct platform_device *ghes_dev) +{ + struct ghes *ghes; + struct acpi_hest_generic *generic; + + ghes = platform_get_drvdata(ghes_dev); + generic = ghes->generic; + + ghes->flags |= GHES_EXITING; + switch (generic->notify.type) { + case ACPI_HEST_NOTIFY_POLLED: + del_timer_sync(&ghes->timer); + break; + case ACPI_HEST_NOTIFY_EXTERNAL: + free_irq(ghes->irq, ghes); + break; + case ACPI_HEST_NOTIFY_SCI: + mutex_lock(&ghes_list_mutex); + list_del_rcu(&ghes->list); + if (list_empty(&ghes_sci)) + unregister_acpi_hed_notifier(&ghes_notifier_sci); + mutex_unlock(&ghes_list_mutex); + break; + case ACPI_HEST_NOTIFY_NMI: + ghes_nmi_remove(ghes); + break; + default: + BUG(); + break; + } + + ghes_fini(ghes); + + ghes_edac_unregister(ghes); + + kfree(ghes); + + platform_set_drvdata(ghes_dev, NULL); + + return 0; +} + +static struct platform_driver ghes_platform_driver = { + .driver = { + .name = "GHES", + }, + .probe = ghes_probe, + .remove = ghes_remove, +}; + +static int __init ghes_init(void) +{ + int rc; + + if (acpi_disabled) + return -ENODEV; + + if (hest_disable) { + pr_info(GHES_PFX "HEST is not enabled!\n"); + return -EINVAL; + } + + if (ghes_disable) { + pr_info(GHES_PFX "GHES is not enabled!\n"); + return -EINVAL; + } + + ghes_nmi_init_cxt(); + + rc = ghes_ioremap_init(); + if (rc) + goto err; + + rc = ghes_estatus_pool_init(); + if (rc) + goto err_ioremap_exit; + + rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE * + GHES_ESTATUS_CACHE_ALLOCED_MAX); + if (rc) + goto err_pool_exit; + + rc = platform_driver_register(&ghes_platform_driver); + if (rc) + goto err_pool_exit; + + rc = apei_osc_setup(); + if (rc == 0 && osc_sb_apei_support_acked) + pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n"); + else if (rc == 0 && !osc_sb_apei_support_acked) + pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n"); + else if (rc && osc_sb_apei_support_acked) + pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n"); + else + pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n"); + + return 0; +err_pool_exit: + ghes_estatus_pool_exit(); +err_ioremap_exit: + ghes_ioremap_exit(); +err: + return rc; +} + +static void __exit ghes_exit(void) +{ + platform_driver_unregister(&ghes_platform_driver); + ghes_estatus_pool_exit(); + ghes_ioremap_exit(); +} + +module_init(ghes_init); +module_exit(ghes_exit); + +MODULE_AUTHOR("Huang Ying"); +MODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:GHES"); diff --git a/kernel/drivers/acpi/apei/hest.c b/kernel/drivers/acpi/apei/hest.c new file mode 100644 index 000000000..06e9b411a --- /dev/null +++ b/kernel/drivers/acpi/apei/hest.c @@ -0,0 +1,255 @@ +/* + * APEI Hardware Error Souce Table support + * + * HEST describes error sources in detail; communicates operational + * parameters (i.e. severity levels, masking bits, and threshold + * values) to Linux as necessary. It also allows the BIOS to report + * non-standard error sources to Linux (for example, chipset-specific + * error registers). + * + * For more information about HEST, please refer to ACPI Specification + * version 4.0, section 17.3.2. + * + * Copyright 2009 Intel Corp. + * Author: Huang Ying + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apei-internal.h" + +#define HEST_PFX "HEST: " + +bool hest_disable; +EXPORT_SYMBOL_GPL(hest_disable); + +/* HEST table parsing */ + +static struct acpi_table_hest *__read_mostly hest_tab; + +static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { + [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */ + [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1, + [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi), + [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root), + [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer), + [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge), + [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic), +}; + +static int hest_esrc_len(struct acpi_hest_header *hest_hdr) +{ + u16 hest_type = hest_hdr->type; + int len; + + if (hest_type >= ACPI_HEST_TYPE_RESERVED) + return 0; + + len = hest_esrc_len_tab[hest_type]; + + if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) { + struct acpi_hest_ia_corrected *cmc; + cmc = (struct acpi_hest_ia_corrected *)hest_hdr; + len = sizeof(*cmc) + cmc->num_hardware_banks * + sizeof(struct acpi_hest_ia_error_bank); + } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) { + struct acpi_hest_ia_machine_check *mc; + mc = (struct acpi_hest_ia_machine_check *)hest_hdr; + len = sizeof(*mc) + mc->num_hardware_banks * + sizeof(struct acpi_hest_ia_error_bank); + } + BUG_ON(len == -1); + + return len; +}; + +int apei_hest_parse(apei_hest_func_t func, void *data) +{ + struct acpi_hest_header *hest_hdr; + int i, rc, len; + + if (hest_disable || !hest_tab) + return -EINVAL; + + hest_hdr = (struct acpi_hest_header *)(hest_tab + 1); + for (i = 0; i < hest_tab->error_source_count; i++) { + len = hest_esrc_len(hest_hdr); + if (!len) { + pr_warning(FW_WARN HEST_PFX + "Unknown or unused hardware error source " + "type: %d for hardware error source: %d.\n", + hest_hdr->type, hest_hdr->source_id); + return -EINVAL; + } + if ((void *)hest_hdr + len > + (void *)hest_tab + hest_tab->header.length) { + pr_warning(FW_BUG HEST_PFX + "Table contents overflow for hardware error source: %d.\n", + hest_hdr->source_id); + return -EINVAL; + } + + rc = func(hest_hdr, data); + if (rc) + return rc; + + hest_hdr = (void *)hest_hdr + len; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apei_hest_parse); + +/* + * Check if firmware advertises firmware first mode. We need FF bit to be set + * along with a set of MC banks which work in FF mode. + */ +static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data) +{ + return arch_apei_enable_cmcff(hest_hdr, data); +} + +struct ghes_arr { + struct platform_device **ghes_devs; + unsigned int count; +}; + +static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data) +{ + int *count = data; + + if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR) + (*count)++; + return 0; +} + +static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data) +{ + struct platform_device *ghes_dev; + struct ghes_arr *ghes_arr = data; + int rc, i; + + if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR) + return 0; + + if (!((struct acpi_hest_generic *)hest_hdr)->enabled) + return 0; + for (i = 0; i < ghes_arr->count; i++) { + struct acpi_hest_header *hdr; + ghes_dev = ghes_arr->ghes_devs[i]; + hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data; + if (hdr->source_id == hest_hdr->source_id) { + pr_warning(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n", + hdr->source_id); + return -EIO; + } + } + ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id); + if (!ghes_dev) + return -ENOMEM; + + rc = platform_device_add_data(ghes_dev, &hest_hdr, sizeof(void *)); + if (rc) + goto err; + + rc = platform_device_add(ghes_dev); + if (rc) + goto err; + ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev; + + return 0; +err: + platform_device_put(ghes_dev); + return rc; +} + +static int __init hest_ghes_dev_register(unsigned int ghes_count) +{ + int rc, i; + struct ghes_arr ghes_arr; + + ghes_arr.count = 0; + ghes_arr.ghes_devs = kmalloc(sizeof(void *) * ghes_count, GFP_KERNEL); + if (!ghes_arr.ghes_devs) + return -ENOMEM; + + rc = apei_hest_parse(hest_parse_ghes, &ghes_arr); + if (rc) + goto err; +out: + kfree(ghes_arr.ghes_devs); + return rc; +err: + for (i = 0; i < ghes_arr.count; i++) + platform_device_unregister(ghes_arr.ghes_devs[i]); + goto out; +} + +static int __init setup_hest_disable(char *str) +{ + hest_disable = 1; + return 0; +} + +__setup("hest_disable", setup_hest_disable); + +void __init acpi_hest_init(void) +{ + acpi_status status; + int rc = -ENODEV; + unsigned int ghes_count = 0; + + if (hest_disable) { + pr_info(HEST_PFX "Table parsing disabled.\n"); + return; + } + + status = acpi_get_table(ACPI_SIG_HEST, 0, + (struct acpi_table_header **)&hest_tab); + if (status == AE_NOT_FOUND) + goto err; + else if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + pr_err(HEST_PFX "Failed to get table, %s\n", msg); + rc = -EINVAL; + goto err; + } + + if (!acpi_disable_cmcff) + apei_hest_parse(hest_parse_cmc, NULL); + + if (!ghes_disable) { + rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); + if (rc) + goto err; + rc = hest_ghes_dev_register(ghes_count); + if (rc) + goto err; + } + + pr_info(HEST_PFX "Table parsing has been initialized.\n"); + return; +err: + hest_disable = 1; +} diff --git a/kernel/drivers/acpi/battery.c b/kernel/drivers/acpi/battery.c new file mode 100644 index 000000000..63d43677f --- /dev/null +++ b/kernel/drivers/acpi/battery.c @@ -0,0 +1,1329 @@ +/* + * battery.c - ACPI Battery Driver (Revision: 2.0) + * + * Copyright (C) 2007 Alexey Starikovskiy + * Copyright (C) 2004-2007 Vladimir Lebedev + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ACPI_PROCFS_POWER +#include +#include +#include +#endif + +#include +#include + +#include "battery.h" + +#define PREFIX "ACPI: " + +#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF + +#define ACPI_BATTERY_DEVICE_NAME "Battery" + +/* Battery power unit: 0 means mW, 1 means mA */ +#define ACPI_BATTERY_POWER_UNIT_MA 1 + +#define ACPI_BATTERY_STATE_DISCHARGING 0x1 +#define ACPI_BATTERY_STATE_CHARGING 0x2 +#define ACPI_BATTERY_STATE_CRITICAL 0x4 + +#define _COMPONENT ACPI_BATTERY_COMPONENT + +ACPI_MODULE_NAME("battery"); + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_AUTHOR("Alexey Starikovskiy "); +MODULE_DESCRIPTION("ACPI Battery Driver"); +MODULE_LICENSE("GPL"); + +static int battery_bix_broken_package; +static int battery_notification_delay_ms; +static unsigned int cache_time = 1000; +module_param(cache_time, uint, 0644); +MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); + +#ifdef CONFIG_ACPI_PROCFS_POWER +extern struct proc_dir_entry *acpi_lock_battery_dir(void); +extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); + +enum acpi_battery_files { + info_tag = 0, + state_tag, + alarm_tag, + ACPI_BATTERY_NUMFILES, +}; + +#endif + +static const struct acpi_device_id battery_device_ids[] = { + {"PNP0C0A", 0}, + {"", 0}, +}; + +MODULE_DEVICE_TABLE(acpi, battery_device_ids); + +enum { + ACPI_BATTERY_ALARM_PRESENT, + ACPI_BATTERY_XINFO_PRESENT, + ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, + /* On Lenovo Thinkpad models from 2010 and 2011, the power unit + switches between mWh and mAh depending on whether the system + is running on battery or not. When mAh is the unit, most + reported values are incorrect and need to be adjusted by + 10000/design_voltage. Verified on x201, t410, t410s, and x220. + Pre-2010 and 2012 models appear to always report in mWh and + are thus unaffected (tested with t42, t61, t500, x200, x300, + and x230). Also, in mid-2012 Lenovo issued a BIOS update for + the 2011 models that fixes the issue (tested on x220 with a + post-1.29 BIOS), but as of Nov. 2012, no such update is + available for the 2010 models. */ + ACPI_BATTERY_QUIRK_THINKPAD_MAH, +}; + +struct acpi_battery { + struct mutex lock; + struct mutex sysfs_lock; + struct power_supply *bat; + struct power_supply_desc bat_desc; + struct acpi_device *device; + struct notifier_block pm_nb; + unsigned long update_time; + int revision; + int rate_now; + int capacity_now; + int voltage_now; + int design_capacity; + int full_charge_capacity; + int technology; + int design_voltage; + int design_capacity_warning; + int design_capacity_low; + int cycle_count; + int measurement_accuracy; + int max_sampling_time; + int min_sampling_time; + int max_averaging_interval; + int min_averaging_interval; + int capacity_granularity_1; + int capacity_granularity_2; + int alarm; + char model_number[32]; + char serial_number[32]; + char type[32]; + char oem_info[32]; + int state; + int power_unit; + unsigned long flags; +}; + +#define to_acpi_battery(x) power_supply_get_drvdata(x) + +static inline int acpi_battery_present(struct acpi_battery *battery) +{ + return battery->device->status.battery_present; +} + +static int acpi_battery_technology(struct acpi_battery *battery) +{ + if (!strcasecmp("NiCd", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_NiCd; + if (!strcasecmp("NiMH", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_NiMH; + if (!strcasecmp("LION", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_LION; + if (!strncasecmp("LI-ION", battery->type, 6)) + return POWER_SUPPLY_TECHNOLOGY_LION; + if (!strcasecmp("LiP", battery->type)) + return POWER_SUPPLY_TECHNOLOGY_LIPO; + return POWER_SUPPLY_TECHNOLOGY_UNKNOWN; +} + +static int acpi_battery_get_state(struct acpi_battery *battery); + +static int acpi_battery_is_charged(struct acpi_battery *battery) +{ + /* charging, discharging or critical low */ + if (battery->state != 0) + return 0; + + /* battery not reporting charge */ + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || + battery->capacity_now == 0) + return 0; + + /* good batteries update full_charge as the batteries degrade */ + if (battery->full_charge_capacity == battery->capacity_now) + return 1; + + /* fallback to using design values for broken batteries */ + if (battery->design_capacity == battery->capacity_now) + return 1; + + /* we don't do any sort of metric based on percentages */ + return 0; +} + +static int acpi_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct acpi_battery *battery = to_acpi_battery(psy); + + if (acpi_battery_present(battery)) { + /* run battery update only if it is present */ + acpi_battery_get_state(battery); + } else if (psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (battery->state & ACPI_BATTERY_STATE_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (battery->state & ACPI_BATTERY_STATE_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (acpi_battery_is_charged(battery)) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = acpi_battery_present(battery); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = acpi_battery_technology(battery); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = battery->cycle_count; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->design_voltage * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->voltage_now * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_POWER_NOW: + if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->rate_now * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->design_capacity * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL: + if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->full_charge_capacity * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_ENERGY_NOW: + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN) + ret = -ENODEV; + else + val->intval = battery->capacity_now * 1000; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (battery->capacity_now && battery->full_charge_capacity) + val->intval = battery->capacity_now * 100/ + battery->full_charge_capacity; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + if (battery->state & ACPI_BATTERY_STATE_CRITICAL) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else if (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && + (battery->capacity_now <= battery->alarm)) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else if (acpi_battery_is_charged(battery)) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = battery->model_number; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = battery->oem_info; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = battery->serial_number; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static enum power_supply_property charge_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + +static enum power_supply_property energy_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_SERIAL_NUMBER, +}; + +#ifdef CONFIG_ACPI_PROCFS_POWER +inline char *acpi_battery_units(struct acpi_battery *battery) +{ + return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ? + "mA" : "mW"; +} +#endif + +/* -------------------------------------------------------------------------- + Battery Management + -------------------------------------------------------------------------- */ +struct acpi_offsets { + size_t offset; /* offset inside struct acpi_sbs_battery */ + u8 mode; /* int or string? */ +}; + +static struct acpi_offsets state_offsets[] = { + {offsetof(struct acpi_battery, state), 0}, + {offsetof(struct acpi_battery, rate_now), 0}, + {offsetof(struct acpi_battery, capacity_now), 0}, + {offsetof(struct acpi_battery, voltage_now), 0}, +}; + +static struct acpi_offsets info_offsets[] = { + {offsetof(struct acpi_battery, power_unit), 0}, + {offsetof(struct acpi_battery, design_capacity), 0}, + {offsetof(struct acpi_battery, full_charge_capacity), 0}, + {offsetof(struct acpi_battery, technology), 0}, + {offsetof(struct acpi_battery, design_voltage), 0}, + {offsetof(struct acpi_battery, design_capacity_warning), 0}, + {offsetof(struct acpi_battery, design_capacity_low), 0}, + {offsetof(struct acpi_battery, capacity_granularity_1), 0}, + {offsetof(struct acpi_battery, capacity_granularity_2), 0}, + {offsetof(struct acpi_battery, model_number), 1}, + {offsetof(struct acpi_battery, serial_number), 1}, + {offsetof(struct acpi_battery, type), 1}, + {offsetof(struct acpi_battery, oem_info), 1}, +}; + +static struct acpi_offsets extended_info_offsets[] = { + {offsetof(struct acpi_battery, revision), 0}, + {offsetof(struct acpi_battery, power_unit), 0}, + {offsetof(struct acpi_battery, design_capacity), 0}, + {offsetof(struct acpi_battery, full_charge_capacity), 0}, + {offsetof(struct acpi_battery, technology), 0}, + {offsetof(struct acpi_battery, design_voltage), 0}, + {offsetof(struct acpi_battery, design_capacity_warning), 0}, + {offsetof(struct acpi_battery, design_capacity_low), 0}, + {offsetof(struct acpi_battery, cycle_count), 0}, + {offsetof(struct acpi_battery, measurement_accuracy), 0}, + {offsetof(struct acpi_battery, max_sampling_time), 0}, + {offsetof(struct acpi_battery, min_sampling_time), 0}, + {offsetof(struct acpi_battery, max_averaging_interval), 0}, + {offsetof(struct acpi_battery, min_averaging_interval), 0}, + {offsetof(struct acpi_battery, capacity_granularity_1), 0}, + {offsetof(struct acpi_battery, capacity_granularity_2), 0}, + {offsetof(struct acpi_battery, model_number), 1}, + {offsetof(struct acpi_battery, serial_number), 1}, + {offsetof(struct acpi_battery, type), 1}, + {offsetof(struct acpi_battery, oem_info), 1}, +}; + +static int extract_package(struct acpi_battery *battery, + union acpi_object *package, + struct acpi_offsets *offsets, int num) +{ + int i; + union acpi_object *element; + if (package->type != ACPI_TYPE_PACKAGE) + return -EFAULT; + for (i = 0; i < num; ++i) { + if (package->package.count <= i) + return -EFAULT; + element = &package->package.elements[i]; + if (offsets[i].mode) { + u8 *ptr = (u8 *)battery + offsets[i].offset; + if (element->type == ACPI_TYPE_STRING || + element->type == ACPI_TYPE_BUFFER) + strncpy(ptr, element->string.pointer, 32); + else if (element->type == ACPI_TYPE_INTEGER) { + strncpy(ptr, (u8 *)&element->integer.value, + sizeof(u64)); + ptr[sizeof(u64)] = 0; + } else + *ptr = 0; /* don't have value */ + } else { + int *x = (int *)((u8 *)battery + offsets[i].offset); + *x = (element->type == ACPI_TYPE_INTEGER) ? + element->integer.value : -1; + } + } + return 0; +} + +static int acpi_battery_get_status(struct acpi_battery *battery) +{ + if (acpi_bus_get_status(battery->device)) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA")); + return -ENODEV; + } + return 0; +} + +static int acpi_battery_get_info(struct acpi_battery *battery) +{ + int result = -EFAULT; + acpi_status status = 0; + char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags) ? + "_BIX" : "_BIF"; + + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + if (!acpi_battery_present(battery)) + return 0; + mutex_lock(&battery->lock); + status = acpi_evaluate_object(battery->device->handle, name, + NULL, &buffer); + mutex_unlock(&battery->lock); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name)); + return -ENODEV; + } + + if (battery_bix_broken_package) + result = extract_package(battery, buffer.pointer, + extended_info_offsets + 1, + ARRAY_SIZE(extended_info_offsets) - 1); + else if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags)) + result = extract_package(battery, buffer.pointer, + extended_info_offsets, + ARRAY_SIZE(extended_info_offsets)); + else + result = extract_package(battery, buffer.pointer, + info_offsets, ARRAY_SIZE(info_offsets)); + kfree(buffer.pointer); + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) + battery->full_charge_capacity = battery->design_capacity; + if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) && + battery->power_unit && battery->design_voltage) { + battery->design_capacity = battery->design_capacity * + 10000 / battery->design_voltage; + battery->full_charge_capacity = battery->full_charge_capacity * + 10000 / battery->design_voltage; + battery->design_capacity_warning = + battery->design_capacity_warning * + 10000 / battery->design_voltage; + /* Curiously, design_capacity_low, unlike the rest of them, + is correct. */ + /* capacity_granularity_* equal 1 on the systems tested, so + it's impossible to tell if they would need an adjustment + or not if their values were higher. */ + } + return result; +} + +static int acpi_battery_get_state(struct acpi_battery *battery) +{ + int result = 0; + acpi_status status = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + if (!acpi_battery_present(battery)) + return 0; + + if (battery->update_time && + time_before(jiffies, battery->update_time + + msecs_to_jiffies(cache_time))) + return 0; + + mutex_lock(&battery->lock); + status = acpi_evaluate_object(battery->device->handle, "_BST", + NULL, &buffer); + mutex_unlock(&battery->lock); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST")); + return -ENODEV; + } + + result = extract_package(battery, buffer.pointer, + state_offsets, ARRAY_SIZE(state_offsets)); + battery->update_time = jiffies; + kfree(buffer.pointer); + + /* For buggy DSDTs that report negative 16-bit values for either + * charging or discharging current and/or report 0 as 65536 + * due to bad math. + */ + if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA && + battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && + (s16)(battery->rate_now) < 0) { + battery->rate_now = abs((s16)battery->rate_now); + printk_once(KERN_WARNING FW_BUG + "battery: (dis)charge rate invalid.\n"); + } + + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags) + && battery->capacity_now >= 0 && battery->capacity_now <= 100) + battery->capacity_now = (battery->capacity_now * + battery->full_charge_capacity) / 100; + if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) && + battery->power_unit && battery->design_voltage) { + battery->capacity_now = battery->capacity_now * + 10000 / battery->design_voltage; + } + return result; +} + +static int acpi_battery_set_alarm(struct acpi_battery *battery) +{ + acpi_status status = 0; + + if (!acpi_battery_present(battery) || + !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) + return -ENODEV; + + mutex_lock(&battery->lock); + status = acpi_execute_simple_method(battery->device->handle, "_BTP", + battery->alarm); + mutex_unlock(&battery->lock); + + if (ACPI_FAILURE(status)) + return -ENODEV; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm)); + return 0; +} + +static int acpi_battery_init_alarm(struct acpi_battery *battery) +{ + /* See if alarms are supported, and if so, set default */ + if (!acpi_has_method(battery->device->handle, "_BTP")) { + clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); + return 0; + } + set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); + if (!battery->alarm) + battery->alarm = battery->design_capacity_warning; + return acpi_battery_set_alarm(battery); +} + +static ssize_t acpi_battery_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + return sprintf(buf, "%d\n", battery->alarm * 1000); +} + +static ssize_t acpi_battery_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long x; + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + if (sscanf(buf, "%lu\n", &x) == 1) + battery->alarm = x/1000; + if (acpi_battery_present(battery)) + acpi_battery_set_alarm(battery); + return count; +} + +static struct device_attribute alarm_attr = { + .attr = {.name = "alarm", .mode = 0644}, + .show = acpi_battery_alarm_show, + .store = acpi_battery_alarm_store, +}; + +static int sysfs_add_battery(struct acpi_battery *battery) +{ + struct power_supply_config psy_cfg = { .drv_data = battery, }; + + if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) { + battery->bat_desc.properties = charge_battery_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(charge_battery_props); + } else { + battery->bat_desc.properties = energy_battery_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(energy_battery_props); + } + + battery->bat_desc.name = acpi_device_bid(battery->device); + battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + battery->bat_desc.get_property = acpi_battery_get_property; + + battery->bat = power_supply_register_no_ws(&battery->device->dev, + &battery->bat_desc, &psy_cfg); + + if (IS_ERR(battery->bat)) { + int result = PTR_ERR(battery->bat); + + battery->bat = NULL; + return result; + } + return device_create_file(&battery->bat->dev, &alarm_attr); +} + +static void sysfs_remove_battery(struct acpi_battery *battery) +{ + mutex_lock(&battery->sysfs_lock); + if (!battery->bat) { + mutex_unlock(&battery->sysfs_lock); + return; + } + + device_remove_file(&battery->bat->dev, &alarm_attr); + power_supply_unregister(battery->bat); + battery->bat = NULL; + mutex_unlock(&battery->sysfs_lock); +} + +static void find_battery(const struct dmi_header *dm, void *private) +{ + struct acpi_battery *battery = (struct acpi_battery *)private; + /* Note: the hardcoded offsets below have been extracted from + the source code of dmidecode. */ + if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) { + const u8 *dmi_data = (const u8 *)(dm + 1); + int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6)); + if (dm->length >= 18) + dmi_capacity *= dmi_data[17]; + if (battery->design_capacity * battery->design_voltage / 1000 + != dmi_capacity && + battery->design_capacity * 10 == dmi_capacity) + set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, + &battery->flags); + } +} + +/* + * According to the ACPI spec, some kinds of primary batteries can + * report percentage battery remaining capacity directly to OS. + * In this case, it reports the Last Full Charged Capacity == 100 + * and BatteryPresentRate == 0xFFFFFFFF. + * + * Now we found some battery reports percentage remaining capacity + * even if it's rechargeable. + * https://bugzilla.kernel.org/show_bug.cgi?id=15979 + * + * Handle this correctly so that they won't break userspace. + */ +static void acpi_battery_quirks(struct acpi_battery *battery) +{ + if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) + return; + + if (battery->full_charge_capacity == 100 && + battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN && + battery->capacity_now >= 0 && battery->capacity_now <= 100) { + set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags); + battery->full_charge_capacity = battery->design_capacity; + battery->capacity_now = (battery->capacity_now * + battery->full_charge_capacity) / 100; + } + + if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags)) + return; + + if (battery->power_unit && dmi_name_in_vendors("LENOVO")) { + const char *s; + s = dmi_get_system_info(DMI_PRODUCT_VERSION); + if (s && !strncasecmp(s, "ThinkPad", 8)) { + dmi_walk(find_battery, battery); + if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, + &battery->flags) && + battery->design_voltage) { + battery->design_capacity = + battery->design_capacity * + 10000 / battery->design_voltage; + battery->full_charge_capacity = + battery->full_charge_capacity * + 10000 / battery->design_voltage; + battery->design_capacity_warning = + battery->design_capacity_warning * + 10000 / battery->design_voltage; + battery->capacity_now = battery->capacity_now * + 10000 / battery->design_voltage; + } + } + } +} + +static int acpi_battery_update(struct acpi_battery *battery, bool resume) +{ + int result, old_present = acpi_battery_present(battery); + result = acpi_battery_get_status(battery); + if (result) + return result; + if (!acpi_battery_present(battery)) { + sysfs_remove_battery(battery); + battery->update_time = 0; + return 0; + } + + if (resume) + return 0; + + if (!battery->update_time || + old_present != acpi_battery_present(battery)) { + result = acpi_battery_get_info(battery); + if (result) + return result; + acpi_battery_init_alarm(battery); + } + if (!battery->bat) { + result = sysfs_add_battery(battery); + if (result) + return result; + } + result = acpi_battery_get_state(battery); + if (result) + return result; + acpi_battery_quirks(battery); + + /* + * Wakeup the system if battery is critical low + * or lower than the alarm level + */ + if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || + (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && + (battery->capacity_now <= battery->alarm))) + pm_wakeup_event(&battery->device->dev, 0); + + return result; +} + +static void acpi_battery_refresh(struct acpi_battery *battery) +{ + int power_unit; + + if (!battery->bat) + return; + + power_unit = battery->power_unit; + + acpi_battery_get_info(battery); + + if (power_unit == battery->power_unit) + return; + + /* The battery has changed its reporting units. */ + sysfs_remove_battery(battery); + sysfs_add_battery(battery); +} + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_PROCFS_POWER +static struct proc_dir_entry *acpi_battery_dir; + +static int acpi_battery_print_info(struct seq_file *seq, int result) +{ + struct acpi_battery *battery = seq->private; + + if (result) + goto end; + + seq_printf(seq, "present: %s\n", + acpi_battery_present(battery) ? "yes" : "no"); + if (!acpi_battery_present(battery)) + goto end; + if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "design capacity: unknown\n"); + else + seq_printf(seq, "design capacity: %d %sh\n", + battery->design_capacity, + acpi_battery_units(battery)); + + if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "last full capacity: unknown\n"); + else + seq_printf(seq, "last full capacity: %d %sh\n", + battery->full_charge_capacity, + acpi_battery_units(battery)); + + seq_printf(seq, "battery technology: %srechargeable\n", + (!battery->technology)?"non-":""); + + if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "design voltage: unknown\n"); + else + seq_printf(seq, "design voltage: %d mV\n", + battery->design_voltage); + seq_printf(seq, "design capacity warning: %d %sh\n", + battery->design_capacity_warning, + acpi_battery_units(battery)); + seq_printf(seq, "design capacity low: %d %sh\n", + battery->design_capacity_low, + acpi_battery_units(battery)); + seq_printf(seq, "cycle count: %i\n", battery->cycle_count); + seq_printf(seq, "capacity granularity 1: %d %sh\n", + battery->capacity_granularity_1, + acpi_battery_units(battery)); + seq_printf(seq, "capacity granularity 2: %d %sh\n", + battery->capacity_granularity_2, + acpi_battery_units(battery)); + seq_printf(seq, "model number: %s\n", battery->model_number); + seq_printf(seq, "serial number: %s\n", battery->serial_number); + seq_printf(seq, "battery type: %s\n", battery->type); + seq_printf(seq, "OEM info: %s\n", battery->oem_info); + end: + if (result) + seq_printf(seq, "ERROR: Unable to read battery info\n"); + return result; +} + +static int acpi_battery_print_state(struct seq_file *seq, int result) +{ + struct acpi_battery *battery = seq->private; + + if (result) + goto end; + + seq_printf(seq, "present: %s\n", + acpi_battery_present(battery) ? "yes" : "no"); + if (!acpi_battery_present(battery)) + goto end; + + seq_printf(seq, "capacity state: %s\n", + (battery->state & 0x04) ? "critical" : "ok"); + if ((battery->state & 0x01) && (battery->state & 0x02)) + seq_printf(seq, + "charging state: charging/discharging\n"); + else if (battery->state & 0x01) + seq_printf(seq, "charging state: discharging\n"); + else if (battery->state & 0x02) + seq_printf(seq, "charging state: charging\n"); + else + seq_printf(seq, "charging state: charged\n"); + + if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "present rate: unknown\n"); + else + seq_printf(seq, "present rate: %d %s\n", + battery->rate_now, acpi_battery_units(battery)); + + if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "remaining capacity: unknown\n"); + else + seq_printf(seq, "remaining capacity: %d %sh\n", + battery->capacity_now, acpi_battery_units(battery)); + if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "present voltage: unknown\n"); + else + seq_printf(seq, "present voltage: %d mV\n", + battery->voltage_now); + end: + if (result) + seq_printf(seq, "ERROR: Unable to read battery state\n"); + + return result; +} + +static int acpi_battery_print_alarm(struct seq_file *seq, int result) +{ + struct acpi_battery *battery = seq->private; + + if (result) + goto end; + + if (!acpi_battery_present(battery)) { + seq_printf(seq, "present: no\n"); + goto end; + } + seq_printf(seq, "alarm: "); + if (!battery->alarm) + seq_printf(seq, "unsupported\n"); + else + seq_printf(seq, "%u %sh\n", battery->alarm, + acpi_battery_units(battery)); + end: + if (result) + seq_printf(seq, "ERROR: Unable to read battery alarm\n"); + return result; +} + +static ssize_t acpi_battery_write_alarm(struct file *file, + const char __user * buffer, + size_t count, loff_t * ppos) +{ + int result = 0; + char alarm_string[12] = { '\0' }; + struct seq_file *m = file->private_data; + struct acpi_battery *battery = m->private; + + if (!battery || (count > sizeof(alarm_string) - 1)) + return -EINVAL; + if (!acpi_battery_present(battery)) { + result = -ENODEV; + goto end; + } + if (copy_from_user(alarm_string, buffer, count)) { + result = -EFAULT; + goto end; + } + alarm_string[count] = '\0'; + if (kstrtoint(alarm_string, 0, &battery->alarm)) { + result = -EINVAL; + goto end; + } + result = acpi_battery_set_alarm(battery); + end: + if (!result) + return count; + return result; +} + +typedef int(*print_func)(struct seq_file *seq, int result); + +static print_func acpi_print_funcs[ACPI_BATTERY_NUMFILES] = { + acpi_battery_print_info, + acpi_battery_print_state, + acpi_battery_print_alarm, +}; + +static int acpi_battery_read(int fid, struct seq_file *seq) +{ + struct acpi_battery *battery = seq->private; + int result = acpi_battery_update(battery, false); + return acpi_print_funcs[fid](seq, result); +} + +#define DECLARE_FILE_FUNCTIONS(_name) \ +static int acpi_battery_read_##_name(struct seq_file *seq, void *offset) \ +{ \ + return acpi_battery_read(_name##_tag, seq); \ +} \ +static int acpi_battery_##_name##_open_fs(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, acpi_battery_read_##_name, PDE_DATA(inode)); \ +} + +DECLARE_FILE_FUNCTIONS(info); +DECLARE_FILE_FUNCTIONS(state); +DECLARE_FILE_FUNCTIONS(alarm); + +#undef DECLARE_FILE_FUNCTIONS + +#define FILE_DESCRIPTION_RO(_name) \ + { \ + .name = __stringify(_name), \ + .mode = S_IRUGO, \ + .ops = { \ + .open = acpi_battery_##_name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define FILE_DESCRIPTION_RW(_name) \ + { \ + .name = __stringify(_name), \ + .mode = S_IFREG | S_IRUGO | S_IWUSR, \ + .ops = { \ + .open = acpi_battery_##_name##_open_fs, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .write = acpi_battery_write_##_name, \ + .release = single_release, \ + .owner = THIS_MODULE, \ + }, \ + } + +static const struct battery_file { + struct file_operations ops; + umode_t mode; + const char *name; +} acpi_battery_file[] = { + FILE_DESCRIPTION_RO(info), + FILE_DESCRIPTION_RO(state), + FILE_DESCRIPTION_RW(alarm), +}; + +#undef FILE_DESCRIPTION_RO +#undef FILE_DESCRIPTION_RW + +static int acpi_battery_add_fs(struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + int i; + + printk(KERN_WARNING PREFIX "Deprecated procfs I/F for battery is loaded," + " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_battery_dir); + if (!acpi_device_dir(device)) + return -ENODEV; + } + + for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) { + entry = proc_create_data(acpi_battery_file[i].name, + acpi_battery_file[i].mode, + acpi_device_dir(device), + &acpi_battery_file[i].ops, + acpi_driver_data(device)); + if (!entry) + return -ENODEV; + } + return 0; +} + +static void acpi_battery_remove_fs(struct acpi_device *device) +{ + int i; + if (!acpi_device_dir(device)) + return; + for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) + remove_proc_entry(acpi_battery_file[i].name, + acpi_device_dir(device)); + + remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); + acpi_device_dir(device) = NULL; +} + +#endif + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static void acpi_battery_notify(struct acpi_device *device, u32 event) +{ + struct acpi_battery *battery = acpi_driver_data(device); + struct power_supply *old; + + if (!battery) + return; + old = battery->bat; + /* + * On Acer Aspire V5-573G notifications are sometimes triggered too + * early. For example, when AC is unplugged and notification is + * triggered, battery state is still reported as "Full", and changes to + * "Discharging" only after short delay, without any notification. + */ + if (battery_notification_delay_ms > 0) + msleep(battery_notification_delay_ms); + if (event == ACPI_BATTERY_NOTIFY_INFO) + acpi_battery_refresh(battery); + acpi_battery_update(battery, false); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, + acpi_battery_present(battery)); + acpi_notifier_call_chain(device, event, acpi_battery_present(battery)); + /* acpi_battery_update could remove power_supply object */ + if (old && battery->bat) + power_supply_changed(battery->bat); +} + +static int battery_notify(struct notifier_block *nb, + unsigned long mode, void *_unused) +{ + struct acpi_battery *battery = container_of(nb, struct acpi_battery, + pm_nb); + int result; + + switch (mode) { + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + if (!acpi_battery_present(battery)) + return 0; + + if (!battery->bat) { + result = acpi_battery_get_info(battery); + if (result) + return result; + + result = sysfs_add_battery(battery); + if (result) + return result; + } else + acpi_battery_refresh(battery); + + acpi_battery_init_alarm(battery); + acpi_battery_get_state(battery); + break; + } + + return 0; +} + +static int battery_bix_broken_package_quirk(const struct dmi_system_id *d) +{ + battery_bix_broken_package = 1; + return 0; +} + +static int battery_notification_delay_quirk(const struct dmi_system_id *d) +{ + battery_notification_delay_ms = 1000; + return 0; +} + +static struct dmi_system_id bat_dmi_table[] = { + { + .callback = battery_bix_broken_package_quirk, + .ident = "NEC LZ750/LS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "NEC"), + DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"), + }, + }, + { + .callback = battery_notification_delay_quirk, + .ident = "Acer Aspire V5-573G", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-573G"), + }, + }, + {}, +}; + +/* + * Some machines'(E,G Lenovo Z480) ECs are not stable + * during boot up and this causes battery driver fails to be + * probed due to failure of getting battery information + * from EC sometimes. After several retries, the operation + * may work. So add retry code here and 20ms sleep between + * every retries. + */ +static int acpi_battery_update_retry(struct acpi_battery *battery) +{ + int retry, ret; + + for (retry = 5; retry; retry--) { + ret = acpi_battery_update(battery, false); + if (!ret) + break; + + msleep(20); + } + return ret; +} + +static int acpi_battery_add(struct acpi_device *device) +{ + int result = 0; + struct acpi_battery *battery = NULL; + + if (!device) + return -EINVAL; + + if (device->dep_unmet) + return -EPROBE_DEFER; + + battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL); + if (!battery) + return -ENOMEM; + battery->device = device; + strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); + device->driver_data = battery; + mutex_init(&battery->lock); + mutex_init(&battery->sysfs_lock); + if (acpi_has_method(battery->device->handle, "_BIX")) + set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); + + result = acpi_battery_update_retry(battery); + if (result) + goto fail; + +#ifdef CONFIG_ACPI_PROCFS_POWER + result = acpi_battery_add_fs(device); +#endif + if (result) { +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_battery_remove_fs(device); +#endif + goto fail; + } + + printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n", + ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), + device->status.battery_present ? "present" : "absent"); + + battery->pm_nb.notifier_call = battery_notify; + register_pm_notifier(&battery->pm_nb); + + device_init_wakeup(&device->dev, 1); + + return result; + +fail: + sysfs_remove_battery(battery); + mutex_destroy(&battery->lock); + mutex_destroy(&battery->sysfs_lock); + kfree(battery); + return result; +} + +static int acpi_battery_remove(struct acpi_device *device) +{ + struct acpi_battery *battery = NULL; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + device_init_wakeup(&device->dev, 0); + battery = acpi_driver_data(device); + unregister_pm_notifier(&battery->pm_nb); +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_battery_remove_fs(device); +#endif + sysfs_remove_battery(battery); + mutex_destroy(&battery->lock); + mutex_destroy(&battery->sysfs_lock); + kfree(battery); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/* this is needed to learn about changes made in suspended state */ +static int acpi_battery_resume(struct device *dev) +{ + struct acpi_battery *battery; + + if (!dev) + return -EINVAL; + + battery = acpi_driver_data(to_acpi_device(dev)); + if (!battery) + return -EINVAL; + + battery->update_time = 0; + acpi_battery_update(battery, true); + return 0; +} +#else +#define acpi_battery_resume NULL +#endif + +static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume); + +static struct acpi_driver acpi_battery_driver = { + .name = "battery", + .class = ACPI_BATTERY_CLASS, + .ids = battery_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = acpi_battery_add, + .remove = acpi_battery_remove, + .notify = acpi_battery_notify, + }, + .drv.pm = &acpi_battery_pm, +}; + +static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) +{ + if (acpi_disabled) + return; + + dmi_check_system(bat_dmi_table); + +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_battery_dir = acpi_lock_battery_dir(); + if (!acpi_battery_dir) + return; +#endif + if (acpi_bus_register_driver(&acpi_battery_driver) < 0) { +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_unlock_battery_dir(acpi_battery_dir); +#endif + return; + } + return; +} + +static int __init acpi_battery_init(void) +{ + async_schedule(acpi_battery_init_async, NULL); + return 0; +} + +static void __exit acpi_battery_exit(void) +{ + acpi_bus_unregister_driver(&acpi_battery_driver); +#ifdef CONFIG_ACPI_PROCFS_POWER + acpi_unlock_battery_dir(acpi_battery_dir); +#endif +} + +module_init(acpi_battery_init); +module_exit(acpi_battery_exit); diff --git a/kernel/drivers/acpi/battery.h b/kernel/drivers/acpi/battery.h new file mode 100644 index 000000000..6c0849769 --- /dev/null +++ b/kernel/drivers/acpi/battery.h @@ -0,0 +1,10 @@ +#ifndef __ACPI_BATTERY_H +#define __ACPI_BATTERY_H + +#define ACPI_BATTERY_CLASS "battery" + +#define ACPI_BATTERY_NOTIFY_STATUS 0x80 +#define ACPI_BATTERY_NOTIFY_INFO 0x81 +#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82 + +#endif diff --git a/kernel/drivers/acpi/bgrt.c b/kernel/drivers/acpi/bgrt.c new file mode 100644 index 000000000..a83e3c62c --- /dev/null +++ b/kernel/drivers/acpi/bgrt.c @@ -0,0 +1,111 @@ +/* + * Copyright 2012 Red Hat, Inc + * Copyright 2012 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +static struct kobject *bgrt_kobj; + +static ssize_t show_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->version); +} +static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->status); +} +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + +static ssize_t show_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_type); +} +static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); + +static ssize_t show_xoffset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_x); +} +static DEVICE_ATTR(xoffset, S_IRUGO, show_xoffset, NULL); + +static ssize_t show_yoffset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", bgrt_tab->image_offset_y); +} +static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL); + +static ssize_t image_read(struct file *file, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + memcpy(buf, attr->private + off, count); + return count; +} + +static BIN_ATTR_RO(image, 0); /* size gets filled in later */ + +static struct attribute *bgrt_attributes[] = { + &dev_attr_version.attr, + &dev_attr_status.attr, + &dev_attr_type.attr, + &dev_attr_xoffset.attr, + &dev_attr_yoffset.attr, + NULL, +}; + +static struct bin_attribute *bgrt_bin_attributes[] = { + &bin_attr_image, + NULL, +}; + +static struct attribute_group bgrt_attribute_group = { + .attrs = bgrt_attributes, + .bin_attrs = bgrt_bin_attributes, +}; + +static int __init bgrt_init(void) +{ + int ret; + + if (!bgrt_image) + return -ENODEV; + + bin_attr_image.private = bgrt_image; + bin_attr_image.size = bgrt_image_size; + + bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj); + if (!bgrt_kobj) + return -EINVAL; + + ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group); + if (ret) + goto out_kobject; + + return 0; + +out_kobject: + kobject_put(bgrt_kobj); + return ret; +} + +module_init(bgrt_init); + +MODULE_AUTHOR("Matthew Garrett, Josh Triplett "); +MODULE_DESCRIPTION("BGRT boot graphic support"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/blacklist.c b/kernel/drivers/acpi/blacklist.c new file mode 100644 index 000000000..1d1791935 --- /dev/null +++ b/kernel/drivers/acpi/blacklist.c @@ -0,0 +1,331 @@ +/* + * blacklist.c + * + * Check to see if the given machine has a known bad ACPI BIOS + * or if the BIOS is too old. + * Check given machine against acpi_osi_dmi_table[]. + * + * Copyright (C) 2004 Len Brown + * Copyright (C) 2002 Andy Grover + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include + +#include "internal.h" + +enum acpi_blacklist_predicates { + all_versions, + less_than_or_equal, + equal, + greater_than_or_equal, +}; + +struct acpi_blacklist_item { + char oem_id[7]; + char oem_table_id[9]; + u32 oem_revision; + char *table; + enum acpi_blacklist_predicates oem_revision_predicate; + char *reason; + u32 is_critical_error; +}; + +static struct dmi_system_id acpi_osi_dmi_table[] __initdata; + +/* + * POLICY: If *anything* doesn't work, put it on the blacklist. + * If they are critical errors, mark it critical, and abort driver load. + */ +static struct acpi_blacklist_item acpi_blacklist[] __initdata = { + /* Compaq Presario 1700 */ + {"PTLTD ", " DSDT ", 0x06040000, ACPI_SIG_DSDT, less_than_or_equal, + "Multiple problems", 1}, + /* Sony FX120, FX140, FX150? */ + {"SONY ", "U0 ", 0x20010313, ACPI_SIG_DSDT, less_than_or_equal, + "ACPI driver problem", 1}, + /* Compaq Presario 800, Insyde BIOS */ + {"INT440", "SYSFexxx", 0x00001001, ACPI_SIG_DSDT, less_than_or_equal, + "Does not use _REG to protect EC OpRegions", 1}, + /* IBM 600E - _ADR should return 7, but it returns 1 */ + {"IBM ", "TP600E ", 0x00000105, ACPI_SIG_DSDT, less_than_or_equal, + "Incorrect _ADR", 1}, + + {""} +}; + +int __init acpi_blacklisted(void) +{ + int i = 0; + int blacklisted = 0; + struct acpi_table_header table_header; + + while (acpi_blacklist[i].oem_id[0] != '\0') { + if (acpi_get_table_header(acpi_blacklist[i].table, 0, &table_header)) { + i++; + continue; + } + + if (strncmp(acpi_blacklist[i].oem_id, table_header.oem_id, 6)) { + i++; + continue; + } + + if (strncmp + (acpi_blacklist[i].oem_table_id, table_header.oem_table_id, + 8)) { + i++; + continue; + } + + if ((acpi_blacklist[i].oem_revision_predicate == all_versions) + || (acpi_blacklist[i].oem_revision_predicate == + less_than_or_equal + && table_header.oem_revision <= + acpi_blacklist[i].oem_revision) + || (acpi_blacklist[i].oem_revision_predicate == + greater_than_or_equal + && table_header.oem_revision >= + acpi_blacklist[i].oem_revision) + || (acpi_blacklist[i].oem_revision_predicate == equal + && table_header.oem_revision == + acpi_blacklist[i].oem_revision)) { + + printk(KERN_ERR PREFIX + "Vendor \"%6.6s\" System \"%8.8s\" " + "Revision 0x%x has a known ACPI BIOS problem.\n", + acpi_blacklist[i].oem_id, + acpi_blacklist[i].oem_table_id, + acpi_blacklist[i].oem_revision); + + printk(KERN_ERR PREFIX + "Reason: %s. This is a %s error\n", + acpi_blacklist[i].reason, + (acpi_blacklist[i]. + is_critical_error ? "non-recoverable" : + "recoverable")); + + blacklisted = acpi_blacklist[i].is_critical_error; + break; + } else { + i++; + } + } + + dmi_check_system(acpi_osi_dmi_table); + + return blacklisted; +} +#ifdef CONFIG_DMI +static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) +{ + acpi_dmi_osi_linux(1, d); /* enable */ + return 0; +} +static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) +{ + printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + acpi_osi_setup("!Windows 2006"); + acpi_osi_setup("!Windows 2006 SP1"); + acpi_osi_setup("!Windows 2006 SP2"); + return 0; +} +static int __init dmi_disable_osi_win7(const struct dmi_system_id *d) +{ + printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + acpi_osi_setup("!Windows 2009"); + return 0; +} +static int __init dmi_disable_osi_win8(const struct dmi_system_id *d) +{ + printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + acpi_osi_setup("!Windows 2012"); + return 0; +} + +static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { + { + .callback = dmi_disable_osi_vista, + .ident = "Fujitsu Siemens", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), + }, + }, + { + /* + * There have a NVIF method in MSI GX723 DSDT need call by Nvidia + * driver (e.g. nouveau) when user press brightness hotkey. + * Currently, nouveau driver didn't do the job and it causes there + * have a infinite while loop in DSDT when user press hotkey. + * We add MSI GX723's dmi information to this table for workaround + * this issue. + * Will remove MSI GX723 from the table after nouveau grows support. + */ + .callback = dmi_disable_osi_vista, + .ident = "MSI GX723", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX723"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Sony VGN-NS10J_S", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Sony VGN-SR290J", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "VGN-NS50B_L", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "VGN-SR19XN", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Toshiba Satellite L355", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"), + }, + }, + { + .callback = dmi_disable_osi_win7, + .ident = "ASUS K50IJ", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Toshiba P305D", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Toshiba NB100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "NB100"), + }, + }, + + /* + * The wireless hotkey does not work on those machines when + * returning true for _OSI("Windows 2012") + */ + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 7737", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 7537", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 5437", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 3437", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Vostro 3446", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Vostro 3546", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"), + }, + }, + + /* + * BIOS invocation of _OSI(Linux) is almost always a BIOS bug. + * Linux ignores it, except for the machines enumerated below. + */ + + /* + * Without this this EEEpc exports a non working WMI interface, with + * this it exports a working "good old" eeepc_laptop interface, fixing + * both brightness control, and rfkill not working. + */ + { + .callback = dmi_enable_osi_linux, + .ident = "Asus EEE PC 1015PX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"), + }, + }, + {} +}; + +#endif /* CONFIG_DMI */ diff --git a/kernel/drivers/acpi/bus.c b/kernel/drivers/acpi/bus.c new file mode 100644 index 000000000..513e7230e --- /dev/null +++ b/kernel/drivers/acpi/bus.c @@ -0,0 +1,706 @@ +/* + * acpi_bus.c - ACPI Bus Driver ($Revision: 80 $) + * + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_X86 +#include +#endif +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_BUS_COMPONENT +ACPI_MODULE_NAME("bus"); + +struct acpi_device *acpi_root; +struct proc_dir_entry *acpi_root_dir; +EXPORT_SYMBOL(acpi_root_dir); + +#ifdef CONFIG_X86 +#ifdef CONFIG_ACPI_CUSTOM_DSDT +static inline int set_copy_dsdt(const struct dmi_system_id *id) +{ + return 0; +} +#else +static int set_copy_dsdt(const struct dmi_system_id *id) +{ + printk(KERN_NOTICE "%s detected - " + "force copy of DSDT to local memory\n", id->ident); + acpi_gbl_copy_dsdt_locally = 1; + return 0; +} +#endif + +static struct dmi_system_id dsdt_dmi_table[] __initdata = { + /* + * Invoke DSDT corruption work-around on all Toshiba Satellite. + * https://bugzilla.kernel.org/show_bug.cgi?id=14679 + */ + { + .callback = set_copy_dsdt, + .ident = "TOSHIBA Satellite", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), + }, + }, + {} +}; +#else +static struct dmi_system_id dsdt_dmi_table[] __initdata = { + {} +}; +#endif + +/* -------------------------------------------------------------------------- + Device Management + -------------------------------------------------------------------------- */ + +acpi_status acpi_bus_get_status_handle(acpi_handle handle, + unsigned long long *sta) +{ + acpi_status status; + + status = acpi_evaluate_integer(handle, "_STA", NULL, sta); + if (ACPI_SUCCESS(status)) + return AE_OK; + + if (status == AE_NOT_FOUND) { + *sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; + return AE_OK; + } + return status; +} + +int acpi_bus_get_status(struct acpi_device *device) +{ + acpi_status status; + unsigned long long sta; + + status = acpi_bus_get_status_handle(device->handle, &sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + + acpi_set_device_status(device, sta); + + if (device->status.functional && !device->status.present) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: " + "functional but not present;\n", + device->pnp.bus_id, (u32)sta)); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", + device->pnp.bus_id, (u32)sta)); + return 0; +} +EXPORT_SYMBOL(acpi_bus_get_status); + +void acpi_bus_private_data_handler(acpi_handle handle, + void *context) +{ + return; +} +EXPORT_SYMBOL(acpi_bus_private_data_handler); + +int acpi_bus_attach_private_data(acpi_handle handle, void *data) +{ + acpi_status status; + + status = acpi_attach_data(handle, + acpi_bus_private_data_handler, data); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "Error attaching device data\n"); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_bus_attach_private_data); + +int acpi_bus_get_private_data(acpi_handle handle, void **data) +{ + acpi_status status; + + if (!*data) + return -EINVAL; + + status = acpi_get_data(handle, acpi_bus_private_data_handler, data); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "No context for object\n"); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_bus_get_private_data); + +void acpi_bus_detach_private_data(acpi_handle handle) +{ + acpi_detach_data(handle, acpi_bus_private_data_handler); +} +EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); + +static void acpi_print_osc_error(acpi_handle handle, + struct acpi_osc_context *context, char *error) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; + int i; + + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) + printk(KERN_DEBUG "%s\n", error); + else { + printk(KERN_DEBUG "%s:%s\n", (char *)buffer.pointer, error); + kfree(buffer.pointer); + } + printk(KERN_DEBUG"_OSC request data:"); + for (i = 0; i < context->cap.length; i += sizeof(u32)) + printk("%x ", *((u32 *)(context->cap.pointer + i))); + printk("\n"); +} + +acpi_status acpi_str_to_uuid(char *str, u8 *uuid) +{ + int i; + static int opc_map_to_uuid[16] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, + 24, 26, 28, 30, 32, 34}; + + if (strlen(str) != 36) + return AE_BAD_PARAMETER; + for (i = 0; i < 36; i++) { + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (str[i] != '-') + return AE_BAD_PARAMETER; + } else if (!isxdigit(str[i])) + return AE_BAD_PARAMETER; + } + for (i = 0; i < 16; i++) { + uuid[i] = hex_to_bin(str[opc_map_to_uuid[i]]) << 4; + uuid[i] |= hex_to_bin(str[opc_map_to_uuid[i] + 1]); + } + return AE_OK; +} +EXPORT_SYMBOL_GPL(acpi_str_to_uuid); + +acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) +{ + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[4]; + union acpi_object *out_obj; + u8 uuid[16]; + u32 errors; + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + + if (!context) + return AE_ERROR; + if (ACPI_FAILURE(acpi_str_to_uuid(context->uuid_str, uuid))) + return AE_ERROR; + context->ret.length = ACPI_ALLOCATE_BUFFER; + context->ret.pointer = NULL; + + /* Setting up input parameters */ + input.count = 4; + input.pointer = in_params; + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = 16; + in_params[0].buffer.pointer = uuid; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = context->rev; + in_params[2].type = ACPI_TYPE_INTEGER; + in_params[2].integer.value = context->cap.length/sizeof(u32); + in_params[3].type = ACPI_TYPE_BUFFER; + in_params[3].buffer.length = context->cap.length; + in_params[3].buffer.pointer = context->cap.pointer; + + status = acpi_evaluate_object(handle, "_OSC", &input, &output); + if (ACPI_FAILURE(status)) + return status; + + if (!output.length) + return AE_NULL_OBJECT; + + out_obj = output.pointer; + if (out_obj->type != ACPI_TYPE_BUFFER + || out_obj->buffer.length != context->cap.length) { + acpi_print_osc_error(handle, context, + "_OSC evaluation returned wrong type"); + status = AE_TYPE; + goto out_kfree; + } + /* Need to ignore the bit0 in result code */ + errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); + if (errors) { + if (errors & OSC_REQUEST_ERROR) + acpi_print_osc_error(handle, context, + "_OSC request failed"); + if (errors & OSC_INVALID_UUID_ERROR) + acpi_print_osc_error(handle, context, + "_OSC invalid UUID"); + if (errors & OSC_INVALID_REVISION_ERROR) + acpi_print_osc_error(handle, context, + "_OSC invalid revision"); + if (errors & OSC_CAPABILITIES_MASK_ERROR) { + if (((u32 *)context->cap.pointer)[OSC_QUERY_DWORD] + & OSC_QUERY_ENABLE) + goto out_success; + status = AE_SUPPORT; + goto out_kfree; + } + status = AE_ERROR; + goto out_kfree; + } +out_success: + context->ret.length = out_obj->buffer.length; + context->ret.pointer = kmemdup(out_obj->buffer.pointer, + context->ret.length, GFP_KERNEL); + if (!context->ret.pointer) { + status = AE_NO_MEMORY; + goto out_kfree; + } + status = AE_OK; + +out_kfree: + kfree(output.pointer); + if (status != AE_OK) + context->ret.pointer = NULL; + return status; +} +EXPORT_SYMBOL(acpi_run_osc); + +bool osc_sb_apei_support_acked; +static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; +static void acpi_bus_osc_support(void) +{ + u32 capbuf[2]; + struct acpi_osc_context context = { + .uuid_str = sb_uuid_str, + .rev = 1, + .cap.length = 8, + .cap.pointer = capbuf, + }; + acpi_handle handle; + + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_DWORD] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ +#if defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR) ||\ + defined(CONFIG_ACPI_PROCESSOR_AGGREGATOR_MODULE) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; +#endif + +#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; +#endif + + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; + + if (!ghes_disable) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) + return; + if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) { + u32 *capbuf_ret = context.ret.pointer; + if (context.ret.length > OSC_SUPPORT_DWORD) + osc_sb_apei_support_acked = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; + kfree(context.ret.pointer); + } + /* do we need to check other returned cap? Sounds no */ +} + +/* -------------------------------------------------------------------------- + Notification Handling + -------------------------------------------------------------------------- */ + +/** + * acpi_bus_notify + * --------------- + * Callback for all 'system-level' device notifications (values 0x00-0x7F). + */ +static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) +{ + struct acpi_device *adev; + struct acpi_driver *driver; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + bool hotplug_event = false; + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); + hotplug_event = true; + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); + hotplug_event = true; + break; + + case ACPI_NOTIFY_DEVICE_WAKE: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n"); + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); + hotplug_event = true; + break; + + case ACPI_NOTIFY_DEVICE_CHECK_LIGHT: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n"); + /* TBD: Exactly what does 'light' mean? */ + break; + + case ACPI_NOTIFY_FREQUENCY_MISMATCH: + acpi_handle_err(handle, "Device cannot be configured due " + "to a frequency mismatch\n"); + break; + + case ACPI_NOTIFY_BUS_MODE_MISMATCH: + acpi_handle_err(handle, "Device cannot be configured due " + "to a bus mode mismatch\n"); + break; + + case ACPI_NOTIFY_POWER_FAULT: + acpi_handle_err(handle, "Device has suffered a power fault\n"); + break; + + default: + acpi_handle_debug(handle, "Unknown event type 0x%x\n", type); + break; + } + + adev = acpi_bus_get_acpi_device(handle); + if (!adev) + goto err; + + driver = adev->driver; + if (driver && driver->ops.notify && + (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS)) + driver->ops.notify(adev, type); + + if (hotplug_event && ACPI_SUCCESS(acpi_hotplug_schedule(adev, type))) + return; + + acpi_bus_put_acpi_device(adev); + return; + + err: + acpi_evaluate_ost(handle, type, ost_code, NULL); +} + +/* -------------------------------------------------------------------------- + Initialization/Cleanup + -------------------------------------------------------------------------- */ + +static int __init acpi_bus_init_irq(void) +{ + acpi_status status; + char *message = NULL; + + + /* + * Let the system know what interrupt model we are using by + * evaluating the \_PIC object, if exists. + */ + + switch (acpi_irq_model) { + case ACPI_IRQ_MODEL_PIC: + message = "PIC"; + break; + case ACPI_IRQ_MODEL_IOAPIC: + message = "IOAPIC"; + break; + case ACPI_IRQ_MODEL_IOSAPIC: + message = "IOSAPIC"; + break; + case ACPI_IRQ_MODEL_GIC: + message = "GIC"; + break; + case ACPI_IRQ_MODEL_PLATFORM: + message = "platform specific model"; + break; + default: + printk(KERN_WARNING PREFIX "Unknown interrupt routing model\n"); + return -ENODEV; + } + + printk(KERN_INFO PREFIX "Using %s for interrupt routing\n", message); + + status = acpi_execute_simple_method(NULL, "\\_PIC", acpi_irq_model); + if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PIC")); + return -ENODEV; + } + + return 0; +} + +/** + * acpi_early_init - Initialize ACPICA and populate the ACPI namespace. + * + * The ACPI tables are accessible after this, but the handling of events has not + * been initialized and the global lock is not available yet, so AML should not + * be executed at this point. + * + * Doing this before switching the EFI runtime services to virtual mode allows + * the EfiBootServices memory to be freed slightly earlier on boot. + */ +void __init acpi_early_init(void) +{ + acpi_status status; + + if (acpi_disabled) + return; + + printk(KERN_INFO PREFIX "Core revision %08x\n", ACPI_CA_VERSION); + + /* It's safe to verify table checksums during late stage */ + acpi_gbl_verify_table_checksum = TRUE; + + /* enable workarounds, unless strict ACPI spec. compliance */ + if (!acpi_strict) + acpi_gbl_enable_interpreter_slack = TRUE; + + acpi_gbl_permanent_mmap = 1; + + /* + * If the machine falls into the DMI check table, + * DSDT will be copied to memory + */ + dmi_check_system(dsdt_dmi_table); + + status = acpi_reallocate_root_table(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to reallocate ACPI tables\n"); + goto error0; + } + + status = acpi_initialize_subsystem(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to initialize the ACPI Interpreter\n"); + goto error0; + } + + status = acpi_load_tables(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to load the System Description Tables\n"); + goto error0; + } + +#ifdef CONFIG_X86 + if (!acpi_ioapic) { + /* compatible (0) means level (3) */ + if (!(acpi_sci_flags & ACPI_MADT_TRIGGER_MASK)) { + acpi_sci_flags &= ~ACPI_MADT_TRIGGER_MASK; + acpi_sci_flags |= ACPI_MADT_TRIGGER_LEVEL; + } + /* Set PIC-mode SCI trigger type */ + acpi_pic_sci_set_trigger(acpi_gbl_FADT.sci_interrupt, + (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK) >> 2); + } else { + /* + * now that acpi_gbl_FADT is initialized, + * update it with result from INT_SRC_OVR parsing + */ + acpi_gbl_FADT.sci_interrupt = acpi_sci_override_gsi; + } +#endif + return; + + error0: + disable_acpi(); +} + +/** + * acpi_subsystem_init - Finalize the early initialization of ACPI. + * + * Switch over the platform to the ACPI mode (if possible), initialize the + * handling of ACPI events, install the interrupt and global lock handlers. + * + * Doing this too early is generally unsafe, but at the same time it needs to be + * done before all things that really depend on ACPI. The right spot appears to + * be before finalizing the EFI initialization. + */ +void __init acpi_subsystem_init(void) +{ + acpi_status status; + + if (acpi_disabled) + return; + + status = acpi_enable_subsystem(~ACPI_NO_ACPI_ENABLE); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to enable ACPI\n"); + disable_acpi(); + } else { + /* + * If the system is using ACPI then we can be reasonably + * confident that any regulators are managed by the firmware + * so tell the regulator core it has everything it needs to + * know. + */ + regulator_has_full_constraints(); + } +} + +static int __init acpi_bus_init(void) +{ + int result; + acpi_status status; + + acpi_os_initialize1(); + + status = acpi_enable_subsystem(ACPI_NO_ACPI_ENABLE); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to start the ACPI Interpreter\n"); + goto error1; + } + + /* + * ACPI 2.0 requires the EC driver to be loaded and work before + * the EC device is found in the namespace (i.e. before acpi_initialize_objects() + * is called). + * + * This is accomplished by looking for the ECDT table, and getting + * the EC parameters out of that. + */ + status = acpi_ec_ecdt_probe(); + /* Ignore result. Not having an ECDT is not fatal. */ + + status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n"); + goto error1; + } + + /* + * _OSC method may exist in module level code, + * so it must be run after ACPI_FULL_INITIALIZATION + */ + acpi_bus_osc_support(); + + /* + * _PDC control method may load dynamic SSDT tables, + * and we need to install the table handler before that. + */ + acpi_sysfs_init(); + + acpi_early_processor_set_pdc(); + + /* + * Maybe EC region is required at bus_scan/acpi_get_devices. So it + * is necessary to enable it as early as possible. + */ + acpi_boot_ec_enable(); + + printk(KERN_INFO PREFIX "Interpreter enabled\n"); + + /* Initialize sleep structures */ + acpi_sleep_init(); + + /* + * Get the system interrupt model and evaluate \_PIC. + */ + result = acpi_bus_init_irq(); + if (result) + goto error1; + + /* + * Register the for all standard device notifications. + */ + status = + acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, + &acpi_bus_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to register for device notifications\n"); + goto error1; + } + + /* + * Create the top ACPI proc directory + */ + acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL); + + return 0; + + /* Mimic structured exception handling */ + error1: + acpi_terminate(); + return -ENODEV; +} + +struct kobject *acpi_kobj; +EXPORT_SYMBOL_GPL(acpi_kobj); + +static int __init acpi_init(void) +{ + int result; + + if (acpi_disabled) { + printk(KERN_INFO PREFIX "Interpreter disabled.\n"); + return -ENODEV; + } + + acpi_kobj = kobject_create_and_add("acpi", firmware_kobj); + if (!acpi_kobj) { + printk(KERN_WARNING "%s: kset create error\n", __func__); + acpi_kobj = NULL; + } + + init_acpi_device_notify(); + result = acpi_bus_init(); + if (result) { + disable_acpi(); + return result; + } + + pci_mmcfg_late_init(); + acpi_scan_init(); + acpi_ec_init(); + acpi_debugfs_init(); + acpi_sleep_proc_init(); + acpi_wakeup_device_init(); + return 0; +} + +subsys_initcall(acpi_init); diff --git a/kernel/drivers/acpi/button.c b/kernel/drivers/acpi/button.c new file mode 100644 index 000000000..6d5d1832a --- /dev/null +++ b/kernel/drivers/acpi/button.c @@ -0,0 +1,453 @@ +/* + * button.c - ACPI Button Driver + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +#define ACPI_BUTTON_CLASS "button" +#define ACPI_BUTTON_FILE_INFO "info" +#define ACPI_BUTTON_FILE_STATE "state" +#define ACPI_BUTTON_TYPE_UNKNOWN 0x00 +#define ACPI_BUTTON_NOTIFY_STATUS 0x80 + +#define ACPI_BUTTON_SUBCLASS_POWER "power" +#define ACPI_BUTTON_HID_POWER "PNP0C0C" +#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" +#define ACPI_BUTTON_TYPE_POWER 0x01 + +#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" +#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" +#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" +#define ACPI_BUTTON_TYPE_SLEEP 0x03 + +#define ACPI_BUTTON_SUBCLASS_LID "lid" +#define ACPI_BUTTON_HID_LID "PNP0C0D" +#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" +#define ACPI_BUTTON_TYPE_LID 0x05 + +#define _COMPONENT ACPI_BUTTON_COMPONENT +ACPI_MODULE_NAME("button"); + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI Button Driver"); +MODULE_LICENSE("GPL"); + +static const struct acpi_device_id button_device_ids[] = { + {ACPI_BUTTON_HID_LID, 0}, + {ACPI_BUTTON_HID_SLEEP, 0}, + {ACPI_BUTTON_HID_SLEEPF, 0}, + {ACPI_BUTTON_HID_POWER, 0}, + {ACPI_BUTTON_HID_POWERF, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, button_device_ids); + +static int acpi_button_add(struct acpi_device *device); +static int acpi_button_remove(struct acpi_device *device); +static void acpi_button_notify(struct acpi_device *device, u32 event); + +#ifdef CONFIG_PM_SLEEP +static int acpi_button_suspend(struct device *dev); +static int acpi_button_resume(struct device *dev); +#else +#define acpi_button_suspend NULL +#define acpi_button_resume NULL +#endif +static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume); + +static struct acpi_driver acpi_button_driver = { + .name = "button", + .class = ACPI_BUTTON_CLASS, + .ids = button_device_ids, + .ops = { + .add = acpi_button_add, + .remove = acpi_button_remove, + .notify = acpi_button_notify, + }, + .drv.pm = &acpi_button_pm, +}; + +struct acpi_button { + unsigned int type; + struct input_dev *input; + char phys[32]; /* for input device */ + unsigned long pushed; + bool suspended; +}; + +static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); +static struct acpi_device *lid_device; + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_button_dir; +static struct proc_dir_entry *acpi_lid_dir; + +static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_device *device = seq->private; + acpi_status status; + unsigned long long state; + + status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); + seq_printf(seq, "state: %s\n", + ACPI_FAILURE(status) ? "unsupported" : + (state ? "open" : "closed")); + return 0; +} + +static int acpi_button_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_button_state_seq_show, PDE_DATA(inode)); +} + +static const struct file_operations acpi_button_state_fops = { + .owner = THIS_MODULE, + .open = acpi_button_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_button_add_fs(struct acpi_device *device) +{ + struct acpi_button *button = acpi_driver_data(device); + struct proc_dir_entry *entry = NULL; + int ret = 0; + + /* procfs I/F for ACPI lid device only */ + if (button->type != ACPI_BUTTON_TYPE_LID) + return 0; + + if (acpi_button_dir || acpi_lid_dir) { + printk(KERN_ERR PREFIX "More than one Lid device found!\n"); + return -EEXIST; + } + + /* create /proc/acpi/button */ + acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); + if (!acpi_button_dir) + return -ENODEV; + + /* create /proc/acpi/button/lid */ + acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); + if (!acpi_lid_dir) { + ret = -ENODEV; + goto remove_button_dir; + } + + /* create /proc/acpi/button/lid/LID/ */ + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir); + if (!acpi_device_dir(device)) { + ret = -ENODEV; + goto remove_lid_dir; + } + + /* create /proc/acpi/button/lid/LID/state */ + entry = proc_create_data(ACPI_BUTTON_FILE_STATE, + S_IRUGO, acpi_device_dir(device), + &acpi_button_state_fops, device); + if (!entry) { + ret = -ENODEV; + goto remove_dev_dir; + } + +done: + return ret; + +remove_dev_dir: + remove_proc_entry(acpi_device_bid(device), + acpi_lid_dir); + acpi_device_dir(device) = NULL; +remove_lid_dir: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); +remove_button_dir: + remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); + goto done; +} + +static int acpi_button_remove_fs(struct acpi_device *device) +{ + struct acpi_button *button = acpi_driver_data(device); + + if (button->type != ACPI_BUTTON_TYPE_LID) + return 0; + + remove_proc_entry(ACPI_BUTTON_FILE_STATE, + acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), + acpi_lid_dir); + acpi_device_dir(device) = NULL; + remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir); + remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); + + return 0; +} + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ +int acpi_lid_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acpi_lid_notifier, nb); +} +EXPORT_SYMBOL(acpi_lid_notifier_register); + +int acpi_lid_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb); +} +EXPORT_SYMBOL(acpi_lid_notifier_unregister); + +int acpi_lid_open(void) +{ + acpi_status status; + unsigned long long state; + + if (!lid_device) + return -ENODEV; + + status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL, + &state); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return !!state; +} +EXPORT_SYMBOL(acpi_lid_open); + +static int acpi_lid_send_state(struct acpi_device *device) +{ + struct acpi_button *button = acpi_driver_data(device); + unsigned long long state; + acpi_status status; + int ret; + + status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); + if (ACPI_FAILURE(status)) + return -ENODEV; + + /* input layer checks if event is redundant */ + input_report_switch(button->input, SW_LID, !state); + input_sync(button->input); + + if (state) + pm_wakeup_event(&device->dev, 0); + + ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); + if (ret == NOTIFY_DONE) + ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, + device); + if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { + /* + * It is also regarded as success if the notifier_chain + * returns NOTIFY_OK or NOTIFY_DONE. + */ + ret = 0; + } + return ret; +} + +static void acpi_button_notify(struct acpi_device *device, u32 event) +{ + struct acpi_button *button = acpi_driver_data(device); + struct input_dev *input; + + switch (event) { + case ACPI_FIXED_HARDWARE_EVENT: + event = ACPI_BUTTON_NOTIFY_STATUS; + /* fall through */ + case ACPI_BUTTON_NOTIFY_STATUS: + input = button->input; + if (button->type == ACPI_BUTTON_TYPE_LID) { + acpi_lid_send_state(device); + } else { + int keycode; + + pm_wakeup_event(&device->dev, 0); + if (button->suspended) + break; + + keycode = test_bit(KEY_SLEEP, input->keybit) ? + KEY_SLEEP : KEY_POWER; + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + + acpi_bus_generate_netlink_event( + device->pnp.device_class, + dev_name(&device->dev), + event, ++button->pushed); + } + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } +} + +#ifdef CONFIG_PM_SLEEP +static int acpi_button_suspend(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct acpi_button *button = acpi_driver_data(device); + + button->suspended = true; + return 0; +} + +static int acpi_button_resume(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct acpi_button *button = acpi_driver_data(device); + + button->suspended = false; + if (button->type == ACPI_BUTTON_TYPE_LID) + return acpi_lid_send_state(device); + return 0; +} +#endif + +static int acpi_button_add(struct acpi_device *device) +{ + struct acpi_button *button; + struct input_dev *input; + const char *hid = acpi_device_hid(device); + char *name, *class; + int error; + + button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); + if (!button) + return -ENOMEM; + + device->driver_data = button; + + button->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_button; + } + + name = acpi_device_name(device); + class = acpi_device_class(device); + + if (!strcmp(hid, ACPI_BUTTON_HID_POWER) || + !strcmp(hid, ACPI_BUTTON_HID_POWERF)) { + button->type = ACPI_BUTTON_TYPE_POWER; + strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER); + sprintf(class, "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); + } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) || + !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) { + button->type = ACPI_BUTTON_TYPE_SLEEP; + strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP); + sprintf(class, "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); + } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) { + button->type = ACPI_BUTTON_TYPE_LID; + strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID); + sprintf(class, "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); + } else { + printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); + error = -ENODEV; + goto err_free_input; + } + + error = acpi_button_add_fs(device); + if (error) + goto err_free_input; + + snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); + + input->name = name; + input->phys = button->phys; + input->id.bustype = BUS_HOST; + input->id.product = button->type; + input->dev.parent = &device->dev; + + switch (button->type) { + case ACPI_BUTTON_TYPE_POWER: + input_set_capability(input, EV_KEY, KEY_POWER); + break; + + case ACPI_BUTTON_TYPE_SLEEP: + input_set_capability(input, EV_KEY, KEY_SLEEP); + break; + + case ACPI_BUTTON_TYPE_LID: + input_set_capability(input, EV_SW, SW_LID); + break; + } + + error = input_register_device(input); + if (error) + goto err_remove_fs; + if (button->type == ACPI_BUTTON_TYPE_LID) { + acpi_lid_send_state(device); + /* + * This assumes there's only one lid device, or if there are + * more we only care about the last one... + */ + lid_device = device; + } + + printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device)); + return 0; + + err_remove_fs: + acpi_button_remove_fs(device); + err_free_input: + input_free_device(input); + err_free_button: + kfree(button); + return error; +} + +static int acpi_button_remove(struct acpi_device *device) +{ + struct acpi_button *button = acpi_driver_data(device); + + acpi_button_remove_fs(device); + input_unregister_device(button->input); + kfree(button); + return 0; +} + +module_acpi_driver(acpi_button_driver); diff --git a/kernel/drivers/acpi/cm_sbs.c b/kernel/drivers/acpi/cm_sbs.c new file mode 100644 index 000000000..6c9ee68e4 --- /dev/null +++ b/kernel/drivers/acpi/cm_sbs.c @@ -0,0 +1,105 @@ +/* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +ACPI_MODULE_NAME("cm_sbs"); +#define ACPI_AC_CLASS "ac_adapter" +#define ACPI_BATTERY_CLASS "battery" +#define _COMPONENT ACPI_SBS_COMPONENT +static struct proc_dir_entry *acpi_ac_dir; +static struct proc_dir_entry *acpi_battery_dir; + +static DEFINE_MUTEX(cm_sbs_mutex); + +static int lock_ac_dir_cnt; +static int lock_battery_dir_cnt; + +struct proc_dir_entry *acpi_lock_ac_dir(void) +{ + mutex_lock(&cm_sbs_mutex); + if (!acpi_ac_dir) + acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir); + if (acpi_ac_dir) { + lock_ac_dir_cnt++; + } else { + printk(KERN_ERR PREFIX + "Cannot create %s\n", ACPI_AC_CLASS); + } + mutex_unlock(&cm_sbs_mutex); + return acpi_ac_dir; +} +EXPORT_SYMBOL(acpi_lock_ac_dir); + +void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir_param) +{ + mutex_lock(&cm_sbs_mutex); + if (acpi_ac_dir_param) + lock_ac_dir_cnt--; + if (lock_ac_dir_cnt == 0 && acpi_ac_dir_param && acpi_ac_dir) { + remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); + acpi_ac_dir = NULL; + } + mutex_unlock(&cm_sbs_mutex); +} +EXPORT_SYMBOL(acpi_unlock_ac_dir); + +struct proc_dir_entry *acpi_lock_battery_dir(void) +{ + mutex_lock(&cm_sbs_mutex); + if (!acpi_battery_dir) { + acpi_battery_dir = + proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); + } + if (acpi_battery_dir) { + lock_battery_dir_cnt++; + } else { + printk(KERN_ERR PREFIX + "Cannot create %s\n", ACPI_BATTERY_CLASS); + } + mutex_unlock(&cm_sbs_mutex); + return acpi_battery_dir; +} +EXPORT_SYMBOL(acpi_lock_battery_dir); + +void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param) +{ + mutex_lock(&cm_sbs_mutex); + if (acpi_battery_dir_param) + lock_battery_dir_cnt--; + if (lock_battery_dir_cnt == 0 && acpi_battery_dir_param + && acpi_battery_dir) { + remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); + acpi_battery_dir = NULL; + } + mutex_unlock(&cm_sbs_mutex); + return; +} +EXPORT_SYMBOL(acpi_unlock_battery_dir); diff --git a/kernel/drivers/acpi/container.c b/kernel/drivers/acpi/container.c new file mode 100644 index 000000000..c8ead9f97 --- /dev/null +++ b/kernel/drivers/acpi/container.c @@ -0,0 +1,136 @@ +/* + * container.c - ACPI Generic Container Driver + * + * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com) + * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com) + * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com) + * Copyright (C) 2004 FUJITSU LIMITED + * Copyright (C) 2004, 2013 Intel Corp. + * Author: Rafael J. Wysocki + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_CONTAINER_COMPONENT +ACPI_MODULE_NAME("container"); + +static const struct acpi_device_id container_device_ids[] = { + {"ACPI0004", 0}, + {"PNP0A05", 0}, + {"PNP0A06", 0}, + {"", 0}, +}; + +#ifdef CONFIG_ACPI_CONTAINER + +static int acpi_container_offline(struct container_dev *cdev) +{ + struct acpi_device *adev = ACPI_COMPANION(&cdev->dev); + struct acpi_device *child; + + /* Check all of the dependent devices' physical companions. */ + list_for_each_entry(child, &adev->children, node) + if (!acpi_scan_is_offline(child, false)) + return -EBUSY; + + return 0; +} + +static void acpi_container_release(struct device *dev) +{ + kfree(to_container_dev(dev)); +} + +static int container_device_attach(struct acpi_device *adev, + const struct acpi_device_id *not_used) +{ + struct container_dev *cdev; + struct device *dev; + int ret; + + if (adev->flags.is_dock_station) + return 0; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->offline = acpi_container_offline; + dev = &cdev->dev; + dev->bus = &container_subsys; + dev_set_name(dev, "%s", dev_name(&adev->dev)); + ACPI_COMPANION_SET(dev, adev); + dev->release = acpi_container_release; + ret = device_register(dev); + if (ret) { + put_device(dev); + return ret; + } + adev->driver_data = dev; + return 1; +} + +static void container_device_detach(struct acpi_device *adev) +{ + struct device *dev = acpi_driver_data(adev); + + adev->driver_data = NULL; + if (dev) + device_unregister(dev); +} + +static void container_device_online(struct acpi_device *adev) +{ + struct device *dev = acpi_driver_data(adev); + + kobject_uevent(&dev->kobj, KOBJ_ONLINE); +} + +static struct acpi_scan_handler container_handler = { + .ids = container_device_ids, + .attach = container_device_attach, + .detach = container_device_detach, + .hotplug = { + .enabled = true, + .demand_offline = true, + .notify_online = container_device_online, + }, +}; + +void __init acpi_container_init(void) +{ + acpi_scan_add_handler(&container_handler); +} + +#else + +static struct acpi_scan_handler container_handler = { + .ids = container_device_ids, +}; + +void __init acpi_container_init(void) +{ + acpi_scan_add_handler_with_hotplug(&container_handler, "container"); +} + +#endif /* CONFIG_ACPI_CONTAINER */ diff --git a/kernel/drivers/acpi/custom_method.c b/kernel/drivers/acpi/custom_method.c new file mode 100644 index 000000000..c68e72414 --- /dev/null +++ b/kernel/drivers/acpi/custom_method.c @@ -0,0 +1,100 @@ +/* + * custom_method.c - debugfs interface for customizing ACPI control method + */ + +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME("custom_method"); +MODULE_LICENSE("GPL"); + +static struct dentry *cm_dentry; + +/* /sys/kernel/debug/acpi/custom_method */ + +static ssize_t cm_write(struct file *file, const char __user * user_buf, + size_t count, loff_t *ppos) +{ + static char *buf; + static u32 max_size; + static u32 uncopied_bytes; + + struct acpi_table_header table; + acpi_status status; + + if (!(*ppos)) { + /* parse the table header to get the table length */ + if (count <= sizeof(struct acpi_table_header)) + return -EINVAL; + if (copy_from_user(&table, user_buf, + sizeof(struct acpi_table_header))) + return -EFAULT; + uncopied_bytes = max_size = table.length; + buf = kzalloc(max_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + if (buf == NULL) + return -EINVAL; + + if ((*ppos > max_size) || + (*ppos + count > max_size) || + (*ppos + count < count) || + (count > uncopied_bytes)) + return -EINVAL; + + if (copy_from_user(buf + (*ppos), user_buf, count)) { + kfree(buf); + buf = NULL; + return -EFAULT; + } + + uncopied_bytes -= count; + *ppos += count; + + if (!uncopied_bytes) { + status = acpi_install_method(buf); + kfree(buf); + buf = NULL; + if (ACPI_FAILURE(status)) + return -EINVAL; + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); + } + + return count; +} + +static const struct file_operations cm_fops = { + .write = cm_write, + .llseek = default_llseek, +}; + +static int __init acpi_custom_method_init(void) +{ + if (acpi_debugfs_dir == NULL) + return -ENOENT; + + cm_dentry = debugfs_create_file("custom_method", S_IWUSR, + acpi_debugfs_dir, NULL, &cm_fops); + if (cm_dentry == NULL) + return -ENODEV; + + return 0; +} + +static void __exit acpi_custom_method_exit(void) +{ + if (cm_dentry) + debugfs_remove(cm_dentry); + } + +module_init(acpi_custom_method_init); +module_exit(acpi_custom_method_exit); diff --git a/kernel/drivers/acpi/debugfs.c b/kernel/drivers/acpi/debugfs.c new file mode 100644 index 000000000..6b1919f6b --- /dev/null +++ b/kernel/drivers/acpi/debugfs.c @@ -0,0 +1,19 @@ +/* + * debugfs.c - ACPI debugfs interface to userspace. + */ + +#include +#include +#include +#include + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME("debugfs"); + +struct dentry *acpi_debugfs_dir; +EXPORT_SYMBOL_GPL(acpi_debugfs_dir); + +void __init acpi_debugfs_init(void) +{ + acpi_debugfs_dir = debugfs_create_dir("acpi", NULL); +} diff --git a/kernel/drivers/acpi/device_pm.c b/kernel/drivers/acpi/device_pm.c new file mode 100644 index 000000000..8217e0bda --- /dev/null +++ b/kernel/drivers/acpi/device_pm.c @@ -0,0 +1,1118 @@ +/* + * drivers/acpi/device_pm.c - ACPI device power management routines. + * + * Copyright (C) 2012, Intel Corp. + * Author: Rafael J. Wysocki + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_POWER_COMPONENT +ACPI_MODULE_NAME("device_pm"); + +/** + * acpi_power_state_string - String representation of ACPI device power state. + * @state: ACPI device power state to return the string representation of. + */ +const char *acpi_power_state_string(int state) +{ + switch (state) { + case ACPI_STATE_D0: + return "D0"; + case ACPI_STATE_D1: + return "D1"; + case ACPI_STATE_D2: + return "D2"; + case ACPI_STATE_D3_HOT: + return "D3hot"; + case ACPI_STATE_D3_COLD: + return "D3cold"; + default: + return "(unknown)"; + } +} + +/** + * acpi_device_get_power - Get power state of an ACPI device. + * @device: Device to get the power state of. + * @state: Place to store the power state of the device. + * + * This function does not update the device's power.state field, but it may + * update its parent's power.state field (when the parent's power state is + * unknown and the device's power state turns out to be D0). + */ +int acpi_device_get_power(struct acpi_device *device, int *state) +{ + int result = ACPI_STATE_UNKNOWN; + + if (!device || !state) + return -EINVAL; + + if (!device->flags.power_manageable) { + /* TBD: Non-recursive algorithm for walking up hierarchy. */ + *state = device->parent ? + device->parent->power.state : ACPI_STATE_D0; + goto out; + } + + /* + * Get the device's power state from power resources settings and _PSC, + * if available. + */ + if (device->power.flags.power_resources) { + int error = acpi_power_get_inferred_state(device, &result); + if (error) + return error; + } + if (device->power.flags.explicit_get) { + acpi_handle handle = device->handle; + unsigned long long psc; + acpi_status status; + + status = acpi_evaluate_integer(handle, "_PSC", NULL, &psc); + if (ACPI_FAILURE(status)) + return -ENODEV; + + /* + * The power resources settings may indicate a power state + * shallower than the actual power state of the device. + * + * Moreover, on systems predating ACPI 4.0, if the device + * doesn't depend on any power resources and _PSC returns 3, + * that means "power off". We need to maintain compatibility + * with those systems. + */ + if (psc > result && psc < ACPI_STATE_D3_COLD) + result = psc; + else if (result == ACPI_STATE_UNKNOWN) + result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc; + } + + /* + * If we were unsure about the device parent's power state up to this + * point, the fact that the device is in D0 implies that the parent has + * to be in D0 too, except if ignore_parent is set. + */ + if (!device->power.flags.ignore_parent && device->parent + && device->parent->power.state == ACPI_STATE_UNKNOWN + && result == ACPI_STATE_D0) + device->parent->power.state = ACPI_STATE_D0; + + *state = result; + + out: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", + device->pnp.bus_id, acpi_power_state_string(*state))); + + return 0; +} + +static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state) +{ + if (adev->power.states[state].flags.explicit_set) { + char method[5] = { '_', 'P', 'S', '0' + state, '\0' }; + acpi_status status; + + status = acpi_evaluate_object(adev->handle, method, NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + } + return 0; +} + +/** + * acpi_device_set_power - Set power state of an ACPI device. + * @device: Device to set the power state of. + * @state: New power state to set. + * + * Callers must ensure that the device is power manageable before using this + * function. + */ +int acpi_device_set_power(struct acpi_device *device, int state) +{ + int result = 0; + bool cut_power = false; + + if (!device || !device->flags.power_manageable + || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) + return -EINVAL; + + /* Make sure this is a valid target state */ + + if (state == device->power.state) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already in %s\n", + device->pnp.bus_id, + acpi_power_state_string(state))); + return 0; + } + + if (!device->power.states[state].flags.valid) { + dev_warn(&device->dev, "Power state %s not supported\n", + acpi_power_state_string(state)); + return -ENODEV; + } + if (!device->power.flags.ignore_parent && + device->parent && (state < device->parent->power.state)) { + dev_warn(&device->dev, + "Cannot transition to power state %s for parent in %s\n", + acpi_power_state_string(state), + acpi_power_state_string(device->parent->power.state)); + return -ENODEV; + } + + /* For D3cold we should first transition into D3hot. */ + if (state == ACPI_STATE_D3_COLD + && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { + state = ACPI_STATE_D3_HOT; + cut_power = true; + } + + if (state < device->power.state && state != ACPI_STATE_D0 + && device->power.state >= ACPI_STATE_D3_HOT) { + dev_warn(&device->dev, + "Cannot transition to non-D0 state from D3\n"); + return -ENODEV; + } + + /* + * Transition Power + * ---------------- + * In accordance with the ACPI specification first apply power (via + * power resources) and then evaluate _PSx. + */ + if (device->power.flags.power_resources) { + result = acpi_power_transition(device, state); + if (result) + goto end; + } + result = acpi_dev_pm_explicit_set(device, state); + if (result) + goto end; + + if (cut_power) { + device->power.state = state; + state = ACPI_STATE_D3_COLD; + result = acpi_power_transition(device, state); + } + + end: + if (result) { + dev_warn(&device->dev, "Failed to change power state to %s\n", + acpi_power_state_string(state)); + } else { + device->power.state = state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Device [%s] transitioned to %s\n", + device->pnp.bus_id, + acpi_power_state_string(state))); + } + + return result; +} +EXPORT_SYMBOL(acpi_device_set_power); + +int acpi_bus_set_power(acpi_handle handle, int state) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + if (result) + return result; + + return acpi_device_set_power(device, state); +} +EXPORT_SYMBOL(acpi_bus_set_power); + +int acpi_bus_init_power(struct acpi_device *device) +{ + int state; + int result; + + if (!device) + return -EINVAL; + + device->power.state = ACPI_STATE_UNKNOWN; + if (!acpi_device_is_present(device)) + return -ENXIO; + + result = acpi_device_get_power(device, &state); + if (result) + return result; + + if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { + result = acpi_power_on_resources(device, state); + if (result) + return result; + + result = acpi_dev_pm_explicit_set(device, state); + if (result) + return result; + } else if (state == ACPI_STATE_UNKNOWN) { + /* + * No power resources and missing _PSC? Cross fingers and make + * it D0 in hope that this is what the BIOS put the device into. + * [We tried to force D0 here by executing _PS0, but that broke + * Toshiba P870-303 in a nasty way.] + */ + state = ACPI_STATE_D0; + } + device->power.state = state; + return 0; +} + +/** + * acpi_device_fix_up_power - Force device with missing _PSC into D0. + * @device: Device object whose power state is to be fixed up. + * + * Devices without power resources and _PSC, but having _PS0 and _PS3 defined, + * are assumed to be put into D0 by the BIOS. However, in some cases that may + * not be the case and this function should be used then. + */ +int acpi_device_fix_up_power(struct acpi_device *device) +{ + int ret = 0; + + if (!device->power.flags.power_resources + && !device->power.flags.explicit_get + && device->power.state == ACPI_STATE_D0) + ret = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0); + + return ret; +} + +int acpi_device_update_power(struct acpi_device *device, int *state_p) +{ + int state; + int result; + + if (device->power.state == ACPI_STATE_UNKNOWN) { + result = acpi_bus_init_power(device); + if (!result && state_p) + *state_p = device->power.state; + + return result; + } + + result = acpi_device_get_power(device, &state); + if (result) + return result; + + if (state == ACPI_STATE_UNKNOWN) { + state = ACPI_STATE_D0; + result = acpi_device_set_power(device, state); + if (result) + return result; + } else { + if (device->power.flags.power_resources) { + /* + * We don't need to really switch the state, bu we need + * to update the power resources' reference counters. + */ + result = acpi_power_transition(device, state); + if (result) + return result; + } + device->power.state = state; + } + if (state_p) + *state_p = state; + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_device_update_power); + +int acpi_bus_update_power(acpi_handle handle, int *state_p) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + return result ? result : acpi_device_update_power(device, state_p); +} +EXPORT_SYMBOL_GPL(acpi_bus_update_power); + +bool acpi_bus_power_manageable(acpi_handle handle) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + return result ? false : device->flags.power_manageable; +} +EXPORT_SYMBOL(acpi_bus_power_manageable); + +#ifdef CONFIG_PM +static DEFINE_MUTEX(acpi_pm_notifier_lock); + +static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) +{ + struct acpi_device *adev; + + if (val != ACPI_NOTIFY_DEVICE_WAKE) + return; + + adev = acpi_bus_get_acpi_device(handle); + if (!adev) + return; + + mutex_lock(&acpi_pm_notifier_lock); + + if (adev->wakeup.flags.notifier_present) { + __pm_wakeup_event(adev->wakeup.ws, 0); + if (adev->wakeup.context.work.func) + queue_pm_work(&adev->wakeup.context.work); + } + + mutex_unlock(&acpi_pm_notifier_lock); + + acpi_bus_put_acpi_device(adev); +} + +/** + * acpi_add_pm_notifier - Register PM notify handler for given ACPI device. + * @adev: ACPI device to add the notify handler for. + * @dev: Device to generate a wakeup event for while handling the notification. + * @work_func: Work function to execute when handling the notification. + * + * NOTE: @adev need not be a run-wake or wakeup device to be a valid source of + * PM wakeup events. For example, wakeup events may be generated for bridges + * if one of the devices below the bridge is signaling wakeup, even if the + * bridge itself doesn't have a wakeup GPE associated with it. + */ +acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, + void (*work_func)(struct work_struct *work)) +{ + acpi_status status = AE_ALREADY_EXISTS; + + if (!dev && !work_func) + return AE_BAD_PARAMETER; + + mutex_lock(&acpi_pm_notifier_lock); + + if (adev->wakeup.flags.notifier_present) + goto out; + + adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev)); + adev->wakeup.context.dev = dev; + if (work_func) + INIT_WORK(&adev->wakeup.context.work, work_func); + + status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY, + acpi_pm_notify_handler, NULL); + if (ACPI_FAILURE(status)) + goto out; + + adev->wakeup.flags.notifier_present = true; + + out: + mutex_unlock(&acpi_pm_notifier_lock); + return status; +} + +/** + * acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device. + * @adev: ACPI device to remove the notifier from. + */ +acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) +{ + acpi_status status = AE_BAD_PARAMETER; + + mutex_lock(&acpi_pm_notifier_lock); + + if (!adev->wakeup.flags.notifier_present) + goto out; + + status = acpi_remove_notify_handler(adev->handle, + ACPI_SYSTEM_NOTIFY, + acpi_pm_notify_handler); + if (ACPI_FAILURE(status)) + goto out; + + if (adev->wakeup.context.work.func) { + cancel_work_sync(&adev->wakeup.context.work); + adev->wakeup.context.work.func = NULL; + } + adev->wakeup.context.dev = NULL; + wakeup_source_unregister(adev->wakeup.ws); + + adev->wakeup.flags.notifier_present = false; + + out: + mutex_unlock(&acpi_pm_notifier_lock); + return status; +} + +bool acpi_bus_can_wakeup(acpi_handle handle) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + return result ? false : device->wakeup.flags.valid; +} +EXPORT_SYMBOL(acpi_bus_can_wakeup); + +/** + * acpi_dev_pm_get_state - Get preferred power state of ACPI device. + * @dev: Device whose preferred target power state to return. + * @adev: ACPI device node corresponding to @dev. + * @target_state: System state to match the resultant device state. + * @d_min_p: Location to store the highest power state available to the device. + * @d_max_p: Location to store the lowest power state available to the device. + * + * Find the lowest power (highest number) and highest power (lowest number) ACPI + * device power states that the device can be in while the system is in the + * state represented by @target_state. Store the integer numbers representing + * those stats in the memory locations pointed to by @d_max_p and @d_min_p, + * respectively. + * + * Callers must ensure that @dev and @adev are valid pointers and that @adev + * actually corresponds to @dev before using this function. + * + * Returns 0 on success or -ENODATA when one of the ACPI methods fails or + * returns a value that doesn't make sense. The memory locations pointed to by + * @d_max_p and @d_min_p are only modified on success. + */ +static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, + u32 target_state, int *d_min_p, int *d_max_p) +{ + char method[] = { '_', 'S', '0' + target_state, 'D', '\0' }; + acpi_handle handle = adev->handle; + unsigned long long ret; + int d_min, d_max; + bool wakeup = false; + acpi_status status; + + /* + * If the system state is S0, the lowest power state the device can be + * in is D3cold, unless the device has _S0W and is supposed to signal + * wakeup, in which case the return value of _S0W has to be used as the + * lowest power state available to the device. + */ + d_min = ACPI_STATE_D0; + d_max = ACPI_STATE_D3_COLD; + + /* + * If present, _SxD methods return the minimum D-state (highest power + * state) we can use for the corresponding S-states. Otherwise, the + * minimum D-state is D0 (ACPI 3.x). + */ + if (target_state > ACPI_STATE_S0) { + /* + * We rely on acpi_evaluate_integer() not clobbering the integer + * provided if AE_NOT_FOUND is returned. + */ + ret = d_min; + status = acpi_evaluate_integer(handle, method, NULL, &ret); + if ((ACPI_FAILURE(status) && status != AE_NOT_FOUND) + || ret > ACPI_STATE_D3_COLD) + return -ENODATA; + + /* + * We need to handle legacy systems where D3hot and D3cold are + * the same and 3 is returned in both cases, so fall back to + * D3cold if D3hot is not a valid state. + */ + if (!adev->power.states[ret].flags.valid) { + if (ret == ACPI_STATE_D3_HOT) + ret = ACPI_STATE_D3_COLD; + else + return -ENODATA; + } + d_min = ret; + wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid + && adev->wakeup.sleep_state >= target_state; + } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != + PM_QOS_FLAGS_NONE) { + wakeup = adev->wakeup.flags.valid; + } + + /* + * If _PRW says we can wake up the system from the target sleep state, + * the D-state returned by _SxD is sufficient for that (we assume a + * wakeup-aware driver if wake is set). Still, if _SxW exists + * (ACPI 3.x), it should return the maximum (lowest power) D-state that + * can wake the system. _S0W may be valid, too. + */ + if (wakeup) { + method[3] = 'W'; + status = acpi_evaluate_integer(handle, method, NULL, &ret); + if (status == AE_NOT_FOUND) { + if (target_state > ACPI_STATE_S0) + d_max = d_min; + } else if (ACPI_SUCCESS(status) && ret <= ACPI_STATE_D3_COLD) { + /* Fall back to D3cold if ret is not a valid state. */ + if (!adev->power.states[ret].flags.valid) + ret = ACPI_STATE_D3_COLD; + + d_max = ret > d_min ? ret : d_min; + } else { + return -ENODATA; + } + } + + if (d_min_p) + *d_min_p = d_min; + + if (d_max_p) + *d_max_p = d_max; + + return 0; +} + +/** + * acpi_pm_device_sleep_state - Get preferred power state of ACPI device. + * @dev: Device whose preferred target power state to return. + * @d_min_p: Location to store the upper limit of the allowed states range. + * @d_max_in: Deepest low-power state to take into consideration. + * Return value: Preferred power state of the device on success, -ENODEV + * if there's no 'struct acpi_device' for @dev, -EINVAL if @d_max_in is + * incorrect, or -ENODATA on ACPI method failure. + * + * The caller must ensure that @dev is valid before using this function. + */ +int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) +{ + struct acpi_device *adev; + int ret, d_min, d_max; + + if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) + return -EINVAL; + + if (d_max_in > ACPI_STATE_D3_HOT) { + enum pm_qos_flags_status stat; + + stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); + if (stat == PM_QOS_FLAGS_ALL) + d_max_in = ACPI_STATE_D3_HOT; + } + + adev = ACPI_COMPANION(dev); + if (!adev) { + dev_dbg(dev, "ACPI companion missing in %s!\n", __func__); + return -ENODEV; + } + + ret = acpi_dev_pm_get_state(dev, adev, acpi_target_system_state(), + &d_min, &d_max); + if (ret) + return ret; + + if (d_max_in < d_min) + return -EINVAL; + + if (d_max > d_max_in) { + for (d_max = d_max_in; d_max > d_min; d_max--) { + if (adev->power.states[d_max].flags.valid) + break; + } + } + + if (d_min_p) + *d_min_p = d_min; + + return d_max; +} +EXPORT_SYMBOL(acpi_pm_device_sleep_state); + +/** + * acpi_pm_notify_work_func - ACPI devices wakeup notification work function. + * @work: Work item to handle. + */ +static void acpi_pm_notify_work_func(struct work_struct *work) +{ + struct device *dev; + + dev = container_of(work, struct acpi_device_wakeup_context, work)->dev; + if (dev) { + pm_wakeup_event(dev, 0); + pm_runtime_resume(dev); + } +} + +/** + * acpi_device_wakeup - Enable/disable wakeup functionality for device. + * @adev: ACPI device to enable/disable wakeup functionality for. + * @target_state: State the system is transitioning into. + * @enable: Whether to enable or disable the wakeup functionality. + * + * Enable/disable the GPE associated with @adev so that it can generate + * wakeup signals for the device in response to external (remote) events and + * enable/disable device wakeup power. + * + * Callers must ensure that @adev is a valid ACPI device node before executing + * this function. + */ +static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state, + bool enable) +{ + struct acpi_device_wakeup *wakeup = &adev->wakeup; + + if (enable) { + acpi_status res; + int error; + + error = acpi_enable_wakeup_device_power(adev, target_state); + if (error) + return error; + + if (adev->wakeup.flags.enabled) + return 0; + + res = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number); + if (ACPI_SUCCESS(res)) { + adev->wakeup.flags.enabled = 1; + } else { + acpi_disable_wakeup_device_power(adev); + return -EIO; + } + } else { + if (adev->wakeup.flags.enabled) { + acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number); + adev->wakeup.flags.enabled = 0; + } + acpi_disable_wakeup_device_power(adev); + } + return 0; +} + +/** + * acpi_pm_device_run_wake - Enable/disable remote wakeup for given device. + * @dev: Device to enable/disable the platform to wake up. + * @enable: Whether to enable or disable the wakeup functionality. + */ +int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) +{ + struct acpi_device *adev; + + if (!device_run_wake(phys_dev)) + return -EINVAL; + + adev = ACPI_COMPANION(phys_dev); + if (!adev) { + dev_dbg(phys_dev, "ACPI companion missing in %s!\n", __func__); + return -ENODEV; + } + + return acpi_device_wakeup(adev, ACPI_STATE_S0, enable); +} +EXPORT_SYMBOL(acpi_pm_device_run_wake); + +#ifdef CONFIG_PM_SLEEP +/** + * acpi_pm_device_sleep_wake - Enable or disable device to wake up the system. + * @dev: Device to enable/desible to wake up the system from sleep states. + * @enable: Whether to enable or disable @dev to wake up the system. + */ +int acpi_pm_device_sleep_wake(struct device *dev, bool enable) +{ + struct acpi_device *adev; + int error; + + if (!device_can_wakeup(dev)) + return -EINVAL; + + adev = ACPI_COMPANION(dev); + if (!adev) { + dev_dbg(dev, "ACPI companion missing in %s!\n", __func__); + return -ENODEV; + } + + error = acpi_device_wakeup(adev, acpi_target_system_state(), enable); + if (!error) + dev_info(dev, "System wakeup %s by ACPI\n", + enable ? "enabled" : "disabled"); + + return error; +} +#endif /* CONFIG_PM_SLEEP */ + +/** + * acpi_dev_pm_low_power - Put ACPI device into a low-power state. + * @dev: Device to put into a low-power state. + * @adev: ACPI device node corresponding to @dev. + * @system_state: System state to choose the device state for. + */ +static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev, + u32 system_state) +{ + int ret, state; + + if (!acpi_device_power_manageable(adev)) + return 0; + + ret = acpi_dev_pm_get_state(dev, adev, system_state, NULL, &state); + return ret ? ret : acpi_device_set_power(adev, state); +} + +/** + * acpi_dev_pm_full_power - Put ACPI device into the full-power state. + * @adev: ACPI device node to put into the full-power state. + */ +static int acpi_dev_pm_full_power(struct acpi_device *adev) +{ + return acpi_device_power_manageable(adev) ? + acpi_device_set_power(adev, ACPI_STATE_D0) : 0; +} + +/** + * acpi_dev_runtime_suspend - Put device into a low-power state using ACPI. + * @dev: Device to put into a low-power state. + * + * Put the given device into a runtime low-power state using the standard ACPI + * mechanism. Set up remote wakeup if desired, choose the state to put the + * device into (this checks if remote wakeup is expected to work too), and set + * the power state of the device. + */ +int acpi_dev_runtime_suspend(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + bool remote_wakeup; + int error; + + if (!adev) + return 0; + + remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > + PM_QOS_FLAGS_NONE; + error = acpi_device_wakeup(adev, ACPI_STATE_S0, remote_wakeup); + if (remote_wakeup && error) + return -EAGAIN; + + error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); + if (error) + acpi_device_wakeup(adev, ACPI_STATE_S0, false); + + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend); + +/** + * acpi_dev_runtime_resume - Put device into the full-power state using ACPI. + * @dev: Device to put into the full-power state. + * + * Put the given device into the full-power state using the standard ACPI + * mechanism at run time. Set the power state of the device to ACPI D0 and + * disable remote wakeup. + */ +int acpi_dev_runtime_resume(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + int error; + + if (!adev) + return 0; + + error = acpi_dev_pm_full_power(adev); + acpi_device_wakeup(adev, ACPI_STATE_S0, false); + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume); + +/** + * acpi_subsys_runtime_suspend - Suspend device using ACPI. + * @dev: Device to suspend. + * + * Carry out the generic runtime suspend procedure for @dev and use ACPI to put + * it into a runtime low-power state. + */ +int acpi_subsys_runtime_suspend(struct device *dev) +{ + int ret = pm_generic_runtime_suspend(dev); + return ret ? ret : acpi_dev_runtime_suspend(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend); + +/** + * acpi_subsys_runtime_resume - Resume device using ACPI. + * @dev: Device to Resume. + * + * Use ACPI to put the given device into the full-power state and carry out the + * generic runtime resume procedure for it. + */ +int acpi_subsys_runtime_resume(struct device *dev) +{ + int ret = acpi_dev_runtime_resume(dev); + return ret ? ret : pm_generic_runtime_resume(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume); + +#ifdef CONFIG_PM_SLEEP +/** + * acpi_dev_suspend_late - Put device into a low-power state using ACPI. + * @dev: Device to put into a low-power state. + * + * Put the given device into a low-power state during system transition to a + * sleep state using the standard ACPI mechanism. Set up system wakeup if + * desired, choose the state to put the device into (this checks if system + * wakeup is expected to work too), and set the power state of the device. + */ +int acpi_dev_suspend_late(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + u32 target_state; + bool wakeup; + int error; + + if (!adev) + return 0; + + target_state = acpi_target_system_state(); + wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev); + error = acpi_device_wakeup(adev, target_state, wakeup); + if (wakeup && error) + return error; + + error = acpi_dev_pm_low_power(dev, adev, target_state); + if (error) + acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false); + + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_suspend_late); + +/** + * acpi_dev_resume_early - Put device into the full-power state using ACPI. + * @dev: Device to put into the full-power state. + * + * Put the given device into the full-power state using the standard ACPI + * mechanism during system transition to the working state. Set the power + * state of the device to ACPI D0 and disable remote wakeup. + */ +int acpi_dev_resume_early(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + int error; + + if (!adev) + return 0; + + error = acpi_dev_pm_full_power(adev); + acpi_device_wakeup(adev, ACPI_STATE_UNKNOWN, false); + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_resume_early); + +/** + * acpi_subsys_prepare - Prepare device for system transition to a sleep state. + * @dev: Device to prepare. + */ +int acpi_subsys_prepare(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + u32 sys_target; + int ret, state; + + ret = pm_generic_prepare(dev); + if (ret < 0) + return ret; + + if (!adev || !pm_runtime_suspended(dev) + || device_may_wakeup(dev) != !!adev->wakeup.prepare_count) + return 0; + + sys_target = acpi_target_system_state(); + if (sys_target == ACPI_STATE_S0) + return 1; + + if (adev->power.flags.dsw_present) + return 0; + + ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state); + return !ret && state == adev->power.state; +} +EXPORT_SYMBOL_GPL(acpi_subsys_prepare); + +/** + * acpi_subsys_complete - Finalize device's resume during system resume. + * @dev: Device to handle. + */ +void acpi_subsys_complete(struct device *dev) +{ + pm_generic_complete(dev); + /* + * If the device had been runtime-suspended before the system went into + * the sleep state it is going out of and it has never been resumed till + * now, resume it in case the firmware powered it up. + */ + if (dev->power.direct_complete) + pm_request_resume(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_complete); + +/** + * acpi_subsys_suspend - Run the device driver's suspend callback. + * @dev: Device to handle. + * + * Follow PCI and resume devices suspended at run time before running their + * system suspend callbacks. + */ +int acpi_subsys_suspend(struct device *dev) +{ + pm_runtime_resume(dev); + return pm_generic_suspend(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_suspend); + +/** + * acpi_subsys_suspend_late - Suspend device using ACPI. + * @dev: Device to suspend. + * + * Carry out the generic late suspend procedure for @dev and use ACPI to put + * it into a low-power state during system transition into a sleep state. + */ +int acpi_subsys_suspend_late(struct device *dev) +{ + int ret = pm_generic_suspend_late(dev); + return ret ? ret : acpi_dev_suspend_late(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); + +/** + * acpi_subsys_resume_early - Resume device using ACPI. + * @dev: Device to Resume. + * + * Use ACPI to put the given device into the full-power state and carry out the + * generic early resume procedure for it during system transition into the + * working state. + */ +int acpi_subsys_resume_early(struct device *dev) +{ + int ret = acpi_dev_resume_early(dev); + return ret ? ret : pm_generic_resume_early(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); + +/** + * acpi_subsys_freeze - Run the device driver's freeze callback. + * @dev: Device to handle. + */ +int acpi_subsys_freeze(struct device *dev) +{ + /* + * This used to be done in acpi_subsys_prepare() for all devices and + * some drivers may depend on it, so do it here. Ideally, however, + * runtime-suspended devices should not be touched during freeze/thaw + * transitions. + */ + pm_runtime_resume(dev); + return pm_generic_freeze(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_freeze); + +#endif /* CONFIG_PM_SLEEP */ + +static struct dev_pm_domain acpi_general_pm_domain = { + .ops = { + .runtime_suspend = acpi_subsys_runtime_suspend, + .runtime_resume = acpi_subsys_runtime_resume, +#ifdef CONFIG_PM_SLEEP + .prepare = acpi_subsys_prepare, + .complete = acpi_subsys_complete, + .suspend = acpi_subsys_suspend, + .suspend_late = acpi_subsys_suspend_late, + .resume_early = acpi_subsys_resume_early, + .freeze = acpi_subsys_freeze, + .poweroff = acpi_subsys_suspend, + .poweroff_late = acpi_subsys_suspend_late, + .restore_early = acpi_subsys_resume_early, +#endif + }, +}; + +/** + * acpi_dev_pm_detach - Remove ACPI power management from the device. + * @dev: Device to take care of. + * @power_off: Whether or not to try to remove power from the device. + * + * Remove the device from the general ACPI PM domain and remove its wakeup + * notifier. If @power_off is set, additionally remove power from the device if + * possible. + * + * Callers must ensure proper synchronization of this function with power + * management callbacks. + */ +static void acpi_dev_pm_detach(struct device *dev, bool power_off) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + + if (adev && dev->pm_domain == &acpi_general_pm_domain) { + dev->pm_domain = NULL; + acpi_remove_pm_notifier(adev); + if (power_off) { + /* + * If the device's PM QoS resume latency limit or flags + * have been exposed to user space, they have to be + * hidden at this point, so that they don't affect the + * choice of the low-power state to put the device into. + */ + dev_pm_qos_hide_latency_limit(dev); + dev_pm_qos_hide_flags(dev); + acpi_device_wakeup(adev, ACPI_STATE_S0, false); + acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); + } + } +} + +/** + * acpi_dev_pm_attach - Prepare device for ACPI power management. + * @dev: Device to prepare. + * @power_on: Whether or not to power on the device. + * + * If @dev has a valid ACPI handle that has a valid struct acpi_device object + * attached to it, install a wakeup notification handler for the device and + * add it to the general ACPI PM domain. If @power_on is set, the device will + * be put into the ACPI D0 state before the function returns. + * + * This assumes that the @dev's bus type uses generic power management callbacks + * (or doesn't use any power management callbacks at all). + * + * Callers must ensure proper synchronization of this function with power + * management callbacks. + */ +int acpi_dev_pm_attach(struct device *dev, bool power_on) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + + if (!adev) + return -ENODEV; + + if (dev->pm_domain) + return -EEXIST; + + acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func); + dev->pm_domain = &acpi_general_pm_domain; + if (power_on) { + acpi_dev_pm_full_power(adev); + acpi_device_wakeup(adev, ACPI_STATE_S0, false); + } + + dev->pm_domain->detach = acpi_dev_pm_detach; + return 0; +} +EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); +#endif /* CONFIG_PM */ diff --git a/kernel/drivers/acpi/dock.c b/kernel/drivers/acpi/dock.c new file mode 100644 index 000000000..a688aa243 --- /dev/null +++ b/kernel/drivers/acpi/dock.c @@ -0,0 +1,666 @@ +/* + * dock.c - ACPI dock station driver + * + * Copyright (C) 2006, 2014, Intel Corp. + * Author: Kristen Carlson Accardi + * Rafael J. Wysocki + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver" + +ACPI_MODULE_NAME("dock"); +MODULE_AUTHOR("Kristen Carlson Accardi"); +MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION); +MODULE_LICENSE("GPL"); + +static bool immediate_undock = 1; +module_param(immediate_undock, bool, 0644); +MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to " + "undock immediately when the undock button is pressed, 0 will cause" + " the driver to wait for userspace to write the undock sysfs file " + " before undocking"); + +struct dock_station { + acpi_handle handle; + unsigned long last_dock_time; + u32 flags; + struct list_head dependent_devices; + + struct list_head sibling; + struct platform_device *dock_device; +}; +static LIST_HEAD(dock_stations); +static int dock_station_count; + +struct dock_dependent_device { + struct list_head list; + struct acpi_device *adev; +}; + +#define DOCK_DOCKING 0x00000001 +#define DOCK_UNDOCKING 0x00000002 +#define DOCK_IS_DOCK 0x00000010 +#define DOCK_IS_ATA 0x00000020 +#define DOCK_IS_BAT 0x00000040 +#define DOCK_EVENT 3 +#define UNDOCK_EVENT 2 + +enum dock_callback_type { + DOCK_CALL_HANDLER, + DOCK_CALL_FIXUP, + DOCK_CALL_UEVENT, +}; + +/***************************************************************************** + * Dock Dependent device functions * + *****************************************************************************/ +/** + * add_dock_dependent_device - associate a device with the dock station + * @ds: Dock station. + * @adev: Dependent ACPI device object. + * + * Add the dependent device to the dock's dependent device list. + */ +static int add_dock_dependent_device(struct dock_station *ds, + struct acpi_device *adev) +{ + struct dock_dependent_device *dd; + + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + dd->adev = adev; + INIT_LIST_HEAD(&dd->list); + list_add_tail(&dd->list, &ds->dependent_devices); + + return 0; +} + +static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, + enum dock_callback_type cb_type) +{ + struct acpi_device *adev = dd->adev; + + acpi_lock_hp_context(); + + if (!adev->hp) + goto out; + + if (cb_type == DOCK_CALL_FIXUP) { + void (*fixup)(struct acpi_device *); + + fixup = adev->hp->fixup; + if (fixup) { + acpi_unlock_hp_context(); + fixup(adev); + return; + } + } else if (cb_type == DOCK_CALL_UEVENT) { + void (*uevent)(struct acpi_device *, u32); + + uevent = adev->hp->uevent; + if (uevent) { + acpi_unlock_hp_context(); + uevent(adev, event); + return; + } + } else { + int (*notify)(struct acpi_device *, u32); + + notify = adev->hp->notify; + if (notify) { + acpi_unlock_hp_context(); + notify(adev, event); + return; + } + } + + out: + acpi_unlock_hp_context(); +} + +static struct dock_station *find_dock_station(acpi_handle handle) +{ + struct dock_station *ds; + + list_for_each_entry(ds, &dock_stations, sibling) + if (ds->handle == handle) + return ds; + + return NULL; +} + +/** + * find_dock_dependent_device - get a device dependent on this dock + * @ds: the dock station + * @adev: ACPI device object to find. + * + * iterate over the dependent device list for this dock. If the + * dependent device matches the handle, return. + */ +static struct dock_dependent_device * +find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev) +{ + struct dock_dependent_device *dd; + + list_for_each_entry(dd, &ds->dependent_devices, list) + if (adev == dd->adev) + return dd; + + return NULL; +} + +void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle) +{ + struct dock_station *ds = find_dock_station(dshandle); + + if (ds && !find_dock_dependent_device(ds, adev)) + add_dock_dependent_device(ds, adev); +} + +/***************************************************************************** + * Dock functions * + *****************************************************************************/ + +/** + * is_dock_device - see if a device is on a dock station + * @adev: ACPI device object to check. + * + * If this device is either the dock station itself, + * or is a device dependent on the dock station, then it + * is a dock device + */ +int is_dock_device(struct acpi_device *adev) +{ + struct dock_station *dock_station; + + if (!dock_station_count) + return 0; + + if (acpi_dock_match(adev->handle)) + return 1; + + list_for_each_entry(dock_station, &dock_stations, sibling) + if (find_dock_dependent_device(dock_station, adev)) + return 1; + + return 0; +} +EXPORT_SYMBOL_GPL(is_dock_device); + +/** + * dock_present - see if the dock station is present. + * @ds: the dock station + * + * execute the _STA method. note that present does not + * imply that we are docked. + */ +static int dock_present(struct dock_station *ds) +{ + unsigned long long sta; + acpi_status status; + + if (ds) { + status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta); + if (ACPI_SUCCESS(status) && sta) + return 1; + } + return 0; +} + +/** + * hot_remove_dock_devices - Remove dock station devices. + * @ds: Dock station. + */ +static void hot_remove_dock_devices(struct dock_station *ds) +{ + struct dock_dependent_device *dd; + + /* + * Walk the list in reverse order so that devices that have been added + * last are removed first (in case there are some indirect dependencies + * between them). + */ + list_for_each_entry_reverse(dd, &ds->dependent_devices, list) + dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); + + list_for_each_entry_reverse(dd, &ds->dependent_devices, list) + acpi_bus_trim(dd->adev); +} + +/** + * hotplug_dock_devices - Insert devices on a dock station. + * @ds: the dock station + * @event: either bus check or device check request + * + * Some devices on the dock station need to have drivers called + * to perform hotplug operations after a dock event has occurred. + * Traverse the list of dock devices that have registered a + * hotplug handler, and call the handler. + */ +static void hotplug_dock_devices(struct dock_station *ds, u32 event) +{ + struct dock_dependent_device *dd; + + /* Call driver specific post-dock fixups. */ + list_for_each_entry(dd, &ds->dependent_devices, list) + dock_hotplug_event(dd, event, DOCK_CALL_FIXUP); + + /* Call driver specific hotplug functions. */ + list_for_each_entry(dd, &ds->dependent_devices, list) + dock_hotplug_event(dd, event, DOCK_CALL_HANDLER); + + /* + * Check if all devices have been enumerated already. If not, run + * acpi_bus_scan() for them and that will cause scan handlers to be + * attached to device objects or acpi_drivers to be stopped/started if + * they are present. + */ + list_for_each_entry(dd, &ds->dependent_devices, list) { + struct acpi_device *adev = dd->adev; + + if (!acpi_device_enumerated(adev)) { + int ret = acpi_bus_scan(adev->handle); + if (ret) + dev_dbg(&adev->dev, "scan error %d\n", -ret); + } + } +} + +static void dock_event(struct dock_station *ds, u32 event, int num) +{ + struct device *dev = &ds->dock_device->dev; + char event_string[13]; + char *envp[] = { event_string, NULL }; + struct dock_dependent_device *dd; + + if (num == UNDOCK_EVENT) + sprintf(event_string, "EVENT=undock"); + else + sprintf(event_string, "EVENT=dock"); + + /* + * Indicate that the status of the dock station has + * changed. + */ + if (num == DOCK_EVENT) + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); + + list_for_each_entry(dd, &ds->dependent_devices, list) + dock_hotplug_event(dd, event, DOCK_CALL_UEVENT); + + if (num != DOCK_EVENT) + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); +} + +/** + * handle_dock - handle a dock event + * @ds: the dock station + * @dock: to dock, or undock - that is the question + * + * Execute the _DCK method in response to an acpi event + */ +static void handle_dock(struct dock_station *ds, int dock) +{ + acpi_status status; + struct acpi_object_list arg_list; + union acpi_object arg; + unsigned long long value; + + acpi_handle_info(ds->handle, "%s\n", dock ? "docking" : "undocking"); + + /* _DCK method has one argument */ + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = dock; + status = acpi_evaluate_integer(ds->handle, "_DCK", &arg_list, &value); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) + acpi_handle_err(ds->handle, "Failed to execute _DCK (0x%x)\n", + status); +} + +static inline void dock(struct dock_station *ds) +{ + handle_dock(ds, 1); +} + +static inline void undock(struct dock_station *ds) +{ + handle_dock(ds, 0); +} + +static inline void begin_dock(struct dock_station *ds) +{ + ds->flags |= DOCK_DOCKING; +} + +static inline void complete_dock(struct dock_station *ds) +{ + ds->flags &= ~(DOCK_DOCKING); + ds->last_dock_time = jiffies; +} + +static inline void begin_undock(struct dock_station *ds) +{ + ds->flags |= DOCK_UNDOCKING; +} + +static inline void complete_undock(struct dock_station *ds) +{ + ds->flags &= ~(DOCK_UNDOCKING); +} + +/** + * dock_in_progress - see if we are in the middle of handling a dock event + * @ds: the dock station + * + * Sometimes while docking, false dock events can be sent to the driver + * because good connections aren't made or some other reason. Ignore these + * if we are in the middle of doing something. + */ +static int dock_in_progress(struct dock_station *ds) +{ + if ((ds->flags & DOCK_DOCKING) || + time_before(jiffies, (ds->last_dock_time + HZ))) + return 1; + return 0; +} + +/** + * handle_eject_request - handle an undock request checking for error conditions + * + * Check to make sure the dock device is still present, then undock and + * hotremove all the devices that may need removing. + */ +static int handle_eject_request(struct dock_station *ds, u32 event) +{ + if (dock_in_progress(ds)) + return -EBUSY; + + /* + * here we need to generate the undock + * event prior to actually doing the undock + * so that the device struct still exists. + * Also, even send the dock event if the + * device is not present anymore + */ + dock_event(ds, event, UNDOCK_EVENT); + + hot_remove_dock_devices(ds); + undock(ds); + acpi_evaluate_lck(ds->handle, 0); + acpi_evaluate_ej0(ds->handle); + if (dock_present(ds)) { + acpi_handle_err(ds->handle, "Unable to undock!\n"); + return -EBUSY; + } + complete_undock(ds); + return 0; +} + +/** + * dock_notify - Handle ACPI dock notification. + * @adev: Dock station's ACPI device object. + * @event: Event code. + * + * If we are notified to dock, then check to see if the dock is + * present and then dock. Notify all drivers of the dock event, + * and then hotplug and devices that may need hotplugging. + */ +int dock_notify(struct acpi_device *adev, u32 event) +{ + acpi_handle handle = adev->handle; + struct dock_station *ds = find_dock_station(handle); + int surprise_removal = 0; + + if (!ds) + return -ENODEV; + + /* + * According to acpi spec 3.0a, if a DEVICE_CHECK notification + * is sent and _DCK is present, it is assumed to mean an undock + * request. + */ + if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK) + event = ACPI_NOTIFY_EJECT_REQUEST; + + /* + * dock station: BUS_CHECK - docked or surprise removal + * DEVICE_CHECK - undocked + * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal + * + * To simplify event handling, dock dependent device handler always + * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and + * ACPI_NOTIFY_EJECT_REQUEST for removal + */ + switch (event) { + case ACPI_NOTIFY_BUS_CHECK: + case ACPI_NOTIFY_DEVICE_CHECK: + if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) { + begin_dock(ds); + dock(ds); + if (!dock_present(ds)) { + acpi_handle_err(handle, "Unable to dock!\n"); + complete_dock(ds); + break; + } + hotplug_dock_devices(ds, event); + complete_dock(ds); + dock_event(ds, event, DOCK_EVENT); + acpi_evaluate_lck(ds->handle, 1); + acpi_update_all_gpes(); + break; + } + if (dock_present(ds) || dock_in_progress(ds)) + break; + /* This is a surprise removal */ + surprise_removal = 1; + event = ACPI_NOTIFY_EJECT_REQUEST; + /* Fall back */ + case ACPI_NOTIFY_EJECT_REQUEST: + begin_undock(ds); + if ((immediate_undock && !(ds->flags & DOCK_IS_ATA)) + || surprise_removal) + handle_eject_request(ds, event); + else + dock_event(ds, event, UNDOCK_EVENT); + break; + } + return 0; +} + +/* + * show_docked - read method for "docked" file in sysfs + */ +static ssize_t show_docked(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dock_station *dock_station = dev->platform_data; + struct acpi_device *adev = NULL; + + acpi_bus_get_device(dock_station->handle, &adev); + return snprintf(buf, PAGE_SIZE, "%u\n", acpi_device_enumerated(adev)); +} +static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL); + +/* + * show_flags - read method for flags file in sysfs + */ +static ssize_t show_flags(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dock_station *dock_station = dev->platform_data; + return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags); + +} +static DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL); + +/* + * write_undock - write method for "undock" file in sysfs + */ +static ssize_t write_undock(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct dock_station *dock_station = dev->platform_data; + + if (!count) + return -EINVAL; + + acpi_scan_lock_acquire(); + begin_undock(dock_station); + ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST); + acpi_scan_lock_release(); + return ret ? ret: count; +} +static DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock); + +/* + * show_dock_uid - read method for "uid" file in sysfs + */ +static ssize_t show_dock_uid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long long lbuf; + struct dock_station *dock_station = dev->platform_data; + acpi_status status = acpi_evaluate_integer(dock_station->handle, + "_UID", NULL, &lbuf); + if (ACPI_FAILURE(status)) + return 0; + + return snprintf(buf, PAGE_SIZE, "%llx\n", lbuf); +} +static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); + +static ssize_t show_dock_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dock_station *dock_station = dev->platform_data; + char *type; + + if (dock_station->flags & DOCK_IS_DOCK) + type = "dock_station"; + else if (dock_station->flags & DOCK_IS_ATA) + type = "ata_bay"; + else if (dock_station->flags & DOCK_IS_BAT) + type = "battery_bay"; + else + type = "unknown"; + + return snprintf(buf, PAGE_SIZE, "%s\n", type); +} +static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL); + +static struct attribute *dock_attributes[] = { + &dev_attr_docked.attr, + &dev_attr_flags.attr, + &dev_attr_undock.attr, + &dev_attr_uid.attr, + &dev_attr_type.attr, + NULL +}; + +static struct attribute_group dock_attribute_group = { + .attrs = dock_attributes +}; + +/** + * acpi_dock_add - Add a new dock station + * @adev: Dock station ACPI device object. + * + * allocated and initialize a new dock station device. + */ +void acpi_dock_add(struct acpi_device *adev) +{ + struct dock_station *dock_station, ds = { NULL, }; + struct platform_device_info pdevinfo; + acpi_handle handle = adev->handle; + struct platform_device *dd; + int ret; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name = "dock"; + pdevinfo.id = dock_station_count; + pdevinfo.fwnode = acpi_fwnode_handle(adev); + pdevinfo.data = &ds; + pdevinfo.size_data = sizeof(ds); + dd = platform_device_register_full(&pdevinfo); + if (IS_ERR(dd)) + return; + + dock_station = dd->dev.platform_data; + + dock_station->handle = handle; + dock_station->dock_device = dd; + dock_station->last_dock_time = jiffies - HZ; + + INIT_LIST_HEAD(&dock_station->sibling); + INIT_LIST_HEAD(&dock_station->dependent_devices); + + /* we want the dock device to send uevents */ + dev_set_uevent_suppress(&dd->dev, 0); + + if (acpi_dock_match(handle)) + dock_station->flags |= DOCK_IS_DOCK; + if (acpi_ata_match(handle)) + dock_station->flags |= DOCK_IS_ATA; + if (acpi_device_is_battery(adev)) + dock_station->flags |= DOCK_IS_BAT; + + ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); + if (ret) + goto err_unregister; + + /* add the dock station as a device dependent on itself */ + ret = add_dock_dependent_device(dock_station, adev); + if (ret) + goto err_rmgroup; + + dock_station_count++; + list_add(&dock_station->sibling, &dock_stations); + adev->flags.is_dock_station = true; + dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n", + dock_station_count); + return; + +err_rmgroup: + sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); + +err_unregister: + platform_device_unregister(dd); + acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret); +} diff --git a/kernel/drivers/acpi/ec.c b/kernel/drivers/acpi/ec.c new file mode 100644 index 000000000..5e8fed448 --- /dev/null +++ b/kernel/drivers/acpi/ec.c @@ -0,0 +1,1459 @@ +/* + * ec.c - ACPI Embedded Controller Driver (v3) + * + * Copyright (C) 2001-2015 Intel Corporation + * Author: 2014, 2015 Lv Zheng + * 2006, 2007 Alexey Starikovskiy + * 2006 Denis Sadykov + * 2004 Luming Yu + * 2001, 2002 Andy Grover + * 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2008 Alexey Starikovskiy + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* Uncomment next line to get verbose printout */ +/* #define DEBUG */ +#define pr_fmt(fmt) "ACPI : EC: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define ACPI_EC_CLASS "embedded_controller" +#define ACPI_EC_DEVICE_NAME "Embedded Controller" +#define ACPI_EC_FILE_INFO "info" + +/* EC status register */ +#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ +#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ +#define ACPI_EC_FLAG_CMD 0x08 /* Input buffer contains a command */ +#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ +#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ + +/* EC commands */ +enum ec_command { + ACPI_EC_COMMAND_READ = 0x80, + ACPI_EC_COMMAND_WRITE = 0x81, + ACPI_EC_BURST_ENABLE = 0x82, + ACPI_EC_BURST_DISABLE = 0x83, + ACPI_EC_COMMAND_QUERY = 0x84, +}; + +#define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ +#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ +#define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ +#define ACPI_EC_UDELAY_POLL 1000 /* Wait 1ms for EC transaction polling */ +#define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query + * when trying to clear the EC */ + +enum { + EC_FLAGS_QUERY_PENDING, /* Query is pending */ + EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and + * OpReg are installed */ + EC_FLAGS_STARTED, /* Driver is started */ + EC_FLAGS_STOPPED, /* Driver is stopped */ + EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the + * current command processing */ +}; + +#define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ +#define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */ + +/* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ +static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; +module_param(ec_delay, uint, 0644); +MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes"); + +/* + * If the number of false interrupts per one transaction exceeds + * this threshold, will think there is a GPE storm happened and + * will disable the GPE for normal transaction. + */ +static unsigned int ec_storm_threshold __read_mostly = 8; +module_param(ec_storm_threshold, uint, 0644); +MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); + +struct acpi_ec_query_handler { + struct list_head node; + acpi_ec_query_func func; + acpi_handle handle; + void *data; + u8 query_bit; + struct kref kref; +}; + +struct transaction { + const u8 *wdata; + u8 *rdata; + unsigned short irq_count; + u8 command; + u8 wi; + u8 ri; + u8 wlen; + u8 rlen; + u8 flags; + unsigned long timestamp; +}; + +static int acpi_ec_query(struct acpi_ec *ec, u8 *data); +static void advance_transaction(struct acpi_ec *ec); + +struct acpi_ec *boot_ec, *first_ec; +EXPORT_SYMBOL(first_ec); + +static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ +static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ +static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ +static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ +static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ + +/* -------------------------------------------------------------------------- + * Logging/Debugging + * -------------------------------------------------------------------------- */ + +/* + * Splitters used by the developers to track the boundary of the EC + * handling processes. + */ +#ifdef DEBUG +#define EC_DBG_SEP " " +#define EC_DBG_DRV "+++++" +#define EC_DBG_STM "=====" +#define EC_DBG_REQ "*****" +#define EC_DBG_EVT "#####" +#else +#define EC_DBG_SEP "" +#define EC_DBG_DRV +#define EC_DBG_STM +#define EC_DBG_REQ +#define EC_DBG_EVT +#endif + +#define ec_log_raw(fmt, ...) \ + pr_info(fmt "\n", ##__VA_ARGS__) +#define ec_dbg_raw(fmt, ...) \ + pr_debug(fmt "\n", ##__VA_ARGS__) +#define ec_log(filter, fmt, ...) \ + ec_log_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__) +#define ec_dbg(filter, fmt, ...) \ + ec_dbg_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__) + +#define ec_log_drv(fmt, ...) \ + ec_log(EC_DBG_DRV, fmt, ##__VA_ARGS__) +#define ec_dbg_drv(fmt, ...) \ + ec_dbg(EC_DBG_DRV, fmt, ##__VA_ARGS__) +#define ec_dbg_stm(fmt, ...) \ + ec_dbg(EC_DBG_STM, fmt, ##__VA_ARGS__) +#define ec_dbg_req(fmt, ...) \ + ec_dbg(EC_DBG_REQ, fmt, ##__VA_ARGS__) +#define ec_dbg_evt(fmt, ...) \ + ec_dbg(EC_DBG_EVT, fmt, ##__VA_ARGS__) +#define ec_dbg_ref(ec, fmt, ...) \ + ec_dbg_raw("%lu: " fmt, ec->reference_count, ## __VA_ARGS__) + +/* -------------------------------------------------------------------------- + * Device Flags + * -------------------------------------------------------------------------- */ + +static bool acpi_ec_started(struct acpi_ec *ec) +{ + return test_bit(EC_FLAGS_STARTED, &ec->flags) && + !test_bit(EC_FLAGS_STOPPED, &ec->flags); +} + +static bool acpi_ec_flushed(struct acpi_ec *ec) +{ + return ec->reference_count == 1; +} + +/* -------------------------------------------------------------------------- + * EC Registers + * -------------------------------------------------------------------------- */ + +static inline u8 acpi_ec_read_status(struct acpi_ec *ec) +{ + u8 x = inb(ec->command_addr); + + ec_dbg_raw("EC_SC(R) = 0x%2.2x " + "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d", + x, + !!(x & ACPI_EC_FLAG_SCI), + !!(x & ACPI_EC_FLAG_BURST), + !!(x & ACPI_EC_FLAG_CMD), + !!(x & ACPI_EC_FLAG_IBF), + !!(x & ACPI_EC_FLAG_OBF)); + return x; +} + +static inline u8 acpi_ec_read_data(struct acpi_ec *ec) +{ + u8 x = inb(ec->data_addr); + + ec->curr->timestamp = jiffies; + ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x); + return x; +} + +static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) +{ + ec_dbg_raw("EC_SC(W) = 0x%2.2x", command); + outb(command, ec->command_addr); + ec->curr->timestamp = jiffies; +} + +static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) +{ + ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data); + outb(data, ec->data_addr); + ec->curr->timestamp = jiffies; +} + +#ifdef DEBUG +static const char *acpi_ec_cmd_string(u8 cmd) +{ + switch (cmd) { + case 0x80: + return "RD_EC"; + case 0x81: + return "WR_EC"; + case 0x82: + return "BE_EC"; + case 0x83: + return "BD_EC"; + case 0x84: + return "QR_EC"; + } + return "UNKNOWN"; +} +#else +#define acpi_ec_cmd_string(cmd) "UNDEF" +#endif + +/* -------------------------------------------------------------------------- + * GPE Registers + * -------------------------------------------------------------------------- */ + +static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec) +{ + acpi_event_status gpe_status = 0; + + (void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status); + return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false; +} + +static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open) +{ + if (open) + acpi_enable_gpe(NULL, ec->gpe); + else { + BUG_ON(ec->reference_count < 1); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); + } + if (acpi_ec_is_gpe_raised(ec)) { + /* + * On some platforms, EN=1 writes cannot trigger GPE. So + * software need to manually trigger a pseudo GPE event on + * EN=1 writes. + */ + ec_dbg_raw("Polling quirk"); + advance_transaction(ec); + } +} + +static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close) +{ + if (close) + acpi_disable_gpe(NULL, ec->gpe); + else { + BUG_ON(ec->reference_count < 1); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); + } +} + +static inline void acpi_ec_clear_gpe(struct acpi_ec *ec) +{ + /* + * GPE STS is a W1C register, which means: + * 1. Software can clear it without worrying about clearing other + * GPEs' STS bits when the hardware sets them in parallel. + * 2. As long as software can ensure only clearing it when it is + * set, hardware won't set it in parallel. + * So software can clear GPE in any contexts. + * Warning: do not move the check into advance_transaction() as the + * EC commands will be sent without GPE raised. + */ + if (!acpi_ec_is_gpe_raised(ec)) + return; + acpi_clear_gpe(NULL, ec->gpe); +} + +/* -------------------------------------------------------------------------- + * Transaction Management + * -------------------------------------------------------------------------- */ + +static void acpi_ec_submit_request(struct acpi_ec *ec) +{ + ec->reference_count++; + if (ec->reference_count == 1) + acpi_ec_enable_gpe(ec, true); +} + +static void acpi_ec_complete_request(struct acpi_ec *ec) +{ + bool flushed = false; + + ec->reference_count--; + if (ec->reference_count == 0) + acpi_ec_disable_gpe(ec, true); + flushed = acpi_ec_flushed(ec); + if (flushed) + wake_up(&ec->wait); +} + +static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag) +{ + if (!test_bit(flag, &ec->flags)) { + acpi_ec_disable_gpe(ec, false); + ec_dbg_drv("Polling enabled"); + set_bit(flag, &ec->flags); + } +} + +static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag) +{ + if (test_bit(flag, &ec->flags)) { + clear_bit(flag, &ec->flags); + acpi_ec_enable_gpe(ec, false); + ec_dbg_drv("Polling disabled"); + } +} + +/* + * acpi_ec_submit_flushable_request() - Increase the reference count unless + * the flush operation is not in + * progress + * @ec: the EC device + * + * This function must be used before taking a new action that should hold + * the reference count. If this function returns false, then the action + * must be discarded or it will prevent the flush operation from being + * completed. + */ +static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) +{ + if (!acpi_ec_started(ec)) + return false; + acpi_ec_submit_request(ec); + return true; +} + +static void acpi_ec_submit_query(struct acpi_ec *ec) +{ + if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { + ec_dbg_req("Event started"); + schedule_work(&ec->work); + } +} + +static void acpi_ec_complete_query(struct acpi_ec *ec) +{ + if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { + clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + ec_dbg_req("Event stopped"); + } +} + +static int ec_transaction_completed(struct acpi_ec *ec) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ec->lock, flags); + if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) + ret = 1; + spin_unlock_irqrestore(&ec->lock, flags); + return ret; +} + +static void advance_transaction(struct acpi_ec *ec) +{ + struct transaction *t; + u8 status; + bool wakeup = false; + + ec_dbg_stm("%s (%d)", in_interrupt() ? "IRQ" : "TASK", + smp_processor_id()); + /* + * By always clearing STS before handling all indications, we can + * ensure a hardware STS 0->1 change after this clearing can always + * trigger a GPE interrupt. + */ + acpi_ec_clear_gpe(ec); + status = acpi_ec_read_status(ec); + t = ec->curr; + if (!t) + goto err; + if (t->flags & ACPI_EC_COMMAND_POLL) { + if (t->wlen > t->wi) { + if ((status & ACPI_EC_FLAG_IBF) == 0) + acpi_ec_write_data(ec, t->wdata[t->wi++]); + else + goto err; + } else if (t->rlen > t->ri) { + if ((status & ACPI_EC_FLAG_OBF) == 1) { + t->rdata[t->ri++] = acpi_ec_read_data(ec); + if (t->rlen == t->ri) { + t->flags |= ACPI_EC_COMMAND_COMPLETE; + if (t->command == ACPI_EC_COMMAND_QUERY) + ec_dbg_req("Command(%s) hardware completion", + acpi_ec_cmd_string(t->command)); + wakeup = true; + } + } else + goto err; + } else if (t->wlen == t->wi && + (status & ACPI_EC_FLAG_IBF) == 0) { + t->flags |= ACPI_EC_COMMAND_COMPLETE; + wakeup = true; + } + goto out; + } else { + if (EC_FLAGS_QUERY_HANDSHAKE && + !(status & ACPI_EC_FLAG_SCI) && + (t->command == ACPI_EC_COMMAND_QUERY)) { + t->flags |= ACPI_EC_COMMAND_POLL; + acpi_ec_complete_query(ec); + t->rdata[t->ri++] = 0x00; + t->flags |= ACPI_EC_COMMAND_COMPLETE; + ec_dbg_req("Command(%s) software completion", + acpi_ec_cmd_string(t->command)); + wakeup = true; + } else if ((status & ACPI_EC_FLAG_IBF) == 0) { + acpi_ec_write_cmd(ec, t->command); + t->flags |= ACPI_EC_COMMAND_POLL; + acpi_ec_complete_query(ec); + } else + goto err; + goto out; + } +err: + /* + * If SCI bit is set, then don't think it's a false IRQ + * otherwise will take a not handled IRQ as a false one. + */ + if (!(status & ACPI_EC_FLAG_SCI)) { + if (in_interrupt() && t) { + if (t->irq_count < ec_storm_threshold) + ++t->irq_count; + /* Allow triggering on 0 threshold */ + if (t->irq_count == ec_storm_threshold) + acpi_ec_set_storm(ec, EC_FLAGS_COMMAND_STORM); + } + } +out: + if (status & ACPI_EC_FLAG_SCI) + acpi_ec_submit_query(ec); + if (wakeup && in_interrupt()) + wake_up(&ec->wait); +} + +static void start_transaction(struct acpi_ec *ec) +{ + ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; + ec->curr->flags = 0; + ec->curr->timestamp = jiffies; + advance_transaction(ec); +} + +static int ec_poll(struct acpi_ec *ec) +{ + unsigned long flags; + int repeat = 5; /* number of command restarts */ + + while (repeat--) { + unsigned long delay = jiffies + + msecs_to_jiffies(ec_delay); + unsigned long usecs = ACPI_EC_UDELAY_POLL; + do { + /* don't sleep with disabled interrupts */ + if (EC_FLAGS_MSI || irqs_disabled()) { + usecs = ACPI_EC_MSI_UDELAY; + udelay(usecs); + if (ec_transaction_completed(ec)) + return 0; + } else { + if (wait_event_timeout(ec->wait, + ec_transaction_completed(ec), + usecs_to_jiffies(usecs))) + return 0; + } + spin_lock_irqsave(&ec->lock, flags); + if (time_after(jiffies, + ec->curr->timestamp + + usecs_to_jiffies(usecs))) + advance_transaction(ec); + spin_unlock_irqrestore(&ec->lock, flags); + } while (time_before(jiffies, delay)); + pr_debug("controller reset, restart transaction\n"); + spin_lock_irqsave(&ec->lock, flags); + start_transaction(ec); + spin_unlock_irqrestore(&ec->lock, flags); + } + return -ETIME; +} + +static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, + struct transaction *t) +{ + unsigned long tmp; + int ret = 0; + + if (EC_FLAGS_MSI) + udelay(ACPI_EC_MSI_UDELAY); + /* start transaction */ + spin_lock_irqsave(&ec->lock, tmp); + /* Enable GPE for command processing (IBF=0/OBF=1) */ + if (!acpi_ec_submit_flushable_request(ec)) { + ret = -EINVAL; + goto unlock; + } + ec_dbg_ref(ec, "Increase command"); + /* following two actions should be kept atomic */ + ec->curr = t; + ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command)); + start_transaction(ec); + spin_unlock_irqrestore(&ec->lock, tmp); + ret = ec_poll(ec); + spin_lock_irqsave(&ec->lock, tmp); + if (t->irq_count == ec_storm_threshold) + acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM); + ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command)); + ec->curr = NULL; + /* Disable GPE for command processing (IBF=0/OBF=1) */ + acpi_ec_complete_request(ec); + ec_dbg_ref(ec, "Decrease command"); +unlock: + spin_unlock_irqrestore(&ec->lock, tmp); + return ret; +} + +static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) +{ + int status; + u32 glk; + + if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata)) + return -EINVAL; + if (t->rdata) + memset(t->rdata, 0, t->rlen); + mutex_lock(&ec->mutex); + if (ec->global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) { + status = -ENODEV; + goto unlock; + } + } + + status = acpi_ec_transaction_unlocked(ec, t); + + if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags)) + msleep(1); + if (ec->global_lock) + acpi_release_global_lock(glk); +unlock: + mutex_unlock(&ec->mutex); + return status; +} + +static int acpi_ec_burst_enable(struct acpi_ec *ec) +{ + u8 d; + struct transaction t = {.command = ACPI_EC_BURST_ENABLE, + .wdata = NULL, .rdata = &d, + .wlen = 0, .rlen = 1}; + + return acpi_ec_transaction(ec, &t); +} + +static int acpi_ec_burst_disable(struct acpi_ec *ec) +{ + struct transaction t = {.command = ACPI_EC_BURST_DISABLE, + .wdata = NULL, .rdata = NULL, + .wlen = 0, .rlen = 0}; + + return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ? + acpi_ec_transaction(ec, &t) : 0; +} + +static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data) +{ + int result; + u8 d; + struct transaction t = {.command = ACPI_EC_COMMAND_READ, + .wdata = &address, .rdata = &d, + .wlen = 1, .rlen = 1}; + + result = acpi_ec_transaction(ec, &t); + *data = d; + return result; +} + +static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) +{ + u8 wdata[2] = { address, data }; + struct transaction t = {.command = ACPI_EC_COMMAND_WRITE, + .wdata = wdata, .rdata = NULL, + .wlen = 2, .rlen = 0}; + + return acpi_ec_transaction(ec, &t); +} + +int ec_read(u8 addr, u8 *val) +{ + int err; + u8 temp_data; + + if (!first_ec) + return -ENODEV; + + err = acpi_ec_read(first_ec, addr, &temp_data); + + if (!err) { + *val = temp_data; + return 0; + } + return err; +} +EXPORT_SYMBOL(ec_read); + +int ec_write(u8 addr, u8 val) +{ + int err; + + if (!first_ec) + return -ENODEV; + + err = acpi_ec_write(first_ec, addr, val); + + return err; +} +EXPORT_SYMBOL(ec_write); + +int ec_transaction(u8 command, + const u8 *wdata, unsigned wdata_len, + u8 *rdata, unsigned rdata_len) +{ + struct transaction t = {.command = command, + .wdata = wdata, .rdata = rdata, + .wlen = wdata_len, .rlen = rdata_len}; + + if (!first_ec) + return -ENODEV; + + return acpi_ec_transaction(first_ec, &t); +} +EXPORT_SYMBOL(ec_transaction); + +/* Get the handle to the EC device */ +acpi_handle ec_get_handle(void) +{ + if (!first_ec) + return NULL; + return first_ec->handle; +} +EXPORT_SYMBOL(ec_get_handle); + +/* + * Process _Q events that might have accumulated in the EC. + * Run with locked ec mutex. + */ +static void acpi_ec_clear(struct acpi_ec *ec) +{ + int i, status; + u8 value = 0; + + for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { + status = acpi_ec_query(ec, &value); + if (status || !value) + break; + } + + if (unlikely(i == ACPI_EC_CLEAR_MAX)) + pr_warn("Warning: Maximum of %d stale EC events cleared\n", i); + else + pr_info("%d stale EC events cleared\n", i); +} + +static void acpi_ec_start(struct acpi_ec *ec, bool resuming) +{ + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { + ec_dbg_drv("Starting EC"); + /* Enable GPE for event processing (SCI_EVT=1) */ + if (!resuming) { + acpi_ec_submit_request(ec); + ec_dbg_ref(ec, "Increase driver"); + } + ec_log_drv("EC started"); + } + spin_unlock_irqrestore(&ec->lock, flags); +} + +static bool acpi_ec_stopped(struct acpi_ec *ec) +{ + unsigned long flags; + bool flushed; + + spin_lock_irqsave(&ec->lock, flags); + flushed = acpi_ec_flushed(ec); + spin_unlock_irqrestore(&ec->lock, flags); + return flushed; +} + +static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) +{ + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + if (acpi_ec_started(ec)) { + ec_dbg_drv("Stopping EC"); + set_bit(EC_FLAGS_STOPPED, &ec->flags); + spin_unlock_irqrestore(&ec->lock, flags); + wait_event(ec->wait, acpi_ec_stopped(ec)); + spin_lock_irqsave(&ec->lock, flags); + /* Disable GPE for event processing (SCI_EVT=1) */ + if (!suspending) { + acpi_ec_complete_request(ec); + ec_dbg_ref(ec, "Decrease driver"); + } + clear_bit(EC_FLAGS_STARTED, &ec->flags); + clear_bit(EC_FLAGS_STOPPED, &ec->flags); + ec_log_drv("EC stopped"); + } + spin_unlock_irqrestore(&ec->lock, flags); +} + +void acpi_ec_block_transactions(void) +{ + struct acpi_ec *ec = first_ec; + + if (!ec) + return; + + mutex_lock(&ec->mutex); + /* Prevent transactions from being carried out */ + acpi_ec_stop(ec, true); + mutex_unlock(&ec->mutex); +} + +void acpi_ec_unblock_transactions(void) +{ + struct acpi_ec *ec = first_ec; + + if (!ec) + return; + + /* Allow transactions to be carried out again */ + acpi_ec_start(ec, true); + + if (EC_FLAGS_CLEAR_ON_RESUME) + acpi_ec_clear(ec); +} + +void acpi_ec_unblock_transactions_early(void) +{ + /* + * Allow transactions to happen again (this function is called from + * atomic context during wakeup, so we don't need to acquire the mutex). + */ + if (first_ec) + acpi_ec_start(first_ec, true); +} + +/* -------------------------------------------------------------------------- + Event Management + -------------------------------------------------------------------------- */ +static struct acpi_ec_query_handler * +acpi_ec_get_query_handler(struct acpi_ec_query_handler *handler) +{ + if (handler) + kref_get(&handler->kref); + return handler; +} + +static void acpi_ec_query_handler_release(struct kref *kref) +{ + struct acpi_ec_query_handler *handler = + container_of(kref, struct acpi_ec_query_handler, kref); + + kfree(handler); +} + +static void acpi_ec_put_query_handler(struct acpi_ec_query_handler *handler) +{ + kref_put(&handler->kref, acpi_ec_query_handler_release); +} + +int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, + acpi_handle handle, acpi_ec_query_func func, + void *data) +{ + struct acpi_ec_query_handler *handler = + kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL); + + if (!handler) + return -ENOMEM; + + handler->query_bit = query_bit; + handler->handle = handle; + handler->func = func; + handler->data = data; + mutex_lock(&ec->mutex); + kref_init(&handler->kref); + list_add(&handler->node, &ec->list); + mutex_unlock(&ec->mutex); + return 0; +} +EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); + +void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) +{ + struct acpi_ec_query_handler *handler, *tmp; + LIST_HEAD(free_list); + + mutex_lock(&ec->mutex); + list_for_each_entry_safe(handler, tmp, &ec->list, node) { + if (query_bit == handler->query_bit) { + list_del_init(&handler->node); + list_add(&handler->node, &free_list); + } + } + mutex_unlock(&ec->mutex); + list_for_each_entry_safe(handler, tmp, &free_list, node) + acpi_ec_put_query_handler(handler); +} +EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); + +static void acpi_ec_run(void *cxt) +{ + struct acpi_ec_query_handler *handler = cxt; + + if (!handler) + return; + ec_dbg_evt("Query(0x%02x) started", handler->query_bit); + if (handler->func) + handler->func(handler->data); + else if (handler->handle) + acpi_evaluate_object(handler->handle, NULL, NULL, NULL); + ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit); + acpi_ec_put_query_handler(handler); +} + +static int acpi_ec_query(struct acpi_ec *ec, u8 *data) +{ + u8 value = 0; + int result; + acpi_status status; + struct acpi_ec_query_handler *handler; + struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, + .wdata = NULL, .rdata = &value, + .wlen = 0, .rlen = 1}; + + /* + * Query the EC to find out which _Qxx method we need to evaluate. + * Note that successful completion of the query causes the ACPI_EC_SCI + * bit to be cleared (and thus clearing the interrupt source). + */ + result = acpi_ec_transaction(ec, &t); + if (result) + return result; + if (data) + *data = value; + if (!value) + return -ENODATA; + + mutex_lock(&ec->mutex); + list_for_each_entry(handler, &ec->list, node) { + if (value == handler->query_bit) { + /* have custom handler for this bit */ + handler = acpi_ec_get_query_handler(handler); + ec_dbg_evt("Query(0x%02x) scheduled", + handler->query_bit); + status = acpi_os_execute((handler->func) ? + OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, + acpi_ec_run, handler); + if (ACPI_FAILURE(status)) + result = -EBUSY; + break; + } + } + mutex_unlock(&ec->mutex); + return result; +} + +static void acpi_ec_gpe_poller(struct work_struct *work) +{ + struct acpi_ec *ec = container_of(work, struct acpi_ec, work); + + acpi_ec_query(ec, NULL); +} + +static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, void *data) +{ + unsigned long flags; + struct acpi_ec *ec = data; + + spin_lock_irqsave(&ec->lock, flags); + advance_transaction(ec); + spin_unlock_irqrestore(&ec->lock, flags); + return ACPI_INTERRUPT_HANDLED; +} + +/* -------------------------------------------------------------------------- + * Address Space Management + * -------------------------------------------------------------------------- */ + +static acpi_status +acpi_ec_space_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct acpi_ec *ec = handler_context; + int result = 0, i, bytes = bits / 8; + u8 *value = (u8 *)value64; + + if ((address > 0xFF) || !value || !handler_context) + return AE_BAD_PARAMETER; + + if (function != ACPI_READ && function != ACPI_WRITE) + return AE_BAD_PARAMETER; + + if (EC_FLAGS_MSI || bits > 8) + acpi_ec_burst_enable(ec); + + for (i = 0; i < bytes; ++i, ++address, ++value) + result = (function == ACPI_READ) ? + acpi_ec_read(ec, address, value) : + acpi_ec_write(ec, address, *value); + + if (EC_FLAGS_MSI || bits > 8) + acpi_ec_burst_disable(ec); + + switch (result) { + case -EINVAL: + return AE_BAD_PARAMETER; + case -ENODEV: + return AE_NOT_FOUND; + case -ETIME: + return AE_TIME; + default: + return AE_OK; + } +} + +/* -------------------------------------------------------------------------- + * Driver Interface + * -------------------------------------------------------------------------- */ + +static acpi_status +ec_parse_io_ports(struct acpi_resource *resource, void *context); + +static struct acpi_ec *make_acpi_ec(void) +{ + struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); + + if (!ec) + return NULL; + ec->flags = 1 << EC_FLAGS_QUERY_PENDING; + mutex_init(&ec->mutex); + init_waitqueue_head(&ec->wait); + INIT_LIST_HEAD(&ec->list); + spin_lock_init(&ec->lock); + INIT_WORK(&ec->work, acpi_ec_gpe_poller); + return ec; +} + +static acpi_status +acpi_ec_register_query_methods(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; + struct acpi_ec *ec = context; + int value = 0; + acpi_status status; + + status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + + if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) + acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); + return AE_OK; +} + +static acpi_status +ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) +{ + acpi_status status; + unsigned long long tmp = 0; + struct acpi_ec *ec = context; + + /* clear addr values, ec_parse_io_ports depend on it */ + ec->command_addr = ec->data_addr = 0; + + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + ec_parse_io_ports, ec); + if (ACPI_FAILURE(status)) + return status; + + /* Get GPE bit assignment (EC events). */ + /* TODO: Add support for _GPE returning a package */ + status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); + if (ACPI_FAILURE(status)) + return status; + ec->gpe = tmp; + /* Use the global lock for all EC transactions? */ + tmp = 0; + acpi_evaluate_integer(handle, "_GLK", NULL, &tmp); + ec->global_lock = tmp; + ec->handle = handle; + return AE_CTRL_TERMINATE; +} + +static int ec_install_handlers(struct acpi_ec *ec) +{ + acpi_status status; + + if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) + return 0; + status = acpi_install_gpe_raw_handler(NULL, ec->gpe, + ACPI_GPE_EDGE_TRIGGERED, + &acpi_ec_gpe_handler, ec); + if (ACPI_FAILURE(status)) + return -ENODEV; + + acpi_ec_start(ec, false); + status = acpi_install_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, + &acpi_ec_space_handler, + NULL, ec); + if (ACPI_FAILURE(status)) { + if (status == AE_NOT_FOUND) { + /* + * Maybe OS fails in evaluating the _REG object. + * The AE_NOT_FOUND error will be ignored and OS + * continue to initialize EC. + */ + pr_err("Fail in evaluating the _REG object" + " of EC device. Broken bios is suspected.\n"); + } else { + acpi_ec_stop(ec, false); + acpi_remove_gpe_handler(NULL, ec->gpe, + &acpi_ec_gpe_handler); + return -ENODEV; + } + } + + set_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); + return 0; +} + +static void ec_remove_handlers(struct acpi_ec *ec) +{ + if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) + return; + acpi_ec_stop(ec, false); + if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) + pr_err("failed to remove space handler\n"); + if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, + &acpi_ec_gpe_handler))) + pr_err("failed to remove gpe handler\n"); + clear_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); +} + +static int acpi_ec_add(struct acpi_device *device) +{ + struct acpi_ec *ec = NULL; + int ret; + + strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_EC_CLASS); + + /* Check for boot EC */ + if (boot_ec && + (boot_ec->handle == device->handle || + boot_ec->handle == ACPI_ROOT_OBJECT)) { + ec = boot_ec; + boot_ec = NULL; + } else { + ec = make_acpi_ec(); + if (!ec) + return -ENOMEM; + } + if (ec_parse_device(device->handle, 0, ec, NULL) != + AE_CTRL_TERMINATE) { + kfree(ec); + return -EINVAL; + } + + /* Find and register all query methods */ + acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, + acpi_ec_register_query_methods, NULL, ec, NULL); + + if (!first_ec) + first_ec = ec; + device->driver_data = ec; + + ret = !!request_region(ec->data_addr, 1, "EC data"); + WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr); + ret = !!request_region(ec->command_addr, 1, "EC cmd"); + WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr); + + pr_info("GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", + ec->gpe, ec->command_addr, ec->data_addr); + + ret = ec_install_handlers(ec); + + /* Reprobe devices depending on the EC */ + acpi_walk_dep_device_list(ec->handle); + + /* EC is fully operational, allow queries */ + clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + + /* Clear stale _Q events if hardware might require that */ + if (EC_FLAGS_CLEAR_ON_RESUME) + acpi_ec_clear(ec); + return ret; +} + +static int acpi_ec_remove(struct acpi_device *device) +{ + struct acpi_ec *ec; + struct acpi_ec_query_handler *handler, *tmp; + + if (!device) + return -EINVAL; + + ec = acpi_driver_data(device); + ec_remove_handlers(ec); + mutex_lock(&ec->mutex); + list_for_each_entry_safe(handler, tmp, &ec->list, node) { + list_del(&handler->node); + kfree(handler); + } + mutex_unlock(&ec->mutex); + release_region(ec->data_addr, 1); + release_region(ec->command_addr, 1); + device->driver_data = NULL; + if (ec == first_ec) + first_ec = NULL; + kfree(ec); + return 0; +} + +static acpi_status +ec_parse_io_ports(struct acpi_resource *resource, void *context) +{ + struct acpi_ec *ec = context; + + if (resource->type != ACPI_RESOURCE_TYPE_IO) + return AE_OK; + + /* + * The first address region returned is the data port, and + * the second address region returned is the status/command + * port. + */ + if (ec->data_addr == 0) + ec->data_addr = resource->data.io.minimum; + else if (ec->command_addr == 0) + ec->command_addr = resource->data.io.minimum; + else + return AE_CTRL_TERMINATE; + + return AE_OK; +} + +int __init acpi_boot_ec_enable(void) +{ + if (!boot_ec || test_bit(EC_FLAGS_HANDLERS_INSTALLED, &boot_ec->flags)) + return 0; + if (!ec_install_handlers(boot_ec)) { + first_ec = boot_ec; + return 0; + } + return -EFAULT; +} + +static const struct acpi_device_id ec_device_ids[] = { + {"PNP0C09", 0}, + {"", 0}, +}; + +/* Some BIOS do not survive early DSDT scan, skip it */ +static int ec_skip_dsdt_scan(const struct dmi_system_id *id) +{ + EC_FLAGS_SKIP_DSDT_SCAN = 1; + return 0; +} + +/* ASUStek often supplies us with broken ECDT, validate it */ +static int ec_validate_ecdt(const struct dmi_system_id *id) +{ + EC_FLAGS_VALIDATE_ECDT = 1; + return 0; +} + +/* MSI EC needs special treatment, enable it */ +static int ec_flag_msi(const struct dmi_system_id *id) +{ + pr_debug("Detected MSI hardware, enabling workarounds.\n"); + EC_FLAGS_MSI = 1; + EC_FLAGS_VALIDATE_ECDT = 1; + return 0; +} + +/* + * Clevo M720 notebook actually works ok with IRQ mode, if we lifted + * the GPE storm threshold back to 20 + */ +static int ec_enlarge_storm_threshold(const struct dmi_system_id *id) +{ + pr_debug("Setting the EC GPE storm threshold to 20\n"); + ec_storm_threshold = 20; + return 0; +} + +/* + * Acer EC firmware refuses to respond QR_EC when SCI_EVT is not set, for + * which case, we complete the QR_EC without issuing it to the firmware. + * https://bugzilla.kernel.org/show_bug.cgi?id=86211 + */ +static int ec_flag_query_handshake(const struct dmi_system_id *id) +{ + pr_debug("Detected the EC firmware requiring QR_EC issued when SCI_EVT set\n"); + EC_FLAGS_QUERY_HANDSHAKE = 1; + return 0; +} + +/* + * On some hardware it is necessary to clear events accumulated by the EC during + * sleep. These ECs stop reporting GPEs until they are manually polled, if too + * many events are accumulated. (e.g. Samsung Series 5/9 notebooks) + * + * https://bugzilla.kernel.org/show_bug.cgi?id=44161 + * + * Ideally, the EC should also be instructed NOT to accumulate events during + * sleep (which Windows seems to do somehow), but the interface to control this + * behaviour is not known at this time. + * + * Models known to be affected are Samsung 530Uxx/535Uxx/540Uxx/550Pxx/900Xxx, + * however it is very likely that other Samsung models are affected. + * + * On systems which don't accumulate _Q events during sleep, this extra check + * should be harmless. + */ +static int ec_clear_on_resume(const struct dmi_system_id *id) +{ + pr_debug("Detected system needing EC poll on resume.\n"); + EC_FLAGS_CLEAR_ON_RESUME = 1; + return 0; +} + +static struct dmi_system_id ec_dmi_table[] __initdata = { + { + ec_skip_dsdt_scan, "Compal JFL92", { + DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), + DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL}, + { + ec_flag_msi, "MSI hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star")}, NULL}, + { + ec_flag_msi, "MSI hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star")}, NULL}, + { + ec_flag_msi, "MSI hardware", { + DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star")}, NULL}, + { + ec_flag_msi, "MSI hardware", { + DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR")}, NULL}, + { + ec_flag_msi, "Quanta hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "Quanta"), + DMI_MATCH(DMI_PRODUCT_NAME, "TW8/SW8/DW8"),}, NULL}, + { + ec_flag_msi, "Quanta hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "Quanta"), + DMI_MATCH(DMI_PRODUCT_NAME, "TW9/SW9"),}, NULL}, + { + ec_flag_msi, "Clevo W350etq", { + DMI_MATCH(DMI_SYS_VENDOR, "CLEVO CO."), + DMI_MATCH(DMI_PRODUCT_NAME, "W35_37ET"),}, NULL}, + { + ec_validate_ecdt, "ASUS hardware", { + DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL}, + { + ec_validate_ecdt, "ASUS hardware", { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc.") }, NULL}, + { + ec_enlarge_storm_threshold, "CLEVO hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."), + DMI_MATCH(DMI_PRODUCT_NAME, "M720T/M730T"),}, NULL}, + { + ec_skip_dsdt_scan, "HP Folio 13", { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13"),}, NULL}, + { + ec_validate_ecdt, "ASUS hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),}, NULL}, + { + ec_clear_on_resume, "Samsung hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL}, + { + ec_flag_query_handshake, "Acer hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), }, NULL}, + {}, +}; + +int __init acpi_ec_ecdt_probe(void) +{ + acpi_status status; + struct acpi_ec *saved_ec = NULL; + struct acpi_table_ecdt *ecdt_ptr; + + boot_ec = make_acpi_ec(); + if (!boot_ec) + return -ENOMEM; + /* + * Generate a boot ec context + */ + dmi_check_system(ec_dmi_table); + status = acpi_get_table(ACPI_SIG_ECDT, 1, + (struct acpi_table_header **)&ecdt_ptr); + if (ACPI_SUCCESS(status)) { + pr_info("EC description table is found, configuring boot EC\n"); + boot_ec->command_addr = ecdt_ptr->control.address; + boot_ec->data_addr = ecdt_ptr->data.address; + boot_ec->gpe = ecdt_ptr->gpe; + boot_ec->handle = ACPI_ROOT_OBJECT; + acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, + &boot_ec->handle); + /* Don't trust ECDT, which comes from ASUSTek */ + if (!EC_FLAGS_VALIDATE_ECDT) + goto install; + saved_ec = kmemdup(boot_ec, sizeof(struct acpi_ec), GFP_KERNEL); + if (!saved_ec) + return -ENOMEM; + /* fall through */ + } + + if (EC_FLAGS_SKIP_DSDT_SCAN) { + kfree(saved_ec); + return -ENODEV; + } + + /* This workaround is needed only on some broken machines, + * which require early EC, but fail to provide ECDT */ + pr_debug("Look up EC in DSDT\n"); + status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, + boot_ec, NULL); + /* Check that acpi_get_devices actually find something */ + if (ACPI_FAILURE(status) || !boot_ec->handle) + goto error; + if (saved_ec) { + /* try to find good ECDT from ASUSTek */ + if (saved_ec->command_addr != boot_ec->command_addr || + saved_ec->data_addr != boot_ec->data_addr || + saved_ec->gpe != boot_ec->gpe || + saved_ec->handle != boot_ec->handle) + pr_info("ASUSTek keeps feeding us with broken " + "ECDT tables, which are very hard to workaround. " + "Trying to use DSDT EC info instead. Please send " + "output of acpidump to linux-acpi@vger.kernel.org\n"); + kfree(saved_ec); + saved_ec = NULL; + } else { + /* We really need to limit this workaround, the only ASUS, + * which needs it, has fake EC._INI method, so use it as flag. + * Keep boot_ec struct as it will be needed soon. + */ + if (!dmi_name_in_vendors("ASUS") || + !acpi_has_method(boot_ec->handle, "_INI")) + return -ENODEV; + } +install: + if (!ec_install_handlers(boot_ec)) { + first_ec = boot_ec; + return 0; + } +error: + kfree(boot_ec); + kfree(saved_ec); + boot_ec = NULL; + return -ENODEV; +} + +static struct acpi_driver acpi_ec_driver = { + .name = "ec", + .class = ACPI_EC_CLASS, + .ids = ec_device_ids, + .ops = { + .add = acpi_ec_add, + .remove = acpi_ec_remove, + }, +}; + +int __init acpi_ec_init(void) +{ + int result = 0; + + /* Now register the driver for the EC */ + result = acpi_bus_register_driver(&acpi_ec_driver); + if (result < 0) + return -ENODEV; + + return result; +} + +/* EC driver currently not unloadable */ +#if 0 +static void __exit acpi_ec_exit(void) +{ + + acpi_bus_unregister_driver(&acpi_ec_driver); +} +#endif /* 0 */ diff --git a/kernel/drivers/acpi/ec_sys.c b/kernel/drivers/acpi/ec_sys.c new file mode 100644 index 000000000..b4c216bab --- /dev/null +++ b/kernel/drivers/acpi/ec_sys.c @@ -0,0 +1,162 @@ +/* + * ec_sys.c + * + * Copyright (C) 2010 SUSE Products GmbH/Novell + * Author: + * Thomas Renninger + * + * This work is licensed under the terms of the GNU GPL, version 2. + */ + +#include +#include +#include +#include +#include +#include "internal.h" + +MODULE_AUTHOR("Thomas Renninger "); +MODULE_DESCRIPTION("ACPI EC sysfs access driver"); +MODULE_LICENSE("GPL"); + +static bool write_support; +module_param(write_support, bool, 0644); +MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may " + "be needed."); + +#define EC_SPACE_SIZE 256 + +static struct dentry *acpi_ec_debugfs_dir; + +static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, + size_t count, loff_t *off) +{ + /* Use this if support reading/writing multiple ECs exists in ec.c: + * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; + */ + unsigned int size = EC_SPACE_SIZE; + loff_t init_off = *off; + int err = 0; + + if (*off >= size) + return 0; + if (*off + count >= size) { + size -= *off; + count = size; + } else + size = count; + + while (size) { + u8 byte_read; + err = ec_read(*off, &byte_read); + if (err) + return err; + if (put_user(byte_read, buf + *off - init_off)) { + if (*off - init_off) + return *off - init_off; /* partial read */ + return -EFAULT; + } + *off += 1; + size--; + } + return count; +} + +static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + /* Use this if support reading/writing multiple ECs exists in ec.c: + * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; + */ + + unsigned int size = count; + loff_t init_off = *off; + int err = 0; + + if (*off >= EC_SPACE_SIZE) + return 0; + if (*off + count >= EC_SPACE_SIZE) { + size = EC_SPACE_SIZE - *off; + count = size; + } + + while (size) { + u8 byte_write; + if (get_user(byte_write, buf + *off - init_off)) { + if (*off - init_off) + return *off - init_off; /* partial write */ + return -EFAULT; + } + err = ec_write(*off, byte_write); + if (err) + return err; + + *off += 1; + size--; + } + return count; +} + +static const struct file_operations acpi_ec_io_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = acpi_ec_read_io, + .write = acpi_ec_write_io, + .llseek = default_llseek, +}; + +static int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) +{ + struct dentry *dev_dir; + char name[64]; + umode_t mode = 0400; + + if (ec_device_count == 0) { + acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL); + if (!acpi_ec_debugfs_dir) + return -ENOMEM; + } + + sprintf(name, "ec%u", ec_device_count); + dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir); + if (!dev_dir) { + if (ec_device_count != 0) + goto error; + return -ENOMEM; + } + + if (!debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe)) + goto error; + if (!debugfs_create_bool("use_global_lock", 0444, dev_dir, + (u32 *)&first_ec->global_lock)) + goto error; + + if (write_support) + mode = 0600; + if (!debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops)) + goto error; + + return 0; + +error: + debugfs_remove_recursive(acpi_ec_debugfs_dir); + return -ENOMEM; +} + +static int __init acpi_ec_sys_init(void) +{ + int err = 0; + if (first_ec) + err = acpi_ec_add_debugfs(first_ec, 0); + else + err = -ENODEV; + return err; +} + +static void __exit acpi_ec_sys_exit(void) +{ + debugfs_remove_recursive(acpi_ec_debugfs_dir); +} + +module_init(acpi_ec_sys_init); +module_exit(acpi_ec_sys_exit); diff --git a/kernel/drivers/acpi/event.c b/kernel/drivers/acpi/event.c new file mode 100644 index 000000000..e24ea4e79 --- /dev/null +++ b/kernel/drivers/acpi/event.c @@ -0,0 +1,183 @@ +/* + * event.c - exporting ACPI events via procfs + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME("event"); + +/* ACPI notifier chain */ +static BLOCKING_NOTIFIER_HEAD(acpi_chain_head); + +int acpi_notifier_call_chain(struct acpi_device *dev, u32 type, u32 data) +{ + struct acpi_bus_event event; + + strcpy(event.device_class, dev->pnp.device_class); + strcpy(event.bus_id, dev->pnp.bus_id); + event.type = type; + event.data = data; + return (blocking_notifier_call_chain(&acpi_chain_head, 0, (void *)&event) + == NOTIFY_BAD) ? -EINVAL : 0; +} +EXPORT_SYMBOL(acpi_notifier_call_chain); + +int register_acpi_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acpi_chain_head, nb); +} +EXPORT_SYMBOL(register_acpi_notifier); + +int unregister_acpi_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&acpi_chain_head, nb); +} +EXPORT_SYMBOL(unregister_acpi_notifier); + +#ifdef CONFIG_NET +static unsigned int acpi_event_seqnum; +struct acpi_genl_event { + acpi_device_class device_class; + char bus_id[15]; + u32 type; + u32 data; +}; + +/* attributes of acpi_genl_family */ +enum { + ACPI_GENL_ATTR_UNSPEC, + ACPI_GENL_ATTR_EVENT, /* ACPI event info needed by user space */ + __ACPI_GENL_ATTR_MAX, +}; +#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1) + +/* commands supported by the acpi_genl_family */ +enum { + ACPI_GENL_CMD_UNSPEC, + ACPI_GENL_CMD_EVENT, /* kernel->user notifications for ACPI events */ + __ACPI_GENL_CMD_MAX, +}; +#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1) + +#define ACPI_GENL_FAMILY_NAME "acpi_event" +#define ACPI_GENL_VERSION 0x01 +#define ACPI_GENL_MCAST_GROUP_NAME "acpi_mc_group" + +static const struct genl_multicast_group acpi_event_mcgrps[] = { + { .name = ACPI_GENL_MCAST_GROUP_NAME, }, +}; + +static struct genl_family acpi_event_genl_family = { + .id = GENL_ID_GENERATE, + .name = ACPI_GENL_FAMILY_NAME, + .version = ACPI_GENL_VERSION, + .maxattr = ACPI_GENL_ATTR_MAX, + .mcgrps = acpi_event_mcgrps, + .n_mcgrps = ARRAY_SIZE(acpi_event_mcgrps), +}; + +int acpi_bus_generate_netlink_event(const char *device_class, + const char *bus_id, + u8 type, int data) +{ + struct sk_buff *skb; + struct nlattr *attr; + struct acpi_genl_event *event; + void *msg_header; + int size; + + /* allocate memory */ + size = nla_total_size(sizeof(struct acpi_genl_event)) + + nla_total_size(0); + + skb = genlmsg_new(size, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + /* add the genetlink message header */ + msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++, + &acpi_event_genl_family, 0, + ACPI_GENL_CMD_EVENT); + if (!msg_header) { + nlmsg_free(skb); + return -ENOMEM; + } + + /* fill the data */ + attr = + nla_reserve(skb, ACPI_GENL_ATTR_EVENT, + sizeof(struct acpi_genl_event)); + if (!attr) { + nlmsg_free(skb); + return -EINVAL; + } + + event = nla_data(attr); + memset(event, 0, sizeof(struct acpi_genl_event)); + + strcpy(event->device_class, device_class); + strcpy(event->bus_id, bus_id); + event->type = type; + event->data = data; + + /* send multicast genetlink message */ + genlmsg_end(skb, msg_header); + + genlmsg_multicast(&acpi_event_genl_family, skb, 0, 0, GFP_ATOMIC); + return 0; +} + +EXPORT_SYMBOL(acpi_bus_generate_netlink_event); + +static int acpi_event_genetlink_init(void) +{ + return genl_register_family(&acpi_event_genl_family); +} + +#else +int acpi_bus_generate_netlink_event(const char *device_class, + const char *bus_id, + u8 type, int data) +{ + return 0; +} + +EXPORT_SYMBOL(acpi_bus_generate_netlink_event); + +static int acpi_event_genetlink_init(void) +{ + return -ENODEV; +} +#endif + +static int __init acpi_event_init(void) +{ + int error = 0; + + if (acpi_disabled) + return 0; + + /* create genetlink for acpi event */ + error = acpi_event_genetlink_init(); + if (error) + printk(KERN_WARNING PREFIX + "Failed to create genetlink family for ACPI event\n"); + return 0; +} + +fs_initcall(acpi_event_init); diff --git a/kernel/drivers/acpi/fan.c b/kernel/drivers/acpi/fan.c new file mode 100644 index 000000000..7a36f0259 --- /dev/null +++ b/kernel/drivers/acpi/fan.c @@ -0,0 +1,420 @@ +/* + * acpi_fan.c - ACPI Fan Driver ($Revision: 29 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI Fan Driver"); +MODULE_LICENSE("GPL"); + +static int acpi_fan_probe(struct platform_device *pdev); +static int acpi_fan_remove(struct platform_device *pdev); + +static const struct acpi_device_id fan_device_ids[] = { + {"PNP0C0B", 0}, + {"INT3404", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, fan_device_ids); + +#ifdef CONFIG_PM_SLEEP +static int acpi_fan_suspend(struct device *dev); +static int acpi_fan_resume(struct device *dev); +static struct dev_pm_ops acpi_fan_pm = { + .resume = acpi_fan_resume, + .freeze = acpi_fan_suspend, + .thaw = acpi_fan_resume, + .restore = acpi_fan_resume, +}; +#define FAN_PM_OPS_PTR (&acpi_fan_pm) +#else +#define FAN_PM_OPS_PTR NULL +#endif + +struct acpi_fan_fps { + u64 control; + u64 trip_point; + u64 speed; + u64 noise_level; + u64 power; +}; + +struct acpi_fan_fif { + u64 revision; + u64 fine_grain_ctrl; + u64 step_size; + u64 low_speed_notification; +}; + +struct acpi_fan { + bool acpi4; + struct acpi_fan_fif fif; + struct acpi_fan_fps *fps; + int fps_count; + struct thermal_cooling_device *cdev; +}; + +static struct platform_driver acpi_fan_driver = { + .probe = acpi_fan_probe, + .remove = acpi_fan_remove, + .driver = { + .name = "acpi-fan", + .acpi_match_table = fan_device_ids, + .pm = FAN_PM_OPS_PTR, + }, +}; + +/* thermal cooling device callbacks */ +static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long + *state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_fan *fan = acpi_driver_data(device); + + if (fan->acpi4) + *state = fan->fps_count - 1; + else + *state = 1; + return 0; +} + +static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_fan *fan = acpi_driver_data(device); + union acpi_object *obj; + acpi_status status; + int control, i; + + status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, "Get fan state failed\n"); + return status; + } + + obj = buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE || + obj->package.count != 3 || + obj->package.elements[1].type != ACPI_TYPE_INTEGER) { + dev_err(&device->dev, "Invalid _FST data\n"); + status = -EINVAL; + goto err; + } + + control = obj->package.elements[1].integer.value; + for (i = 0; i < fan->fps_count; i++) { + if (control == fan->fps[i].control) + break; + } + if (i == fan->fps_count) { + dev_dbg(&device->dev, "Invalid control value returned\n"); + status = -EINVAL; + goto err; + } + + *state = i; + +err: + kfree(obj); + return status; +} + +static int fan_get_state(struct acpi_device *device, unsigned long *state) +{ + int result; + int acpi_state = ACPI_STATE_D0; + + result = acpi_device_update_power(device, &acpi_state); + if (result) + return result; + + *state = (acpi_state == ACPI_STATE_D3_COLD ? 0 : + (acpi_state == ACPI_STATE_D0 ? 1 : -1)); + return 0; +} + +static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long + *state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_fan *fan = acpi_driver_data(device); + + if (fan->acpi4) + return fan_get_state_acpi4(device, state); + else + return fan_get_state(device, state); +} + +static int fan_set_state(struct acpi_device *device, unsigned long state) +{ + if (state != 0 && state != 1) + return -EINVAL; + + return acpi_device_set_power(device, + state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); +} + +static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) +{ + struct acpi_fan *fan = acpi_driver_data(device); + acpi_status status; + + if (state >= fan->fps_count) + return -EINVAL; + + status = acpi_execute_simple_method(device->handle, "_FSL", + fan->fps[state].control); + if (ACPI_FAILURE(status)) { + dev_dbg(&device->dev, "Failed to set state by _FSL\n"); + return status; + } + + return 0; +} + +static int +fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_fan *fan = acpi_driver_data(device); + + if (fan->acpi4) + return fan_set_state_acpi4(device, state); + else + return fan_set_state(device, state); + } + +static const struct thermal_cooling_device_ops fan_cooling_ops = { + .get_max_state = fan_get_max_state, + .get_cur_state = fan_get_cur_state, + .set_cur_state = fan_set_cur_state, +}; + +/* -------------------------------------------------------------------------- + * Driver Interface + * -------------------------------------------------------------------------- +*/ + +static bool acpi_fan_is_acpi4(struct acpi_device *device) +{ + return acpi_has_method(device->handle, "_FIF") && + acpi_has_method(device->handle, "_FPS") && + acpi_has_method(device->handle, "_FSL") && + acpi_has_method(device->handle, "_FST"); +} + +static int acpi_fan_get_fif(struct acpi_device *device) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_fan *fan = acpi_driver_data(device); + struct acpi_buffer format = { sizeof("NNNN"), "NNNN" }; + struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif }; + union acpi_object *obj; + acpi_status status; + + status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer); + if (ACPI_FAILURE(status)) + return status; + + obj = buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE) { + dev_err(&device->dev, "Invalid _FIF data\n"); + status = -EINVAL; + goto err; + } + + status = acpi_extract_package(obj, &format, &fif); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, "Invalid _FIF element\n"); + status = -EINVAL; + } + +err: + kfree(obj); + return status; +} + +static int acpi_fan_speed_cmp(const void *a, const void *b) +{ + const struct acpi_fan_fps *fps1 = a; + const struct acpi_fan_fps *fps2 = b; + return fps1->speed - fps2->speed; +} + +static int acpi_fan_get_fps(struct acpi_device *device) +{ + struct acpi_fan *fan = acpi_driver_data(device); + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + int i; + + status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer); + if (ACPI_FAILURE(status)) + return status; + + obj = buffer.pointer; + if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) { + dev_err(&device->dev, "Invalid _FPS data\n"); + status = -EINVAL; + goto err; + } + + fan->fps_count = obj->package.count - 1; /* minus revision field */ + fan->fps = devm_kzalloc(&device->dev, + fan->fps_count * sizeof(struct acpi_fan_fps), + GFP_KERNEL); + if (!fan->fps) { + dev_err(&device->dev, "Not enough memory\n"); + status = -ENOMEM; + goto err; + } + for (i = 0; i < fan->fps_count; i++) { + struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; + struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] }; + status = acpi_extract_package(&obj->package.elements[i + 1], + &format, &fps); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, "Invalid _FPS element\n"); + break; + } + } + + /* sort the state array according to fan speed in increase order */ + sort(fan->fps, fan->fps_count, sizeof(*fan->fps), + acpi_fan_speed_cmp, NULL); + +err: + kfree(obj); + return status; +} + +static int acpi_fan_probe(struct platform_device *pdev) +{ + int result = 0; + struct thermal_cooling_device *cdev; + struct acpi_fan *fan; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + char *name; + + fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); + if (!fan) { + dev_err(&device->dev, "No memory for fan\n"); + return -ENOMEM; + } + device->driver_data = fan; + platform_set_drvdata(pdev, fan); + + if (acpi_fan_is_acpi4(device)) { + if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device)) + goto end; + fan->acpi4 = true; + } else { + result = acpi_device_update_power(device, NULL); + if (result) { + dev_err(&device->dev, "Setting initial power state\n"); + goto end; + } + } + + if (!strncmp(pdev->name, "PNP0C0B", strlen("PNP0C0B"))) + name = "Fan"; + else + name = acpi_device_bid(device); + + cdev = thermal_cooling_device_register(name, device, + &fan_cooling_ops); + if (IS_ERR(cdev)) { + result = PTR_ERR(cdev); + goto end; + } + + dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id); + + fan->cdev = cdev; + result = sysfs_create_link(&pdev->dev.kobj, + &cdev->device.kobj, + "thermal_cooling"); + if (result) + dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n"); + + result = sysfs_create_link(&cdev->device.kobj, + &pdev->dev.kobj, + "device"); + if (result) + dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n"); + +end: + return result; +} + +static int acpi_fan_remove(struct platform_device *pdev) +{ + struct acpi_fan *fan = platform_get_drvdata(pdev); + + sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&fan->cdev->device.kobj, "device"); + thermal_cooling_device_unregister(fan->cdev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int acpi_fan_suspend(struct device *dev) +{ + struct acpi_fan *fan = dev_get_drvdata(dev); + if (fan->acpi4) + return 0; + + acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0); + + return AE_OK; +} + +static int acpi_fan_resume(struct device *dev) +{ + int result; + struct acpi_fan *fan = dev_get_drvdata(dev); + + if (fan->acpi4) + return 0; + + result = acpi_device_update_power(ACPI_COMPANION(dev), NULL); + if (result) + dev_err(dev, "Error updating fan power state\n"); + + return result; +} +#endif + +module_platform_driver(acpi_fan_driver); diff --git a/kernel/drivers/acpi/glue.c b/kernel/drivers/acpi/glue.c new file mode 100644 index 000000000..39c485b0c --- /dev/null +++ b/kernel/drivers/acpi/glue.c @@ -0,0 +1,358 @@ +/* + * Link physical devices with ACPI devices support + * + * Copyright (c) 2005 David Shaohua Li + * Copyright (c) 2005 Intel Corp. + * + * This file is released under the GPLv2. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define ACPI_GLUE_DEBUG 0 +#if ACPI_GLUE_DEBUG +#define DBG(fmt, ...) \ + printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__) +#else +#define DBG(fmt, ...) \ +do { \ + if (0) \ + printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__); \ +} while (0) +#endif +static LIST_HEAD(bus_type_list); +static DECLARE_RWSEM(bus_type_sem); + +#define PHYSICAL_NODE_STRING "physical_node" +#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10) + +int register_acpi_bus_type(struct acpi_bus_type *type) +{ + if (acpi_disabled) + return -ENODEV; + if (type && type->match && type->find_companion) { + down_write(&bus_type_sem); + list_add_tail(&type->list, &bus_type_list); + up_write(&bus_type_sem); + printk(KERN_INFO PREFIX "bus type %s registered\n", type->name); + return 0; + } + return -ENODEV; +} +EXPORT_SYMBOL_GPL(register_acpi_bus_type); + +int unregister_acpi_bus_type(struct acpi_bus_type *type) +{ + if (acpi_disabled) + return 0; + if (type) { + down_write(&bus_type_sem); + list_del_init(&type->list); + up_write(&bus_type_sem); + printk(KERN_INFO PREFIX "bus type %s unregistered\n", + type->name); + return 0; + } + return -ENODEV; +} +EXPORT_SYMBOL_GPL(unregister_acpi_bus_type); + +static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) +{ + struct acpi_bus_type *tmp, *ret = NULL; + + down_read(&bus_type_sem); + list_for_each_entry(tmp, &bus_type_list, list) { + if (tmp->match(dev)) { + ret = tmp; + break; + } + } + up_read(&bus_type_sem); + return ret; +} + +#define FIND_CHILD_MIN_SCORE 1 +#define FIND_CHILD_MAX_SCORE 2 + +static int find_child_checks(struct acpi_device *adev, bool check_children) +{ + bool sta_present = true; + unsigned long long sta; + acpi_status status; + + status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); + if (status == AE_NOT_FOUND) + sta_present = false; + else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) + return -ENODEV; + + if (check_children && list_empty(&adev->children)) + return -ENODEV; + + return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; +} + +struct acpi_device *acpi_find_child_device(struct acpi_device *parent, + u64 address, bool check_children) +{ + struct acpi_device *adev, *ret = NULL; + int ret_score = 0; + + if (!parent) + return NULL; + + list_for_each_entry(adev, &parent->children, node) { + unsigned long long addr; + acpi_status status; + int score; + + status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR, + NULL, &addr); + if (ACPI_FAILURE(status) || addr != address) + continue; + + if (!ret) { + /* This is the first matching object. Save it. */ + ret = adev; + continue; + } + /* + * There is more than one matching device object with the same + * _ADR value. That really is unexpected, so we are kind of + * beyond the scope of the spec here. We have to choose which + * one to return, though. + * + * First, check if the previously found object is good enough + * and return it if so. Second, do the same for the object that + * we've just found. + */ + if (!ret_score) { + ret_score = find_child_checks(ret, check_children); + if (ret_score == FIND_CHILD_MAX_SCORE) + return ret; + } + score = find_child_checks(adev, check_children); + if (score == FIND_CHILD_MAX_SCORE) { + return adev; + } else if (score > ret_score) { + ret = adev; + ret_score = score; + } + } + return ret; +} +EXPORT_SYMBOL_GPL(acpi_find_child_device); + +static void acpi_physnode_link_name(char *buf, unsigned int node_id) +{ + if (node_id > 0) + snprintf(buf, PHYSICAL_NODE_NAME_SIZE, + PHYSICAL_NODE_STRING "%u", node_id); + else + strcpy(buf, PHYSICAL_NODE_STRING); +} + +int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) +{ + struct acpi_device_physical_node *physical_node, *pn; + char physical_node_name[PHYSICAL_NODE_NAME_SIZE]; + struct list_head *physnode_list; + unsigned int node_id; + int retval = -EINVAL; + + if (has_acpi_companion(dev)) { + if (acpi_dev) { + dev_warn(dev, "ACPI companion already set\n"); + return -EINVAL; + } else { + acpi_dev = ACPI_COMPANION(dev); + } + } + if (!acpi_dev) + return -EINVAL; + + get_device(&acpi_dev->dev); + get_device(dev); + physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL); + if (!physical_node) { + retval = -ENOMEM; + goto err; + } + + mutex_lock(&acpi_dev->physical_node_lock); + + /* + * Keep the list sorted by node_id so that the IDs of removed nodes can + * be recycled easily. + */ + physnode_list = &acpi_dev->physical_node_list; + node_id = 0; + list_for_each_entry(pn, &acpi_dev->physical_node_list, node) { + /* Sanity check. */ + if (pn->dev == dev) { + mutex_unlock(&acpi_dev->physical_node_lock); + + dev_warn(dev, "Already associated with ACPI node\n"); + kfree(physical_node); + if (ACPI_COMPANION(dev) != acpi_dev) + goto err; + + put_device(dev); + put_device(&acpi_dev->dev); + return 0; + } + if (pn->node_id == node_id) { + physnode_list = &pn->node; + node_id++; + } + } + + physical_node->node_id = node_id; + physical_node->dev = dev; + list_add(&physical_node->node, physnode_list); + acpi_dev->physical_node_count++; + + if (!has_acpi_companion(dev)) + ACPI_COMPANION_SET(dev, acpi_dev); + + acpi_physnode_link_name(physical_node_name, node_id); + retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, + physical_node_name); + if (retval) + dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n", + physical_node_name, retval); + + retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, + "firmware_node"); + if (retval) + dev_err(dev, "Failed to create link firmware_node (%d)\n", + retval); + + mutex_unlock(&acpi_dev->physical_node_lock); + + if (acpi_dev->wakeup.flags.valid) + device_set_wakeup_capable(dev, true); + + return 0; + + err: + ACPI_COMPANION_SET(dev, NULL); + put_device(dev); + put_device(&acpi_dev->dev); + return retval; +} +EXPORT_SYMBOL_GPL(acpi_bind_one); + +int acpi_unbind_one(struct device *dev) +{ + struct acpi_device *acpi_dev = ACPI_COMPANION(dev); + struct acpi_device_physical_node *entry; + + if (!acpi_dev) + return 0; + + mutex_lock(&acpi_dev->physical_node_lock); + + list_for_each_entry(entry, &acpi_dev->physical_node_list, node) + if (entry->dev == dev) { + char physnode_name[PHYSICAL_NODE_NAME_SIZE]; + + list_del(&entry->node); + acpi_dev->physical_node_count--; + + acpi_physnode_link_name(physnode_name, entry->node_id); + sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name); + sysfs_remove_link(&dev->kobj, "firmware_node"); + ACPI_COMPANION_SET(dev, NULL); + /* Drop references taken by acpi_bind_one(). */ + put_device(dev); + put_device(&acpi_dev->dev); + kfree(entry); + break; + } + + mutex_unlock(&acpi_dev->physical_node_lock); + return 0; +} +EXPORT_SYMBOL_GPL(acpi_unbind_one); + +static int acpi_platform_notify(struct device *dev) +{ + struct acpi_bus_type *type = acpi_get_bus_type(dev); + struct acpi_device *adev; + int ret; + + ret = acpi_bind_one(dev, NULL); + if (ret && type) { + struct acpi_device *adev; + + adev = type->find_companion(dev); + if (!adev) { + DBG("Unable to get handle for %s\n", dev_name(dev)); + ret = -ENODEV; + goto out; + } + ret = acpi_bind_one(dev, adev); + if (ret) + goto out; + } + adev = ACPI_COMPANION(dev); + if (!adev) + goto out; + + if (type && type->setup) + type->setup(dev); + else if (adev->handler && adev->handler->bind) + adev->handler->bind(dev); + + out: +#if ACPI_GLUE_DEBUG + if (!ret) { + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer); + DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer); + kfree(buffer.pointer); + } else + DBG("Device %s -> No ACPI support\n", dev_name(dev)); +#endif + + return ret; +} + +static int acpi_platform_notify_remove(struct device *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_bus_type *type; + + if (!adev) + return 0; + + type = acpi_get_bus_type(dev); + if (type && type->cleanup) + type->cleanup(dev); + else if (adev->handler && adev->handler->unbind) + adev->handler->unbind(dev); + + acpi_unbind_one(dev); + return 0; +} + +int __init init_acpi_device_notify(void) +{ + if (platform_notify || platform_notify_remove) { + printk(KERN_ERR PREFIX "Can't use platform_notify\n"); + return 0; + } + platform_notify = acpi_platform_notify; + platform_notify_remove = acpi_platform_notify_remove; + return 0; +} diff --git a/kernel/drivers/acpi/gsi.c b/kernel/drivers/acpi/gsi.c new file mode 100644 index 000000000..38208f2d0 --- /dev/null +++ b/kernel/drivers/acpi/gsi.c @@ -0,0 +1,105 @@ +/* + * ACPI GSI IRQ layer + * + * Copyright (C) 2015 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +enum acpi_irq_model_id acpi_irq_model; + +static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) +{ + switch (polarity) { + case ACPI_ACTIVE_LOW: + return trigger == ACPI_EDGE_SENSITIVE ? + IRQ_TYPE_EDGE_FALLING : + IRQ_TYPE_LEVEL_LOW; + case ACPI_ACTIVE_HIGH: + return trigger == ACPI_EDGE_SENSITIVE ? + IRQ_TYPE_EDGE_RISING : + IRQ_TYPE_LEVEL_HIGH; + case ACPI_ACTIVE_BOTH: + if (trigger == ACPI_EDGE_SENSITIVE) + return IRQ_TYPE_EDGE_BOTH; + default: + return IRQ_TYPE_NONE; + } +} + +/** + * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI + * @gsi: GSI IRQ number to map + * @irq: pointer where linux IRQ number is stored + * + * irq location updated with irq value [>0 on success, 0 on failure] + * + * Returns: linux IRQ number on success (>0) + * -EINVAL on failure + */ +int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) +{ + /* + * Only default domain is supported at present, always find + * the mapping corresponding to default domain by passing NULL + * as irq_domain parameter + */ + *irq = irq_find_mapping(NULL, gsi); + /* + * *irq == 0 means no mapping, that should + * be reported as a failure + */ + return (*irq > 0) ? *irq : -EINVAL; +} +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); + +/** + * acpi_register_gsi() - Map a GSI to a linux IRQ number + * @dev: device for which IRQ has to be mapped + * @gsi: GSI IRQ number + * @trigger: trigger type of the GSI number to be mapped + * @polarity: polarity of the GSI to be mapped + * + * Returns: a valid linux IRQ number on success + * -EINVAL on failure + */ +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, + int polarity) +{ + unsigned int irq; + unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity); + + /* + * There is no way at present to look-up the IRQ domain on ACPI, + * hence always create mapping referring to the default domain + * by passing NULL as irq_domain parameter + */ + irq = irq_create_mapping(NULL, gsi); + if (!irq) + return -EINVAL; + + /* Set irq type if specified and different than the current one */ + if (irq_type != IRQ_TYPE_NONE && + irq_type != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_type); + return irq; +} +EXPORT_SYMBOL_GPL(acpi_register_gsi); + +/** + * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping + * @gsi: GSI IRQ number + */ +void acpi_unregister_gsi(u32 gsi) +{ + int irq = irq_find_mapping(NULL, gsi); + + irq_dispose_mapping(irq); +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); diff --git a/kernel/drivers/acpi/hed.c b/kernel/drivers/acpi/hed.c new file mode 100644 index 000000000..aafe3ca82 --- /dev/null +++ b/kernel/drivers/acpi/hed.c @@ -0,0 +1,92 @@ +/* + * ACPI Hardware Error Device (PNP0C33) Driver + * + * Copyright (C) 2010, Intel Corp. + * Author: Huang Ying + * + * ACPI Hardware Error Device is used to report some hardware errors + * notified via SCI, mainly the corrected errors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +static struct acpi_device_id acpi_hed_ids[] = { + {"PNP0C33", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, acpi_hed_ids); + +static acpi_handle hed_handle; + +static BLOCKING_NOTIFIER_HEAD(acpi_hed_notify_list); + +int register_acpi_hed_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acpi_hed_notify_list, nb); +} +EXPORT_SYMBOL_GPL(register_acpi_hed_notifier); + +void unregister_acpi_hed_notifier(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&acpi_hed_notify_list, nb); +} +EXPORT_SYMBOL_GPL(unregister_acpi_hed_notifier); + +/* + * SCI to report hardware error is forwarded to the listeners of HED, + * it is used by HEST Generic Hardware Error Source with notify type + * SCI. + */ +static void acpi_hed_notify(struct acpi_device *device, u32 event) +{ + blocking_notifier_call_chain(&acpi_hed_notify_list, 0, NULL); +} + +static int acpi_hed_add(struct acpi_device *device) +{ + /* Only one hardware error device */ + if (hed_handle) + return -EINVAL; + hed_handle = device->handle; + return 0; +} + +static int acpi_hed_remove(struct acpi_device *device) +{ + hed_handle = NULL; + return 0; +} + +static struct acpi_driver acpi_hed_driver = { + .name = "hardware_error_device", + .class = "hardware_error", + .ids = acpi_hed_ids, + .ops = { + .add = acpi_hed_add, + .remove = acpi_hed_remove, + .notify = acpi_hed_notify, + }, +}; +module_acpi_driver(acpi_hed_driver); + +ACPI_MODULE_NAME("hed"); +MODULE_AUTHOR("Huang Ying"); +MODULE_DESCRIPTION("ACPI Hardware Error Device Driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/int340x_thermal.c b/kernel/drivers/acpi/int340x_thermal.c new file mode 100644 index 000000000..9dcf83682 --- /dev/null +++ b/kernel/drivers/acpi/int340x_thermal.c @@ -0,0 +1,54 @@ +/* + * ACPI support for int340x thermal drivers + * + * Copyright (C) 2014, Intel Corporation + * Authors: Zhang Rui + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "internal.h" + +#define INT3401_DEVICE 0X01 +static const struct acpi_device_id int340x_thermal_device_ids[] = { + {"INT3400"}, + {"INT3401", INT3401_DEVICE}, + {"INT3402"}, + {"INT3403"}, + {"INT3404"}, + {"INT3406"}, + {"INT3407"}, + {"INT3408"}, + {"INT3409"}, + {"INT340A"}, + {"INT340B"}, + {""}, +}; + +static int int340x_thermal_handler_attach(struct acpi_device *adev, + const struct acpi_device_id *id) +{ +#if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE) + acpi_create_platform_device(adev); +#elif defined(INTEL_SOC_DTS_THERMAL) || defined(INTEL_SOC_DTS_THERMAL_MODULE) + /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */ + if (id->driver_data == INT3401_DEVICE) + acpi_create_platform_device(adev); +#endif + return 1; +} + +static struct acpi_scan_handler int340x_thermal_handler = { + .ids = int340x_thermal_device_ids, + .attach = int340x_thermal_handler_attach, +}; + +void __init acpi_int340x_thermal_init(void) +{ + acpi_scan_add_handler(&int340x_thermal_handler); +} diff --git a/kernel/drivers/acpi/internal.h b/kernel/drivers/acpi/internal.h new file mode 100644 index 000000000..ba4a61e96 --- /dev/null +++ b/kernel/drivers/acpi/internal.h @@ -0,0 +1,197 @@ +/* + * acpi/internal.h + * For use by Linux/ACPI infrastructure, not drivers + * + * Copyright (c) 2009, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ACPI_INTERNAL_H_ +#define _ACPI_INTERNAL_H_ + +#define PREFIX "ACPI: " + +acpi_status acpi_os_initialize1(void); +int init_acpi_device_notify(void); +int acpi_scan_init(void); +void acpi_pci_root_init(void); +void acpi_pci_link_init(void); +void acpi_processor_init(void); +void acpi_platform_init(void); +void acpi_pnp_init(void); +void acpi_int340x_thermal_init(void); +int acpi_sysfs_init(void); +void acpi_container_init(void); +void acpi_memory_hotplug_init(void); +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int acpi_ioapic_add(struct acpi_pci_root *root); +int acpi_ioapic_remove(struct acpi_pci_root *root); +#else +static inline int acpi_ioapic_add(struct acpi_pci_root *root) { return 0; } +static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; } +#endif +#ifdef CONFIG_ACPI_DOCK +void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle); +int dock_notify(struct acpi_device *adev, u32 event); +void acpi_dock_add(struct acpi_device *adev); +#else +static inline void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle) {} +static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; } +static inline void acpi_dock_add(struct acpi_device *adev) {} +#endif +#ifdef CONFIG_X86 +void acpi_cmos_rtc_init(void); +#else +static inline void acpi_cmos_rtc_init(void) {} +#endif + +extern bool acpi_force_hot_remove; + +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name); +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name); +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val); + +#ifdef CONFIG_DEBUG_FS +extern struct dentry *acpi_debugfs_dir; +int acpi_debugfs_init(void); +#else +static inline void acpi_debugfs_init(void) { return; } +#endif +void acpi_lpss_init(void); + +void acpi_apd_init(void); + +acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); +bool acpi_queue_hotplug_work(struct work_struct *work); +void acpi_device_hotplug(struct acpi_device *adev, u32 src); +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); + +/* -------------------------------------------------------------------------- + Device Node Initialization / Removal + -------------------------------------------------------------------------- */ +#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \ + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING) + +int acpi_device_add(struct acpi_device *device, + void (*release)(struct device *)); +void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, + int type, unsigned long long sta); +void acpi_device_add_finalize(struct acpi_device *device); +void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); +bool acpi_device_is_present(struct acpi_device *adev); +bool acpi_device_is_battery(struct acpi_device *adev); + +/* -------------------------------------------------------------------------- + Power Resource + -------------------------------------------------------------------------- */ +int acpi_power_init(void); +void acpi_power_resources_list_free(struct list_head *list); +int acpi_extract_power_resources(union acpi_object *package, unsigned int start, + struct list_head *list); +int acpi_add_power_resource(acpi_handle handle); +void acpi_power_add_remove_device(struct acpi_device *adev, bool add); +int acpi_power_wakeup_list_init(struct list_head *list, int *system_level); +int acpi_device_sleep_wake(struct acpi_device *dev, + int enable, int sleep_state, int dev_state); +int acpi_power_get_inferred_state(struct acpi_device *device, int *state); +int acpi_power_on_resources(struct acpi_device *device, int state); +int acpi_power_transition(struct acpi_device *device, int state); + +int acpi_wakeup_device_init(void); + +#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC +void acpi_early_processor_set_pdc(void); +#else +static inline void acpi_early_processor_set_pdc(void) {} +#endif + +/* -------------------------------------------------------------------------- + Embedded Controller + -------------------------------------------------------------------------- */ +struct acpi_ec { + acpi_handle handle; + unsigned long gpe; + unsigned long command_addr; + unsigned long data_addr; + unsigned long global_lock; + unsigned long flags; + unsigned long reference_count; + struct mutex mutex; + wait_queue_head_t wait; + struct list_head list; + struct transaction *curr; + spinlock_t lock; + struct work_struct work; +}; + +extern struct acpi_ec *first_ec; + +/* If we find an EC via the ECDT, we need to keep a ptr to its context */ +/* External interfaces use first EC only, so remember */ +typedef int (*acpi_ec_query_func) (void *data); + +int acpi_ec_init(void); +int acpi_ec_ecdt_probe(void); +int acpi_boot_ec_enable(void); +void acpi_ec_block_transactions(void); +void acpi_ec_unblock_transactions(void); +void acpi_ec_unblock_transactions_early(void); +int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, + acpi_handle handle, acpi_ec_query_func func, + void *data); +void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); + + +/*-------------------------------------------------------------------------- + Suspend/Resume + -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT +extern int acpi_sleep_init(void); +#else +static inline int acpi_sleep_init(void) { return -ENXIO; } +#endif + +#ifdef CONFIG_ACPI_SLEEP +int acpi_sleep_proc_init(void); +int suspend_nvs_alloc(void); +void suspend_nvs_free(void); +int suspend_nvs_save(void); +void suspend_nvs_restore(void); +#else +static inline int acpi_sleep_proc_init(void) { return 0; } +static inline int suspend_nvs_alloc(void) { return 0; } +static inline void suspend_nvs_free(void) {} +static inline int suspend_nvs_save(void) { return 0; } +static inline void suspend_nvs_restore(void) {} +#endif + +/*-------------------------------------------------------------------------- + Video + -------------------------------------------------------------------------- */ +#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) +bool acpi_osi_is_win8(void); +#endif + +/*-------------------------------------------------------------------------- + Device properties + -------------------------------------------------------------------------- */ +void acpi_init_properties(struct acpi_device *adev); +void acpi_free_properties(struct acpi_device *adev); + +#endif /* _ACPI_INTERNAL_H_ */ diff --git a/kernel/drivers/acpi/ioapic.c b/kernel/drivers/acpi/ioapic.c new file mode 100644 index 000000000..ccdc8db16 --- /dev/null +++ b/kernel/drivers/acpi/ioapic.c @@ -0,0 +1,229 @@ +/* + * IOAPIC/IOxAPIC/IOSAPIC driver + * + * Copyright (C) 2009 Fujitsu Limited. + * (c) Copyright 2009 Hewlett-Packard Development Company, L.P. + * + * Copyright (C) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on original drivers/pci/ioapic.c + * Yinghai Lu + * Jiang Liu + */ + +/* + * This driver manages I/O APICs added by hotplug after boot. + * We try to claim all I/O APIC devices, but those present at boot were + * registered when we parsed the ACPI MADT. + */ + +#define pr_fmt(fmt) "ACPI : IOAPIC: " fmt + +#include +#include +#include +#include + +struct acpi_pci_ioapic { + acpi_handle root_handle; + acpi_handle handle; + u32 gsi_base; + struct resource res; + struct pci_dev *pdev; + struct list_head list; +}; + +static LIST_HEAD(ioapic_list); +static DEFINE_MUTEX(ioapic_list_lock); + +static acpi_status setup_res(struct acpi_resource *acpi_res, void *data) +{ + struct resource *res = data; + struct resource_win win; + + res->flags = 0; + if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM) == 0) + return AE_OK; + + if (!acpi_dev_resource_memory(acpi_res, res)) { + if (acpi_dev_resource_address_space(acpi_res, &win) || + acpi_dev_resource_ext_address_space(acpi_res, &win)) + *res = win.res; + } + if ((res->flags & IORESOURCE_PREFETCH) || + (res->flags & IORESOURCE_DISABLED)) + res->flags = 0; + + return AE_CTRL_TERMINATE; +} + +static bool acpi_is_ioapic(acpi_handle handle, char **type) +{ + acpi_status status; + struct acpi_device_info *info; + char *hid = NULL; + bool match = false; + + if (!acpi_has_method(handle, "_GSB")) + return false; + + status = acpi_get_object_info(handle, &info); + if (ACPI_SUCCESS(status)) { + if (info->valid & ACPI_VALID_HID) + hid = info->hardware_id.string; + if (hid) { + if (strcmp(hid, "ACPI0009") == 0) { + *type = "IOxAPIC"; + match = true; + } else if (strcmp(hid, "ACPI000A") == 0) { + *type = "IOAPIC"; + match = true; + } + } + kfree(info); + } + + return match; +} + +static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + acpi_status status; + unsigned long long gsi_base; + struct acpi_pci_ioapic *ioapic; + struct pci_dev *dev = NULL; + struct resource *res = NULL; + char *type = NULL; + + if (!acpi_is_ioapic(handle, &type)) + return AE_OK; + + mutex_lock(&ioapic_list_lock); + list_for_each_entry(ioapic, &ioapic_list, list) + if (ioapic->handle == handle) { + mutex_unlock(&ioapic_list_lock); + return AE_OK; + } + + status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, "failed to evaluate _GSB method\n"); + goto exit; + } + + ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL); + if (!ioapic) { + pr_err("cannot allocate memory for new IOAPIC\n"); + goto exit; + } else { + ioapic->root_handle = (acpi_handle)context; + ioapic->handle = handle; + ioapic->gsi_base = (u32)gsi_base; + INIT_LIST_HEAD(&ioapic->list); + } + + if (acpi_ioapic_registered(handle, (u32)gsi_base)) + goto done; + + dev = acpi_get_pci_dev(handle); + if (dev && pci_resource_len(dev, 0)) { + if (pci_enable_device(dev) < 0) + goto exit_put; + pci_set_master(dev); + if (pci_request_region(dev, 0, type)) + goto exit_disable; + res = &dev->resource[0]; + ioapic->pdev = dev; + } else { + pci_dev_put(dev); + dev = NULL; + + res = &ioapic->res; + acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, res); + if (res->flags == 0) { + acpi_handle_warn(handle, "failed to get resource\n"); + goto exit_free; + } else if (request_resource(&iomem_resource, res)) { + acpi_handle_warn(handle, "failed to insert resource\n"); + goto exit_free; + } + } + + if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) { + acpi_handle_warn(handle, "failed to register IOAPIC\n"); + goto exit_release; + } +done: + list_add(&ioapic->list, &ioapic_list); + mutex_unlock(&ioapic_list_lock); + + if (dev) + dev_info(&dev->dev, "%s at %pR, GSI %u\n", + type, res, (u32)gsi_base); + else + acpi_handle_info(handle, "%s at %pR, GSI %u\n", + type, res, (u32)gsi_base); + + return AE_OK; + +exit_release: + if (dev) + pci_release_region(dev, 0); + else + release_resource(res); +exit_disable: + if (dev) + pci_disable_device(dev); +exit_put: + pci_dev_put(dev); +exit_free: + kfree(ioapic); +exit: + mutex_unlock(&ioapic_list_lock); + *(acpi_status *)rv = AE_ERROR; + return AE_OK; +} + +int acpi_ioapic_add(struct acpi_pci_root *root) +{ + acpi_status status, retval = AE_OK; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle, + UINT_MAX, handle_ioapic_add, NULL, + root->device->handle, (void **)&retval); + + return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV; +} + +int acpi_ioapic_remove(struct acpi_pci_root *root) +{ + int retval = 0; + struct acpi_pci_ioapic *ioapic, *tmp; + + mutex_lock(&ioapic_list_lock); + list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) { + if (root->device->handle != ioapic->root_handle) + continue; + + if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base)) + retval = -EBUSY; + + if (ioapic->pdev) { + pci_release_region(ioapic->pdev, 0); + pci_disable_device(ioapic->pdev); + pci_dev_put(ioapic->pdev); + } else if (ioapic->res.flags && ioapic->res.parent) { + release_resource(&ioapic->res); + } + list_del(&ioapic->list); + kfree(ioapic); + } + mutex_unlock(&ioapic_list_lock); + + return retval; +} diff --git a/kernel/drivers/acpi/numa.c b/kernel/drivers/acpi/numa.c new file mode 100644 index 000000000..1333cbdc3 --- /dev/null +++ b/kernel/drivers/acpi/numa.c @@ -0,0 +1,336 @@ +/* + * acpi_numa.c - ACPI NUMA support + * + * Copyright (C) 2002 Takayoshi Kochi + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +#define ACPI_NUMA 0x80000000 +#define _COMPONENT ACPI_NUMA +ACPI_MODULE_NAME("numa"); + +static nodemask_t nodes_found_map = NODE_MASK_NONE; + +/* maps to convert between proximity domain and logical node ID */ +static int pxm_to_node_map[MAX_PXM_DOMAINS] + = { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE }; +static int node_to_pxm_map[MAX_NUMNODES] + = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; + +unsigned char acpi_srat_revision __initdata; + +int pxm_to_node(int pxm) +{ + if (pxm < 0) + return NUMA_NO_NODE; + return pxm_to_node_map[pxm]; +} + +int node_to_pxm(int node) +{ + if (node < 0) + return PXM_INVAL; + return node_to_pxm_map[node]; +} + +static void __acpi_map_pxm_to_node(int pxm, int node) +{ + if (pxm_to_node_map[pxm] == NUMA_NO_NODE || node < pxm_to_node_map[pxm]) + pxm_to_node_map[pxm] = node; + if (node_to_pxm_map[node] == PXM_INVAL || pxm < node_to_pxm_map[node]) + node_to_pxm_map[node] = pxm; +} + +int acpi_map_pxm_to_node(int pxm) +{ + int node = pxm_to_node_map[pxm]; + + if (node == NUMA_NO_NODE) { + if (nodes_weight(nodes_found_map) >= MAX_NUMNODES) + return NUMA_NO_NODE; + node = first_unset_node(nodes_found_map); + __acpi_map_pxm_to_node(pxm, node); + node_set(node, nodes_found_map); + } + + return node; +} + +static void __init +acpi_table_print_srat_entry(struct acpi_subtable_header *header) +{ + + ACPI_FUNCTION_NAME("acpi_table_print_srat_entry"); + + if (!header) + return; + + switch (header->type) { + + case ACPI_SRAT_TYPE_CPU_AFFINITY: +#ifdef ACPI_DEBUG_OUTPUT + { + struct acpi_srat_cpu_affinity *p = + (struct acpi_srat_cpu_affinity *)header; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n", + p->apic_id, p->local_sapic_eid, + p->proximity_domain_lo, + (p->flags & ACPI_SRAT_CPU_ENABLED)? + "enabled" : "disabled")); + } +#endif /* ACPI_DEBUG_OUTPUT */ + break; + + case ACPI_SRAT_TYPE_MEMORY_AFFINITY: +#ifdef ACPI_DEBUG_OUTPUT + { + struct acpi_srat_mem_affinity *p = + (struct acpi_srat_mem_affinity *)header; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n", + (unsigned long)p->base_address, + (unsigned long)p->length, + p->proximity_domain, + (p->flags & ACPI_SRAT_MEM_ENABLED)? + "enabled" : "disabled", + (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)? + " hot-pluggable" : "", + (p->flags & ACPI_SRAT_MEM_NON_VOLATILE)? + " non-volatile" : "")); + } +#endif /* ACPI_DEBUG_OUTPUT */ + break; + + case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY: +#ifdef ACPI_DEBUG_OUTPUT + { + struct acpi_srat_x2apic_cpu_affinity *p = + (struct acpi_srat_x2apic_cpu_affinity *)header; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "SRAT Processor (x2apicid[0x%08x]) in" + " proximity domain %d %s\n", + p->apic_id, + p->proximity_domain, + (p->flags & ACPI_SRAT_CPU_ENABLED) ? + "enabled" : "disabled")); + } +#endif /* ACPI_DEBUG_OUTPUT */ + break; + default: + printk(KERN_WARNING PREFIX + "Found unsupported SRAT entry (type = 0x%x)\n", + header->type); + break; + } +} + +/* + * A lot of BIOS fill in 10 (= no distance) everywhere. This messes + * up the NUMA heuristics which wants the local node to have a smaller + * distance than the others. + * Do some quick checks here and only use the SLIT if it passes. + */ +static int __init slit_valid(struct acpi_table_slit *slit) +{ + int i, j; + int d = slit->locality_count; + for (i = 0; i < d; i++) { + for (j = 0; j < d; j++) { + u8 val = slit->entry[d*i + j]; + if (i == j) { + if (val != LOCAL_DISTANCE) + return 0; + } else if (val <= LOCAL_DISTANCE) + return 0; + } + } + return 1; +} + +static int __init acpi_parse_slit(struct acpi_table_header *table) +{ + struct acpi_table_slit *slit = (struct acpi_table_slit *)table; + + if (!slit_valid(slit)) { + printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n"); + return -EINVAL; + } + acpi_numa_slit_init(slit); + + return 0; +} + +void __init __weak +acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) +{ + printk(KERN_WARNING PREFIX + "Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id); + return; +} + + +static int __init +acpi_parse_x2apic_affinity(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_srat_x2apic_cpu_affinity *processor_affinity; + + processor_affinity = (struct acpi_srat_x2apic_cpu_affinity *)header; + if (!processor_affinity) + return -EINVAL; + + acpi_table_print_srat_entry(header); + + /* let architecture-dependent part to do it */ + acpi_numa_x2apic_affinity_init(processor_affinity); + + return 0; +} + +static int __init +acpi_parse_processor_affinity(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_srat_cpu_affinity *processor_affinity; + + processor_affinity = (struct acpi_srat_cpu_affinity *)header; + if (!processor_affinity) + return -EINVAL; + + acpi_table_print_srat_entry(header); + + /* let architecture-dependent part to do it */ + acpi_numa_processor_affinity_init(processor_affinity); + + return 0; +} + +static int __initdata parsed_numa_memblks; + +static int __init +acpi_parse_memory_affinity(struct acpi_subtable_header * header, + const unsigned long end) +{ + struct acpi_srat_mem_affinity *memory_affinity; + + memory_affinity = (struct acpi_srat_mem_affinity *)header; + if (!memory_affinity) + return -EINVAL; + + acpi_table_print_srat_entry(header); + + /* let architecture-dependent part to do it */ + if (!acpi_numa_memory_affinity_init(memory_affinity)) + parsed_numa_memblks++; + return 0; +} + +static int __init acpi_parse_srat(struct acpi_table_header *table) +{ + struct acpi_table_srat *srat = (struct acpi_table_srat *)table; + + acpi_srat_revision = srat->header.revision; + + /* Real work done in acpi_table_parse_srat below. */ + + return 0; +} + +static int __init +acpi_table_parse_srat(enum acpi_srat_type id, + acpi_tbl_entry_handler handler, unsigned int max_entries) +{ + return acpi_table_parse_entries(ACPI_SIG_SRAT, + sizeof(struct acpi_table_srat), id, + handler, max_entries); +} + +int __init acpi_numa_init(void) +{ + int cnt = 0; + + /* + * Should not limit number with cpu num that is from NR_CPUS or nr_cpus= + * SRAT cpu entries could have different order with that in MADT. + * So go over all cpu entries in SRAT to get apicid to node mapping. + */ + + /* SRAT: Static Resource Affinity Table */ + if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { + acpi_table_parse_srat(ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY, + acpi_parse_x2apic_affinity, 0); + acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY, + acpi_parse_processor_affinity, 0); + cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY, + acpi_parse_memory_affinity, + NR_NODE_MEMBLKS); + } + + /* SLIT: System Locality Information Table */ + acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); + + acpi_numa_arch_fixup(); + + if (cnt < 0) + return cnt; + else if (!parsed_numa_memblks) + return -ENOENT; + return 0; +} + +static int acpi_get_pxm(acpi_handle h) +{ + unsigned long long pxm; + acpi_status status; + acpi_handle handle; + acpi_handle phandle = h; + + do { + handle = phandle; + status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm); + if (ACPI_SUCCESS(status)) + return pxm; + status = acpi_get_parent(handle, &phandle); + } while (ACPI_SUCCESS(status)); + return -1; +} + +int acpi_get_node(acpi_handle handle) +{ + int pxm; + + pxm = acpi_get_pxm(handle); + if (pxm < 0 || pxm >= MAX_PXM_DOMAINS) + return NUMA_NO_NODE; + + return acpi_map_pxm_to_node(pxm); +} +EXPORT_SYMBOL(acpi_get_node); diff --git a/kernel/drivers/acpi/nvs.c b/kernel/drivers/acpi/nvs.c new file mode 100644 index 000000000..85287b8fe --- /dev/null +++ b/kernel/drivers/acpi/nvs.c @@ -0,0 +1,212 @@ +/* + * nvs.c - Routines for saving and restoring ACPI NVS memory region + * + * Copyright (C) 2008-2011 Rafael J. Wysocki , Novell Inc. + * + * This file is released under the GPLv2. + */ + +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +/* ACPI NVS regions, APEI may use it */ + +struct nvs_region { + __u64 phys_start; + __u64 size; + struct list_head node; +}; + +static LIST_HEAD(nvs_region_list); + +#ifdef CONFIG_ACPI_SLEEP +static int suspend_nvs_register(unsigned long start, unsigned long size); +#else +static inline int suspend_nvs_register(unsigned long a, unsigned long b) +{ + return 0; +} +#endif + +int acpi_nvs_register(__u64 start, __u64 size) +{ + struct nvs_region *region; + + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->phys_start = start; + region->size = size; + list_add_tail(®ion->node, &nvs_region_list); + + return suspend_nvs_register(start, size); +} + +int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data), + void *data) +{ + int rc; + struct nvs_region *region; + + list_for_each_entry(region, &nvs_region_list, node) { + rc = func(region->phys_start, region->size, data); + if (rc) + return rc; + } + + return 0; +} + + +#ifdef CONFIG_ACPI_SLEEP +/* + * Platforms, like ACPI, may want us to save some memory used by them during + * suspend and to restore the contents of this memory during the subsequent + * resume. The code below implements a mechanism allowing us to do that. + */ + +struct nvs_page { + unsigned long phys_start; + unsigned int size; + void *kaddr; + void *data; + bool unmap; + struct list_head node; +}; + +static LIST_HEAD(nvs_list); + +/** + * suspend_nvs_register - register platform NVS memory region to save + * @start - physical address of the region + * @size - size of the region + * + * The NVS region need not be page-aligned (both ends) and we arrange + * things so that the data from page-aligned addresses in this region will + * be copied into separate RAM pages. + */ +static int suspend_nvs_register(unsigned long start, unsigned long size) +{ + struct nvs_page *entry, *next; + + pr_info("PM: Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n", + start, start + size - 1, size); + + while (size > 0) { + unsigned int nr_bytes; + + entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL); + if (!entry) + goto Error; + + list_add_tail(&entry->node, &nvs_list); + entry->phys_start = start; + nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK); + entry->size = (size < nr_bytes) ? size : nr_bytes; + + start += entry->size; + size -= entry->size; + } + return 0; + + Error: + list_for_each_entry_safe(entry, next, &nvs_list, node) { + list_del(&entry->node); + kfree(entry); + } + return -ENOMEM; +} + +/** + * suspend_nvs_free - free data pages allocated for saving NVS regions + */ +void suspend_nvs_free(void) +{ + struct nvs_page *entry; + + list_for_each_entry(entry, &nvs_list, node) + if (entry->data) { + free_page((unsigned long)entry->data); + entry->data = NULL; + if (entry->kaddr) { + if (entry->unmap) { + iounmap(entry->kaddr); + entry->unmap = false; + } else { + acpi_os_unmap_iomem(entry->kaddr, + entry->size); + } + entry->kaddr = NULL; + } + } +} + +/** + * suspend_nvs_alloc - allocate memory necessary for saving NVS regions + */ +int suspend_nvs_alloc(void) +{ + struct nvs_page *entry; + + list_for_each_entry(entry, &nvs_list, node) { + entry->data = (void *)__get_free_page(GFP_KERNEL); + if (!entry->data) { + suspend_nvs_free(); + return -ENOMEM; + } + } + return 0; +} + +/** + * suspend_nvs_save - save NVS memory regions + */ +int suspend_nvs_save(void) +{ + struct nvs_page *entry; + + printk(KERN_INFO "PM: Saving platform NVS memory\n"); + + list_for_each_entry(entry, &nvs_list, node) + if (entry->data) { + unsigned long phys = entry->phys_start; + unsigned int size = entry->size; + + entry->kaddr = acpi_os_get_iomem(phys, size); + if (!entry->kaddr) { + entry->kaddr = acpi_os_ioremap(phys, size); + entry->unmap = !!entry->kaddr; + } + if (!entry->kaddr) { + suspend_nvs_free(); + return -ENOMEM; + } + memcpy(entry->data, entry->kaddr, entry->size); + } + + return 0; +} + +/** + * suspend_nvs_restore - restore NVS memory regions + * + * This function is going to be called with interrupts disabled, so it + * cannot iounmap the virtual addresses used to access the NVS region. + */ +void suspend_nvs_restore(void) +{ + struct nvs_page *entry; + + printk(KERN_INFO "PM: Restoring platform NVS memory\n"); + + list_for_each_entry(entry, &nvs_list, node) + if (entry->data) + memcpy(entry->kaddr, entry->data, entry->size); +} +#endif diff --git a/kernel/drivers/acpi/osl.c b/kernel/drivers/acpi/osl.c new file mode 100644 index 000000000..5226a8b92 --- /dev/null +++ b/kernel/drivers/acpi/osl.c @@ -0,0 +1,1914 @@ +/* + * acpi_osl.c - OS-dependent functions ($Revision: 83 $) + * + * Copyright (C) 2000 Andrew Henroid + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (c) 2008 Intel Corporation + * Author: Matthew Wilcox + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_OS_SERVICES +ACPI_MODULE_NAME("osl"); + +struct acpi_os_dpc { + acpi_osd_exec_callback function; + void *context; + struct work_struct work; +}; + +#ifdef CONFIG_ACPI_CUSTOM_DSDT +#include CONFIG_ACPI_CUSTOM_DSDT_FILE +#endif + +#ifdef ENABLE_DEBUGGER +#include + +/* stuff for debugger support */ +int acpi_in_debugger; +EXPORT_SYMBOL(acpi_in_debugger); + +extern char line_buf[80]; +#endif /*ENABLE_DEBUGGER */ + +static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl, + u32 pm1b_ctrl); +static int (*__acpi_os_prepare_extended_sleep)(u8 sleep_state, u32 val_a, + u32 val_b); + +static acpi_osd_handler acpi_irq_handler; +static void *acpi_irq_context; +static struct workqueue_struct *kacpid_wq; +static struct workqueue_struct *kacpi_notify_wq; +static struct workqueue_struct *kacpi_hotplug_wq; + +/* + * This list of permanent mappings is for memory that may be accessed from + * interrupt context, where we can't do the ioremap(). + */ +struct acpi_ioremap { + struct list_head list; + void __iomem *virt; + acpi_physical_address phys; + acpi_size size; + unsigned long refcount; +}; + +static LIST_HEAD(acpi_ioremaps); +static DEFINE_MUTEX(acpi_ioremap_lock); + +static void __init acpi_osi_setup_late(void); + +/* + * The story of _OSI(Linux) + * + * From pre-history through Linux-2.6.22, + * Linux responded TRUE upon a BIOS OSI(Linux) query. + * + * Unfortunately, reference BIOS writers got wind of this + * and put OSI(Linux) in their example code, quickly exposing + * this string as ill-conceived and opening the door to + * an un-bounded number of BIOS incompatibilities. + * + * For example, OSI(Linux) was used on resume to re-POST a + * video card on one system, because Linux at that time + * could not do a speedy restore in its native driver. + * But then upon gaining quick native restore capability, + * Linux has no way to tell the BIOS to skip the time-consuming + * POST -- putting Linux at a permanent performance disadvantage. + * On another system, the BIOS writer used OSI(Linux) + * to infer native OS support for IPMI! On other systems, + * OSI(Linux) simply got in the way of Linux claiming to + * be compatible with other operating systems, exposing + * BIOS issues such as skipped device initialization. + * + * So "Linux" turned out to be a really poor chose of + * OSI string, and from Linux-2.6.23 onward we respond FALSE. + * + * BIOS writers should NOT query _OSI(Linux) on future systems. + * Linux will complain on the console when it sees it, and return FALSE. + * To get Linux to return TRUE for your system will require + * a kernel source update to add a DMI entry, + * or boot with "acpi_osi=Linux" + */ + +static struct osi_linux { + unsigned int enable:1; + unsigned int dmi:1; + unsigned int cmdline:1; + unsigned int default_disabling:1; +} osi_linux = {0, 0, 0, 0}; + +static u32 acpi_osi_handler(acpi_string interface, u32 supported) +{ + if (!strcmp("Linux", interface)) { + + printk_once(KERN_NOTICE FW_BUG PREFIX + "BIOS _OSI(Linux) query %s%s\n", + osi_linux.enable ? "honored" : "ignored", + osi_linux.cmdline ? " via cmdline" : + osi_linux.dmi ? " via DMI" : ""); + } + + if (!strcmp("Darwin", interface)) { + /* + * Apple firmware will behave poorly if it receives positive + * answers to "Darwin" and any other OS. Respond positively + * to Darwin and then disable all other vendor strings. + */ + acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS); + supported = ACPI_UINT32_MAX; + } + + return supported; +} + +static void __init acpi_request_region (struct acpi_generic_address *gas, + unsigned int length, char *desc) +{ + u64 addr; + + /* Handle possible alignment issues */ + memcpy(&addr, &gas->address, sizeof(addr)); + if (!addr || !length) + return; + + acpi_reserve_region(addr, length, gas->space_id, 0, desc); +} + +static void __init acpi_reserve_resources(void) +{ + acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length, + "ACPI PM1a_EVT_BLK"); + + acpi_request_region(&acpi_gbl_FADT.xpm1b_event_block, acpi_gbl_FADT.pm1_event_length, + "ACPI PM1b_EVT_BLK"); + + acpi_request_region(&acpi_gbl_FADT.xpm1a_control_block, acpi_gbl_FADT.pm1_control_length, + "ACPI PM1a_CNT_BLK"); + + acpi_request_region(&acpi_gbl_FADT.xpm1b_control_block, acpi_gbl_FADT.pm1_control_length, + "ACPI PM1b_CNT_BLK"); + + if (acpi_gbl_FADT.pm_timer_length == 4) + acpi_request_region(&acpi_gbl_FADT.xpm_timer_block, 4, "ACPI PM_TMR"); + + acpi_request_region(&acpi_gbl_FADT.xpm2_control_block, acpi_gbl_FADT.pm2_control_length, + "ACPI PM2_CNT_BLK"); + + /* Length of GPE blocks must be a non-negative multiple of 2 */ + + if (!(acpi_gbl_FADT.gpe0_block_length & 0x1)) + acpi_request_region(&acpi_gbl_FADT.xgpe0_block, + acpi_gbl_FADT.gpe0_block_length, "ACPI GPE0_BLK"); + + if (!(acpi_gbl_FADT.gpe1_block_length & 0x1)) + acpi_request_region(&acpi_gbl_FADT.xgpe1_block, + acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK"); +} + +void acpi_os_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + acpi_os_vprintf(fmt, args); + va_end(args); +} + +void acpi_os_vprintf(const char *fmt, va_list args) +{ + static char buffer[512]; + + vsprintf(buffer, fmt, args); + +#ifdef ENABLE_DEBUGGER + if (acpi_in_debugger) { + kdb_printf("%s", buffer); + } else { + printk(KERN_CONT "%s", buffer); + } +#else + printk(KERN_CONT "%s", buffer); +#endif +} + +#ifdef CONFIG_KEXEC +static unsigned long acpi_rsdp; +static int __init setup_acpi_rsdp(char *arg) +{ + if (kstrtoul(arg, 16, &acpi_rsdp)) + return -EINVAL; + return 0; +} +early_param("acpi_rsdp", setup_acpi_rsdp); +#endif + +acpi_physical_address __init acpi_os_get_root_pointer(void) +{ +#ifdef CONFIG_KEXEC + if (acpi_rsdp) + return acpi_rsdp; +#endif + + if (efi_enabled(EFI_CONFIG_TABLES)) { + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) + return efi.acpi20; + else if (efi.acpi != EFI_INVALID_TABLE_ADDR) + return efi.acpi; + else { + printk(KERN_ERR PREFIX + "System description tables not found\n"); + return 0; + } + } else if (IS_ENABLED(CONFIG_ACPI_LEGACY_TABLES_LOOKUP)) { + acpi_physical_address pa = 0; + + acpi_find_root_pointer(&pa); + return pa; + } + + return 0; +} + +/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ +static struct acpi_ioremap * +acpi_map_lookup(acpi_physical_address phys, acpi_size size) +{ + struct acpi_ioremap *map; + + list_for_each_entry_rcu(map, &acpi_ioremaps, list) + if (map->phys <= phys && + phys + size <= map->phys + map->size) + return map; + + return NULL; +} + +/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ +static void __iomem * +acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) +{ + struct acpi_ioremap *map; + + map = acpi_map_lookup(phys, size); + if (map) + return map->virt + (phys - map->phys); + + return NULL; +} + +void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size) +{ + struct acpi_ioremap *map; + void __iomem *virt = NULL; + + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup(phys, size); + if (map) { + virt = map->virt + (phys - map->phys); + map->refcount++; + } + mutex_unlock(&acpi_ioremap_lock); + return virt; +} +EXPORT_SYMBOL_GPL(acpi_os_get_iomem); + +/* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ +static struct acpi_ioremap * +acpi_map_lookup_virt(void __iomem *virt, acpi_size size) +{ + struct acpi_ioremap *map; + + list_for_each_entry_rcu(map, &acpi_ioremaps, list) + if (map->virt <= virt && + virt + size <= map->virt + map->size) + return map; + + return NULL; +} + +#if defined(CONFIG_IA64) || defined(CONFIG_ARM64) +/* ioremap will take care of cache attributes */ +#define should_use_kmap(pfn) 0 +#else +#define should_use_kmap(pfn) page_is_ram(pfn) +#endif + +static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (should_use_kmap(pfn)) { + if (pg_sz > PAGE_SIZE) + return NULL; + return (void __iomem __force *)kmap(pfn_to_page(pfn)); + } else + return acpi_os_ioremap(pg_off, pg_sz); +} + +static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr) +{ + unsigned long pfn; + + pfn = pg_off >> PAGE_SHIFT; + if (should_use_kmap(pfn)) + kunmap(pfn_to_page(pfn)); + else + iounmap(vaddr); +} + +void __iomem *__init_refok +acpi_os_map_iomem(acpi_physical_address phys, acpi_size size) +{ + struct acpi_ioremap *map; + void __iomem *virt; + acpi_physical_address pg_off; + acpi_size pg_sz; + + if (phys > ULONG_MAX) { + printk(KERN_ERR PREFIX "Cannot map memory that high\n"); + return NULL; + } + + if (!acpi_gbl_permanent_mmap) + return __acpi_map_table((unsigned long)phys, size); + + mutex_lock(&acpi_ioremap_lock); + /* Check if there's a suitable mapping already. */ + map = acpi_map_lookup(phys, size); + if (map) { + map->refcount++; + goto out; + } + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) { + mutex_unlock(&acpi_ioremap_lock); + return NULL; + } + + pg_off = round_down(phys, PAGE_SIZE); + pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; + virt = acpi_map(pg_off, pg_sz); + if (!virt) { + mutex_unlock(&acpi_ioremap_lock); + kfree(map); + return NULL; + } + + INIT_LIST_HEAD(&map->list); + map->virt = virt; + map->phys = pg_off; + map->size = pg_sz; + map->refcount = 1; + + list_add_tail_rcu(&map->list, &acpi_ioremaps); + +out: + mutex_unlock(&acpi_ioremap_lock); + return map->virt + (phys - map->phys); +} +EXPORT_SYMBOL_GPL(acpi_os_map_iomem); + +void *__init_refok +acpi_os_map_memory(acpi_physical_address phys, acpi_size size) +{ + return (void *)acpi_os_map_iomem(phys, size); +} +EXPORT_SYMBOL_GPL(acpi_os_map_memory); + +static void acpi_os_drop_map_ref(struct acpi_ioremap *map) +{ + if (!--map->refcount) + list_del_rcu(&map->list); +} + +static void acpi_os_map_cleanup(struct acpi_ioremap *map) +{ + if (!map->refcount) { + synchronize_rcu_expedited(); + acpi_unmap(map->phys, map->virt); + kfree(map); + } +} + +void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size) +{ + struct acpi_ioremap *map; + + if (!acpi_gbl_permanent_mmap) { + __acpi_unmap_table(virt, size); + return; + } + + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup_virt(virt, size); + if (!map) { + mutex_unlock(&acpi_ioremap_lock); + WARN(true, PREFIX "%s: bad address %p\n", __func__, virt); + return; + } + acpi_os_drop_map_ref(map); + mutex_unlock(&acpi_ioremap_lock); + + acpi_os_map_cleanup(map); +} +EXPORT_SYMBOL_GPL(acpi_os_unmap_iomem); + +void __ref acpi_os_unmap_memory(void *virt, acpi_size size) +{ + return acpi_os_unmap_iomem((void __iomem *)virt, size); +} +EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); + +void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) +{ + if (!acpi_gbl_permanent_mmap) + __acpi_unmap_table(virt, size); +} + +int acpi_os_map_generic_address(struct acpi_generic_address *gas) +{ + u64 addr; + void __iomem *virt; + + if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + return 0; + + /* Handle possible alignment issues */ + memcpy(&addr, &gas->address, sizeof(addr)); + if (!addr || !gas->bit_width) + return -EINVAL; + + virt = acpi_os_map_iomem(addr, gas->bit_width / 8); + if (!virt) + return -EIO; + + return 0; +} +EXPORT_SYMBOL(acpi_os_map_generic_address); + +void acpi_os_unmap_generic_address(struct acpi_generic_address *gas) +{ + u64 addr; + struct acpi_ioremap *map; + + if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + return; + + /* Handle possible alignment issues */ + memcpy(&addr, &gas->address, sizeof(addr)); + if (!addr || !gas->bit_width) + return; + + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup(addr, gas->bit_width / 8); + if (!map) { + mutex_unlock(&acpi_ioremap_lock); + return; + } + acpi_os_drop_map_ref(map); + mutex_unlock(&acpi_ioremap_lock); + + acpi_os_map_cleanup(map); +} +EXPORT_SYMBOL(acpi_os_unmap_generic_address); + +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_os_get_physical_address(void *virt, acpi_physical_address * phys) +{ + if (!phys || !virt) + return AE_BAD_PARAMETER; + + *phys = virt_to_phys(virt); + + return AE_OK; +} +#endif + +#define ACPI_MAX_OVERRIDE_LEN 100 + +static char acpi_os_name[ACPI_MAX_OVERRIDE_LEN]; + +acpi_status +acpi_os_predefined_override(const struct acpi_predefined_names *init_val, + acpi_string * new_val) +{ + if (!init_val || !new_val) + return AE_BAD_PARAMETER; + + *new_val = NULL; + if (!memcmp(init_val->name, "_OS_", 4) && strlen(acpi_os_name)) { + printk(KERN_INFO PREFIX "Overriding _OS definition to '%s'\n", + acpi_os_name); + *new_val = acpi_os_name; + } + + return AE_OK; +} + +#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE +#include +#include + +static u64 acpi_tables_addr; +static int all_tables_size; + +/* Copied from acpica/tbutils.c:acpi_tb_checksum() */ +static u8 __init acpi_table_checksum(u8 *buffer, u32 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) + sum = (u8) (sum + *(buffer++)); + return sum; +} + +/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */ +static const char * const table_sigs[] = { + ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ, + ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT, + ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF, + ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET, + ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI, + ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA, + ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT, + ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT, + ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL }; + +#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) + +#define ACPI_OVERRIDE_TABLES 64 +static struct cpio_data __initdata acpi_initrd_files[ACPI_OVERRIDE_TABLES]; + +#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT) + +void __init acpi_initrd_override(void *data, size_t size) +{ + int sig, no, table_nr = 0, total_offset = 0; + long offset = 0; + struct acpi_table_header *table; + char cpio_path[32] = "kernel/firmware/acpi/"; + struct cpio_data file; + + if (data == NULL || size == 0) + return; + + for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) { + file = find_cpio_data(cpio_path, data, size, &offset); + if (!file.data) + break; + + data += offset; + size -= offset; + + if (file.size < sizeof(struct acpi_table_header)) { + pr_err("ACPI OVERRIDE: Table smaller than ACPI header [%s%s]\n", + cpio_path, file.name); + continue; + } + + table = file.data; + + for (sig = 0; table_sigs[sig]; sig++) + if (!memcmp(table->signature, table_sigs[sig], 4)) + break; + + if (!table_sigs[sig]) { + pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n", + cpio_path, file.name); + continue; + } + if (file.size != table->length) { + pr_err("ACPI OVERRIDE: File length does not match table length [%s%s]\n", + cpio_path, file.name); + continue; + } + if (acpi_table_checksum(file.data, table->length)) { + pr_err("ACPI OVERRIDE: Bad table checksum [%s%s]\n", + cpio_path, file.name); + continue; + } + + pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n", + table->signature, cpio_path, file.name, table->length); + + all_tables_size += table->length; + acpi_initrd_files[table_nr].data = file.data; + acpi_initrd_files[table_nr].size = file.size; + table_nr++; + } + if (table_nr == 0) + return; + + acpi_tables_addr = + memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT, + all_tables_size, PAGE_SIZE); + if (!acpi_tables_addr) { + WARN_ON(1); + return; + } + /* + * Only calling e820_add_reserve does not work and the + * tables are invalid (memory got used) later. + * memblock_reserve works as expected and the tables won't get modified. + * But it's not enough on X86 because ioremap will + * complain later (used by acpi_os_map_memory) that the pages + * that should get mapped are not marked "reserved". + * Both memblock_reserve and e820_add_region (via arch_reserve_mem_area) + * works fine. + */ + memblock_reserve(acpi_tables_addr, all_tables_size); + arch_reserve_mem_area(acpi_tables_addr, all_tables_size); + + /* + * early_ioremap only can remap 256k one time. If we map all + * tables one time, we will hit the limit. Need to map chunks + * one by one during copying the same as that in relocate_initrd(). + */ + for (no = 0; no < table_nr; no++) { + unsigned char *src_p = acpi_initrd_files[no].data; + phys_addr_t size = acpi_initrd_files[no].size; + phys_addr_t dest_addr = acpi_tables_addr + total_offset; + phys_addr_t slop, clen; + char *dest_p; + + total_offset += size; + + while (size) { + slop = dest_addr & ~PAGE_MASK; + clen = size; + if (clen > MAP_CHUNK_SIZE - slop) + clen = MAP_CHUNK_SIZE - slop; + dest_p = early_ioremap(dest_addr & PAGE_MASK, + clen + slop); + memcpy(dest_p + slop, src_p, clen); + early_iounmap(dest_p, clen + slop); + src_p += clen; + dest_addr += clen; + size -= clen; + } + } +} +#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */ + +static void acpi_table_taint(struct acpi_table_header *table) +{ + pr_warn(PREFIX + "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n", + table->signature, table->oem_table_id); + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); +} + + +acpi_status +acpi_os_table_override(struct acpi_table_header * existing_table, + struct acpi_table_header ** new_table) +{ + if (!existing_table || !new_table) + return AE_BAD_PARAMETER; + + *new_table = NULL; + +#ifdef CONFIG_ACPI_CUSTOM_DSDT + if (strncmp(existing_table->signature, "DSDT", 4) == 0) + *new_table = (struct acpi_table_header *)AmlCode; +#endif + if (*new_table != NULL) + acpi_table_taint(existing_table); + return AE_OK; +} + +acpi_status +acpi_os_physical_table_override(struct acpi_table_header *existing_table, + acpi_physical_address *address, + u32 *table_length) +{ +#ifndef CONFIG_ACPI_INITRD_TABLE_OVERRIDE + *table_length = 0; + *address = 0; + return AE_OK; +#else + int table_offset = 0; + struct acpi_table_header *table; + + *table_length = 0; + *address = 0; + + if (!acpi_tables_addr) + return AE_OK; + + do { + if (table_offset + ACPI_HEADER_SIZE > all_tables_size) { + WARN_ON(1); + return AE_OK; + } + + table = acpi_os_map_memory(acpi_tables_addr + table_offset, + ACPI_HEADER_SIZE); + + if (table_offset + table->length > all_tables_size) { + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + WARN_ON(1); + return AE_OK; + } + + table_offset += table->length; + + if (memcmp(existing_table->signature, table->signature, 4)) { + acpi_os_unmap_memory(table, + ACPI_HEADER_SIZE); + continue; + } + + /* Only override tables with matching oem id */ + if (memcmp(table->oem_table_id, existing_table->oem_table_id, + ACPI_OEM_TABLE_ID_SIZE)) { + acpi_os_unmap_memory(table, + ACPI_HEADER_SIZE); + continue; + } + + table_offset -= table->length; + *table_length = table->length; + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + *address = acpi_tables_addr + table_offset; + break; + } while (table_offset + ACPI_HEADER_SIZE < all_tables_size); + + if (*address != 0) + acpi_table_taint(existing_table); + return AE_OK; +#endif +} + +static irqreturn_t acpi_irq(int irq, void *dev_id) +{ + u32 handled; + + handled = (*acpi_irq_handler) (acpi_irq_context); + + if (handled) { + acpi_irq_handled++; + return IRQ_HANDLED; + } else { + acpi_irq_not_handled++; + return IRQ_NONE; + } +} + +acpi_status +acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, + void *context) +{ + unsigned int irq; + + acpi_irq_stats_init(); + + /* + * ACPI interrupts different from the SCI in our copy of the FADT are + * not supported. + */ + if (gsi != acpi_gbl_FADT.sci_interrupt) + return AE_BAD_PARAMETER; + + if (acpi_irq_handler) + return AE_ALREADY_ACQUIRED; + + if (acpi_gsi_to_irq(gsi, &irq) < 0) { + printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n", + gsi); + return AE_OK; + } + + acpi_irq_handler = handler; + acpi_irq_context = context; + if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { + printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); + acpi_irq_handler = NULL; + return AE_NOT_ACQUIRED; + } + + return AE_OK; +} + +acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) +{ + if (irq != acpi_gbl_FADT.sci_interrupt) + return AE_BAD_PARAMETER; + + free_irq(irq, acpi_irq); + acpi_irq_handler = NULL; + + return AE_OK; +} + +/* + * Running in interpreter thread context, safe to sleep + */ + +void acpi_os_sleep(u64 ms) +{ + msleep(ms); +} + +void acpi_os_stall(u32 us) +{ + while (us) { + u32 delay = 1000; + + if (delay > us) + delay = us; + udelay(delay); + touch_nmi_watchdog(); + us -= delay; + } +} + +/* + * Support ACPI 3.0 AML Timer operand + * Returns 64-bit free-running, monotonically increasing timer + * with 100ns granularity + */ +u64 acpi_os_get_timer(void) +{ + u64 time_ns = ktime_to_ns(ktime_get()); + do_div(time_ns, 100); + return time_ns; +} + +acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width) +{ + u32 dummy; + + if (!value) + value = &dummy; + + *value = 0; + if (width <= 8) { + *(u8 *) value = inb(port); + } else if (width <= 16) { + *(u16 *) value = inw(port); + } else if (width <= 32) { + *(u32 *) value = inl(port); + } else { + BUG(); + } + + return AE_OK; +} + +EXPORT_SYMBOL(acpi_os_read_port); + +acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width) +{ + if (width <= 8) { + outb(value, port); + } else if (width <= 16) { + outw(value, port); + } else if (width <= 32) { + outl(value, port); + } else { + BUG(); + } + + return AE_OK; +} + +EXPORT_SYMBOL(acpi_os_write_port); + +#ifdef readq +static inline u64 read64(const volatile void __iomem *addr) +{ + return readq(addr); +} +#else +static inline u64 read64(const volatile void __iomem *addr) +{ + u64 l, h; + l = readl(addr); + h = readl(addr+4); + return l | (h << 32); +} +#endif + +acpi_status +acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) +{ + void __iomem *virt_addr; + unsigned int size = width / 8; + bool unmap = false; + u64 dummy; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + if (!virt_addr) { + rcu_read_unlock(); + virt_addr = acpi_os_ioremap(phys_addr, size); + if (!virt_addr) + return AE_BAD_ADDRESS; + unmap = true; + } + + if (!value) + value = &dummy; + + switch (width) { + case 8: + *(u8 *) value = readb(virt_addr); + break; + case 16: + *(u16 *) value = readw(virt_addr); + break; + case 32: + *(u32 *) value = readl(virt_addr); + break; + case 64: + *(u64 *) value = read64(virt_addr); + break; + default: + BUG(); + } + + if (unmap) + iounmap(virt_addr); + else + rcu_read_unlock(); + + return AE_OK; +} + +#ifdef writeq +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writeq(val, addr); +} +#else +static inline void write64(u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val>>32, addr+4); +} +#endif + +acpi_status +acpi_os_write_memory(acpi_physical_address phys_addr, u64 value, u32 width) +{ + void __iomem *virt_addr; + unsigned int size = width / 8; + bool unmap = false; + + rcu_read_lock(); + virt_addr = acpi_map_vaddr_lookup(phys_addr, size); + if (!virt_addr) { + rcu_read_unlock(); + virt_addr = acpi_os_ioremap(phys_addr, size); + if (!virt_addr) + return AE_BAD_ADDRESS; + unmap = true; + } + + switch (width) { + case 8: + writeb(value, virt_addr); + break; + case 16: + writew(value, virt_addr); + break; + case 32: + writel(value, virt_addr); + break; + case 64: + write64(value, virt_addr); + break; + default: + BUG(); + } + + if (unmap) + iounmap(virt_addr); + else + rcu_read_unlock(); + + return AE_OK; +} + +acpi_status +acpi_os_read_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, + u64 *value, u32 width) +{ + int result, size; + u32 value32; + + if (!value) + return AE_BAD_PARAMETER; + + switch (width) { + case 8: + size = 1; + break; + case 16: + size = 2; + break; + case 32: + size = 4; + break; + default: + return AE_ERROR; + } + + result = raw_pci_read(pci_id->segment, pci_id->bus, + PCI_DEVFN(pci_id->device, pci_id->function), + reg, size, &value32); + *value = value32; + + return (result ? AE_ERROR : AE_OK); +} + +acpi_status +acpi_os_write_pci_configuration(struct acpi_pci_id * pci_id, u32 reg, + u64 value, u32 width) +{ + int result, size; + + switch (width) { + case 8: + size = 1; + break; + case 16: + size = 2; + break; + case 32: + size = 4; + break; + default: + return AE_ERROR; + } + + result = raw_pci_write(pci_id->segment, pci_id->bus, + PCI_DEVFN(pci_id->device, pci_id->function), + reg, size, value); + + return (result ? AE_ERROR : AE_OK); +} + +static void acpi_os_execute_deferred(struct work_struct *work) +{ + struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); + + dpc->function(dpc->context); + kfree(dpc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_execute + * + * PARAMETERS: Type - Type of the callback + * Function - Function to be executed + * Context - Function parameters + * + * RETURN: Status + * + * DESCRIPTION: Depending on type, either queues function for deferred execution or + * immediately executes function on a separate thread. + * + ******************************************************************************/ + +acpi_status acpi_os_execute(acpi_execute_type type, + acpi_osd_exec_callback function, void *context) +{ + acpi_status status = AE_OK; + struct acpi_os_dpc *dpc; + struct workqueue_struct *queue; + int ret; + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Scheduling function [%p(%p)] for deferred execution.\n", + function, context)); + + /* + * Allocate/initialize DPC structure. Note that this memory will be + * freed by the callee. The kernel handles the work_struct list in a + * way that allows us to also free its memory inside the callee. + * Because we may want to schedule several tasks with different + * parameters we can't use the approach some kernel code uses of + * having a static work_struct. + */ + + dpc = kzalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC); + if (!dpc) + return AE_NO_MEMORY; + + dpc->function = function; + dpc->context = context; + + /* + * To prevent lockdep from complaining unnecessarily, make sure that + * there is a different static lockdep key for each workqueue by using + * INIT_WORK() for each of them separately. + */ + if (type == OSL_NOTIFY_HANDLER) { + queue = kacpi_notify_wq; + INIT_WORK(&dpc->work, acpi_os_execute_deferred); + } else { + queue = kacpid_wq; + INIT_WORK(&dpc->work, acpi_os_execute_deferred); + } + + /* + * On some machines, a software-initiated SMI causes corruption unless + * the SMI runs on CPU 0. An SMI can be initiated by any AML, but + * typically it's done in GPE-related methods that are run via + * workqueues, so we can avoid the known corruption cases by always + * queueing on CPU 0. + */ + ret = queue_work_on(0, queue, &dpc->work); + + if (!ret) { + printk(KERN_ERR PREFIX + "Call to queue_work() failed.\n"); + status = AE_ERROR; + kfree(dpc); + } + return status; +} +EXPORT_SYMBOL(acpi_os_execute); + +void acpi_os_wait_events_complete(void) +{ + /* + * Make sure the GPE handler or the fixed event handler is not used + * on another CPU after removal. + */ + if (acpi_irq_handler) + synchronize_hardirq(acpi_gbl_FADT.sci_interrupt); + flush_workqueue(kacpid_wq); + flush_workqueue(kacpi_notify_wq); +} + +struct acpi_hp_work { + struct work_struct work; + struct acpi_device *adev; + u32 src; +}; + +static void acpi_hotplug_work_fn(struct work_struct *work) +{ + struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work); + + acpi_os_wait_events_complete(); + acpi_device_hotplug(hpw->adev, hpw->src); + kfree(hpw); +} + +acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src) +{ + struct acpi_hp_work *hpw; + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Scheduling hotplug event (%p, %u) for deferred execution.\n", + adev, src)); + + hpw = kmalloc(sizeof(*hpw), GFP_KERNEL); + if (!hpw) + return AE_NO_MEMORY; + + INIT_WORK(&hpw->work, acpi_hotplug_work_fn); + hpw->adev = adev; + hpw->src = src; + /* + * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because + * the hotplug code may call driver .remove() functions, which may + * invoke flush_scheduled_work()/acpi_os_wait_events_complete() to flush + * these workqueues. + */ + if (!queue_work(kacpi_hotplug_wq, &hpw->work)) { + kfree(hpw); + return AE_ERROR; + } + return AE_OK; +} + +bool acpi_queue_hotplug_work(struct work_struct *work) +{ + return queue_work(kacpi_hotplug_wq, work); +} + +acpi_status +acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle) +{ + struct semaphore *sem = NULL; + + sem = acpi_os_allocate_zeroed(sizeof(struct semaphore)); + if (!sem) + return AE_NO_MEMORY; + + sema_init(sem, initial_units); + + *handle = (acpi_handle *) sem; + + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Creating semaphore[%p|%d].\n", + *handle, initial_units)); + + return AE_OK; +} + +/* + * TODO: A better way to delete semaphores? Linux doesn't have a + * 'delete_semaphore()' function -- may result in an invalid + * pointer dereference for non-synchronized consumers. Should + * we at least check for blocked threads and signal/cancel them? + */ + +acpi_status acpi_os_delete_semaphore(acpi_handle handle) +{ + struct semaphore *sem = (struct semaphore *)handle; + + if (!sem) + return AE_BAD_PARAMETER; + + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Deleting semaphore[%p].\n", handle)); + + BUG_ON(!list_empty(&sem->wait_list)); + kfree(sem); + sem = NULL; + + return AE_OK; +} + +/* + * TODO: Support for units > 1? + */ +acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout) +{ + acpi_status status = AE_OK; + struct semaphore *sem = (struct semaphore *)handle; + long jiffies; + int ret = 0; + + if (!sem || (units < 1)) + return AE_BAD_PARAMETER; + + if (units > 1) + return AE_SUPPORT; + + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Waiting for semaphore[%p|%d|%d]\n", + handle, units, timeout)); + + if (timeout == ACPI_WAIT_FOREVER) + jiffies = MAX_SCHEDULE_TIMEOUT; + else + jiffies = msecs_to_jiffies(timeout); + + ret = down_timeout(sem, jiffies); + if (ret) + status = AE_TIME; + + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, + "Failed to acquire semaphore[%p|%d|%d], %s", + handle, units, timeout, + acpi_format_exception(status))); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, + "Acquired semaphore[%p|%d|%d]", handle, + units, timeout)); + } + + return status; +} + +/* + * TODO: Support for units > 1? + */ +acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units) +{ + struct semaphore *sem = (struct semaphore *)handle; + + if (!sem || (units < 1)) + return AE_BAD_PARAMETER; + + if (units > 1) + return AE_SUPPORT; + + ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Signaling semaphore[%p|%d]\n", handle, + units)); + + up(sem); + + return AE_OK; +} + +#ifdef ACPI_FUTURE_USAGE +u32 acpi_os_get_line(char *buffer) +{ + +#ifdef ENABLE_DEBUGGER + if (acpi_in_debugger) { + u32 chars; + + kdb_read(buffer, sizeof(line_buf)); + + /* remove the CR kdb includes */ + chars = strlen(buffer) - 1; + buffer[chars] = '\0'; + } +#endif + + return 0; +} +#endif /* ACPI_FUTURE_USAGE */ + +acpi_status acpi_os_signal(u32 function, void *info) +{ + switch (function) { + case ACPI_SIGNAL_FATAL: + printk(KERN_ERR PREFIX "Fatal opcode executed\n"); + break; + case ACPI_SIGNAL_BREAKPOINT: + /* + * AML Breakpoint + * ACPI spec. says to treat it as a NOP unless + * you are debugging. So if/when we integrate + * AML debugger into the kernel debugger its + * hook will go here. But until then it is + * not useful to print anything on breakpoints. + */ + break; + default: + break; + } + + return AE_OK; +} + +static int __init acpi_os_name_setup(char *str) +{ + char *p = acpi_os_name; + int count = ACPI_MAX_OVERRIDE_LEN - 1; + + if (!str || !*str) + return 0; + + for (; count-- && *str; str++) { + if (isalnum(*str) || *str == ' ' || *str == ':') + *p++ = *str; + else if (*str == '\'' || *str == '"') + continue; + else + break; + } + *p = 0; + + return 1; + +} + +__setup("acpi_os_name=", acpi_os_name_setup); + +#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ +#define OSI_STRING_ENTRIES_MAX 16 /* arbitrary */ + +struct osi_setup_entry { + char string[OSI_STRING_LENGTH_MAX]; + bool enable; +}; + +static struct osi_setup_entry + osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = { + {"Module Device", true}, + {"Processor Device", true}, + {"3.0 _SCP Extensions", true}, + {"Processor Aggregator Device", true}, +}; + +void __init acpi_osi_setup(char *str) +{ + struct osi_setup_entry *osi; + bool enable = true; + int i; + + if (!acpi_gbl_create_osi_method) + return; + + if (str == NULL || *str == '\0') { + printk(KERN_INFO PREFIX "_OSI method disabled\n"); + acpi_gbl_create_osi_method = FALSE; + return; + } + + if (*str == '!') { + str++; + if (*str == '\0') { + osi_linux.default_disabling = 1; + return; + } else if (*str == '*') { + acpi_update_interfaces(ACPI_DISABLE_ALL_STRINGS); + for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { + osi = &osi_setup_entries[i]; + osi->enable = false; + } + return; + } + enable = false; + } + + for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { + osi = &osi_setup_entries[i]; + if (!strcmp(osi->string, str)) { + osi->enable = enable; + break; + } else if (osi->string[0] == '\0') { + osi->enable = enable; + strncpy(osi->string, str, OSI_STRING_LENGTH_MAX); + break; + } + } +} + +static void __init set_osi_linux(unsigned int enable) +{ + if (osi_linux.enable != enable) + osi_linux.enable = enable; + + if (osi_linux.enable) + acpi_osi_setup("Linux"); + else + acpi_osi_setup("!Linux"); + + return; +} + +static void __init acpi_cmdline_osi_linux(unsigned int enable) +{ + osi_linux.cmdline = 1; /* cmdline set the default and override DMI */ + osi_linux.dmi = 0; + set_osi_linux(enable); + + return; +} + +void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) +{ + printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + + if (enable == -1) + return; + + osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */ + set_osi_linux(enable); + + return; +} + +/* + * Modify the list of "OS Interfaces" reported to BIOS via _OSI + * + * empty string disables _OSI + * string starting with '!' disables that string + * otherwise string is added to list, augmenting built-in strings + */ +static void __init acpi_osi_setup_late(void) +{ + struct osi_setup_entry *osi; + char *str; + int i; + acpi_status status; + + if (osi_linux.default_disabling) { + status = acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS); + + if (ACPI_SUCCESS(status)) + printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors\n"); + } + + for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { + osi = &osi_setup_entries[i]; + str = osi->string; + + if (*str == '\0') + break; + if (osi->enable) { + status = acpi_install_interface(str); + + if (ACPI_SUCCESS(status)) + printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); + } else { + status = acpi_remove_interface(str); + + if (ACPI_SUCCESS(status)) + printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); + } + } +} + +static int __init osi_setup(char *str) +{ + if (str && !strcmp("Linux", str)) + acpi_cmdline_osi_linux(1); + else if (str && !strcmp("!Linux", str)) + acpi_cmdline_osi_linux(0); + else + acpi_osi_setup(str); + + return 1; +} + +__setup("acpi_osi=", osi_setup); + +/* + * Disable the auto-serialization of named objects creation methods. + * + * This feature is enabled by default. It marks the AML control methods + * that contain the opcodes to create named objects as "Serialized". + */ +static int __init acpi_no_auto_serialize_setup(char *str) +{ + acpi_gbl_auto_serialize_methods = FALSE; + pr_info("ACPI: auto-serialization disabled\n"); + + return 1; +} + +__setup("acpi_no_auto_serialize", acpi_no_auto_serialize_setup); + +/* Check of resource interference between native drivers and ACPI + * OperationRegions (SystemIO and System Memory only). + * IO ports and memory declared in ACPI might be used by the ACPI subsystem + * in arbitrary AML code and can interfere with legacy drivers. + * acpi_enforce_resources= can be set to: + * + * - strict (default) (2) + * -> further driver trying to access the resources will not load + * - lax (1) + * -> further driver trying to access the resources will load, but you + * get a system message that something might go wrong... + * + * - no (0) + * -> ACPI Operation Region resources will not be registered + * + */ +#define ENFORCE_RESOURCES_STRICT 2 +#define ENFORCE_RESOURCES_LAX 1 +#define ENFORCE_RESOURCES_NO 0 + +static unsigned int acpi_enforce_resources = ENFORCE_RESOURCES_STRICT; + +static int __init acpi_enforce_resources_setup(char *str) +{ + if (str == NULL || *str == '\0') + return 0; + + if (!strcmp("strict", str)) + acpi_enforce_resources = ENFORCE_RESOURCES_STRICT; + else if (!strcmp("lax", str)) + acpi_enforce_resources = ENFORCE_RESOURCES_LAX; + else if (!strcmp("no", str)) + acpi_enforce_resources = ENFORCE_RESOURCES_NO; + + return 1; +} + +__setup("acpi_enforce_resources=", acpi_enforce_resources_setup); + +/* Check for resource conflicts between ACPI OperationRegions and native + * drivers */ +int acpi_check_resource_conflict(const struct resource *res) +{ + acpi_adr_space_type space_id; + acpi_size length; + u8 warn = 0; + int clash = 0; + + if (acpi_enforce_resources == ENFORCE_RESOURCES_NO) + return 0; + if (!(res->flags & IORESOURCE_IO) && !(res->flags & IORESOURCE_MEM)) + return 0; + + if (res->flags & IORESOURCE_IO) + space_id = ACPI_ADR_SPACE_SYSTEM_IO; + else + space_id = ACPI_ADR_SPACE_SYSTEM_MEMORY; + + length = resource_size(res); + if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) + warn = 1; + clash = acpi_check_address_range(space_id, res->start, length, warn); + + if (clash) { + if (acpi_enforce_resources != ENFORCE_RESOURCES_NO) { + if (acpi_enforce_resources == ENFORCE_RESOURCES_LAX) + printk(KERN_NOTICE "ACPI: This conflict may" + " cause random problems and system" + " instability\n"); + printk(KERN_INFO "ACPI: If an ACPI driver is available" + " for this device, you should use it instead of" + " the native driver\n"); + } + if (acpi_enforce_resources == ENFORCE_RESOURCES_STRICT) + return -EBUSY; + } + return 0; +} +EXPORT_SYMBOL(acpi_check_resource_conflict); + +int acpi_check_region(resource_size_t start, resource_size_t n, + const char *name) +{ + struct resource res = { + .start = start, + .end = start + n - 1, + .name = name, + .flags = IORESOURCE_IO, + }; + + return acpi_check_resource_conflict(&res); +} +EXPORT_SYMBOL(acpi_check_region); + +/* + * Let drivers know whether the resource checks are effective + */ +int acpi_resources_are_enforced(void) +{ + return acpi_enforce_resources == ENFORCE_RESOURCES_STRICT; +} +EXPORT_SYMBOL(acpi_resources_are_enforced); + +/* + * Deallocate the memory for a spinlock. + */ +void acpi_os_delete_lock(acpi_spinlock handle) +{ + ACPI_FREE(handle); +} + +/* + * Acquire a spinlock. + * + * handle is a pointer to the spinlock_t. + */ + +acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp) +{ + acpi_cpu_flags flags; + spin_lock_irqsave(lockp, flags); + return flags; +} + +/* + * Release a spinlock. See above. + */ + +void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags) +{ + spin_unlock_irqrestore(lockp, flags); +} + +#ifndef ACPI_USE_LOCAL_CACHE + +/******************************************************************************* + * + * FUNCTION: acpi_os_create_cache + * + * PARAMETERS: name - Ascii name for the cache + * size - Size of each cached object + * depth - Maximum depth of the cache (in objects) + * cache - Where the new cache object is returned + * + * RETURN: status + * + * DESCRIPTION: Create a cache object + * + ******************************************************************************/ + +acpi_status +acpi_os_create_cache(char *name, u16 size, u16 depth, acpi_cache_t ** cache) +{ + *cache = kmem_cache_create(name, size, 0, 0, NULL); + if (*cache == NULL) + return AE_ERROR; + else + return AE_OK; +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_purge_cache + * + * PARAMETERS: Cache - Handle to cache object + * + * RETURN: Status + * + * DESCRIPTION: Free all objects within the requested cache. + * + ******************************************************************************/ + +acpi_status acpi_os_purge_cache(acpi_cache_t * cache) +{ + kmem_cache_shrink(cache); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_delete_cache + * + * PARAMETERS: Cache - Handle to cache object + * + * RETURN: Status + * + * DESCRIPTION: Free all objects within the requested cache and delete the + * cache object. + * + ******************************************************************************/ + +acpi_status acpi_os_delete_cache(acpi_cache_t * cache) +{ + kmem_cache_destroy(cache); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_os_release_object + * + * PARAMETERS: Cache - Handle to cache object + * Object - The object to be released + * + * RETURN: None + * + * DESCRIPTION: Release an object to the specified cache. If cache is full, + * the object is deleted. + * + ******************************************************************************/ + +acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) +{ + kmem_cache_free(cache, object); + return (AE_OK); +} +#endif + +static int __init acpi_no_static_ssdt_setup(char *s) +{ + acpi_gbl_disable_ssdt_table_install = TRUE; + pr_info("ACPI: static SSDT installation disabled\n"); + + return 0; +} + +early_param("acpi_no_static_ssdt", acpi_no_static_ssdt_setup); + +static int __init acpi_disable_return_repair(char *s) +{ + printk(KERN_NOTICE PREFIX + "ACPI: Predefined validation mechanism disabled\n"); + acpi_gbl_disable_auto_repair = TRUE; + + return 1; +} + +__setup("acpica_no_return_repair", acpi_disable_return_repair); + +acpi_status __init acpi_os_initialize(void) +{ + acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1a_event_block); + acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block); + acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block); + acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block); + if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) { + /* + * Use acpi_os_map_generic_address to pre-map the reset + * register if it's in system memory. + */ + int rv; + + rv = acpi_os_map_generic_address(&acpi_gbl_FADT.reset_register); + pr_debug(PREFIX "%s: map reset_reg status %d\n", __func__, rv); + } + + return AE_OK; +} + +acpi_status __init acpi_os_initialize1(void) +{ + acpi_reserve_resources(); + kacpid_wq = alloc_workqueue("kacpid", 0, 1); + kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1); + kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0); + BUG_ON(!kacpid_wq); + BUG_ON(!kacpi_notify_wq); + BUG_ON(!kacpi_hotplug_wq); + acpi_install_interface_handler(acpi_osi_handler); + acpi_osi_setup_late(); + return AE_OK; +} + +acpi_status acpi_os_terminate(void) +{ + if (acpi_irq_handler) { + acpi_os_remove_interrupt_handler(acpi_gbl_FADT.sci_interrupt, + acpi_irq_handler); + } + + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe1_block); + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block); + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block); + acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block); + if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) + acpi_os_unmap_generic_address(&acpi_gbl_FADT.reset_register); + + destroy_workqueue(kacpid_wq); + destroy_workqueue(kacpi_notify_wq); + destroy_workqueue(kacpi_hotplug_wq); + + return AE_OK; +} + +acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control, + u32 pm1b_control) +{ + int rc = 0; + if (__acpi_os_prepare_sleep) + rc = __acpi_os_prepare_sleep(sleep_state, + pm1a_control, pm1b_control); + if (rc < 0) + return AE_ERROR; + else if (rc > 0) + return AE_CTRL_SKIP; + + return AE_OK; +} + +void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, + u32 pm1a_ctrl, u32 pm1b_ctrl)) +{ + __acpi_os_prepare_sleep = func; +} + +acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, u32 val_a, + u32 val_b) +{ + int rc = 0; + if (__acpi_os_prepare_extended_sleep) + rc = __acpi_os_prepare_extended_sleep(sleep_state, + val_a, val_b); + if (rc < 0) + return AE_ERROR; + else if (rc > 0) + return AE_CTRL_SKIP; + + return AE_OK; +} + +void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state, + u32 val_a, u32 val_b)) +{ + __acpi_os_prepare_extended_sleep = func; +} diff --git a/kernel/drivers/acpi/pci_irq.c b/kernel/drivers/acpi/pci_irq.c new file mode 100644 index 000000000..b1def411c --- /dev/null +++ b/kernel/drivers/acpi/pci_irq.c @@ -0,0 +1,517 @@ +/* + * pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2002 Dominik Brodowski + * (c) Copyright 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME("pci_irq"); + +struct acpi_prt_entry { + struct list_head list; + struct acpi_pci_id id; + u8 pin; + acpi_handle link; + u32 index; /* GSI, or link _CRS index */ +}; + +static inline char pin_name(int pin) +{ + return 'A' + pin - 1; +} + +/* -------------------------------------------------------------------------- + PCI IRQ Routing Table (PRT) Support + -------------------------------------------------------------------------- */ + +/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ +static const struct dmi_system_id medion_md9580[] = { + { + .ident = "Medion MD9580-F laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), + DMI_MATCH(DMI_PRODUCT_NAME, "A555"), + }, + }, + { } +}; + +/* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */ +static const struct dmi_system_id dell_optiplex[] = { + { + .ident = "Dell Optiplex GX1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"), + }, + }, + { } +}; + +/* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */ +static const struct dmi_system_id hp_t5710[] = { + { + .ident = "HP t5710", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"), + DMI_MATCH(DMI_BOARD_NAME, "098Ch"), + }, + }, + { } +}; + +struct prt_quirk { + const struct dmi_system_id *system; + unsigned int segment; + unsigned int bus; + unsigned int device; + unsigned char pin; + const char *source; /* according to BIOS */ + const char *actual_source; +}; + +#define PCI_INTX_PIN(c) (c - 'A' + 1) + +/* + * These systems have incorrect _PRT entries. The BIOS claims the PCI + * interrupt at the listed segment/bus/device/pin is connected to the first + * link device, but it is actually connected to the second. + */ +static const struct prt_quirk prt_quirks[] = { + { medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'), + "\\_SB_.PCI0.ISA_.LNKA", + "\\_SB_.PCI0.ISA_.LNKB"}, + { dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'), + "\\_SB_.LNKB", + "\\_SB_.LNKA"}, + { hp_t5710, 0, 0, 1, PCI_INTX_PIN('A'), + "\\_SB_.PCI0.LNK1", + "\\_SB_.PCI0.LNK3"}, +}; + +static void do_prt_fixups(struct acpi_prt_entry *entry, + struct acpi_pci_routing_table *prt) +{ + int i; + const struct prt_quirk *quirk; + + for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) { + quirk = &prt_quirks[i]; + + /* All current quirks involve link devices, not GSIs */ + if (!prt->source) + continue; + + if (dmi_check_system(quirk->system) && + entry->id.segment == quirk->segment && + entry->id.bus == quirk->bus && + entry->id.device == quirk->device && + entry->pin == quirk->pin && + !strcmp(prt->source, quirk->source) && + strlen(prt->source) >= strlen(quirk->actual_source)) { + printk(KERN_WARNING PREFIX "firmware reports " + "%04x:%02x:%02x PCI INT %c connected to %s; " + "changing to %s\n", + entry->id.segment, entry->id.bus, + entry->id.device, pin_name(entry->pin), + prt->source, quirk->actual_source); + strcpy(prt->source, quirk->actual_source); + } + } +} + +static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, + int pin, struct acpi_pci_routing_table *prt, + struct acpi_prt_entry **entry_ptr) +{ + int segment = pci_domain_nr(dev->bus); + int bus = dev->bus->number; + int device = PCI_SLOT(dev->devfn); + struct acpi_prt_entry *entry; + + if (((prt->address >> 16) & 0xffff) != device || + prt->pin + 1 != pin) + return -ENODEV; + + entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + /* + * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses + * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert + * it here. + */ + entry->id.segment = segment; + entry->id.bus = bus; + entry->id.device = (prt->address >> 16) & 0xFFFF; + entry->pin = prt->pin + 1; + + do_prt_fixups(entry, prt); + + entry->index = prt->source_index; + + /* + * Type 1: Dynamic + * --------------- + * The 'source' field specifies the PCI interrupt link device used to + * configure the IRQ assigned to this slot|dev|pin. The 'source_index' + * indicates which resource descriptor in the resource template (of + * the link device) this interrupt is allocated from. + * + * NOTE: Don't query the Link Device for IRQ information at this time + * because Link Device enumeration may not have occurred yet + * (e.g. exists somewhere 'below' this _PRT entry in the ACPI + * namespace). + */ + if (prt->source[0]) + acpi_get_handle(handle, prt->source, &entry->link); + + /* + * Type 2: Static + * -------------- + * The 'source' field is NULL, and the 'source_index' field specifies + * the IRQ value, which is hardwired to specific interrupt inputs on + * the interrupt controller. + */ + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, + " %04x:%02x:%02x[%c] -> %s[%d]\n", + entry->id.segment, entry->id.bus, + entry->id.device, pin_name(entry->pin), + prt->source, entry->index)); + + *entry_ptr = entry; + + return 0; +} + +static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, + int pin, struct acpi_prt_entry **entry_ptr) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_pci_routing_table *entry; + acpi_handle handle = NULL; + + if (dev->bus->bridge) + handle = ACPI_HANDLE(dev->bus->bridge); + + if (!handle) + return -ENODEV; + + /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ + status = acpi_get_irq_routing_table(handle, &buffer); + if (ACPI_FAILURE(status)) { + kfree(buffer.pointer); + return -ENODEV; + } + + entry = buffer.pointer; + while (entry && (entry->length > 0)) { + if (!acpi_pci_irq_check_entry(handle, dev, pin, + entry, entry_ptr)) + break; + entry = (struct acpi_pci_routing_table *) + ((unsigned long)entry + entry->length); + } + + kfree(buffer.pointer); + return 0; +} + +/* -------------------------------------------------------------------------- + PCI Interrupt Routing Support + -------------------------------------------------------------------------- */ +#ifdef CONFIG_X86_IO_APIC +extern int noioapicquirk; +extern int noioapicreroute; + +static int bridge_has_boot_interrupt_variant(struct pci_bus *bus) +{ + struct pci_bus *bus_it; + + for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) { + if (!bus_it->self) + return 0; + if (bus_it->self->irq_reroute_variant) + return bus_it->self->irq_reroute_variant; + } + return 0; +} + +/* + * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ + * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does + * during interrupt handling). When this INTx generation cannot be disabled, + * we reroute these interrupts to their legacy equivalent to get rid of + * spurious interrupts. + */ +static int acpi_reroute_boot_interrupt(struct pci_dev *dev, + struct acpi_prt_entry *entry) +{ + if (noioapicquirk || noioapicreroute) { + return 0; + } else { + switch (bridge_has_boot_interrupt_variant(dev->bus)) { + case 0: + /* no rerouting necessary */ + return 0; + case INTEL_IRQ_REROUTE_VARIANT: + /* + * Remap according to INTx routing table in 6700PXH + * specs, intel order number 302628-002, section + * 2.15.2. Other chipsets (80332, ...) have the same + * mapping and are handled here as well. + */ + dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy " + "IRQ %d\n", entry->index, + (entry->index % 4) + 16); + entry->index = (entry->index % 4) + 16; + return 1; + default: + dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy " + "IRQ: unknown mapping\n", entry->index); + return -1; + } + } +} +#endif /* CONFIG_X86_IO_APIC */ + +static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) +{ + struct acpi_prt_entry *entry = NULL; + struct pci_dev *bridge; + u8 bridge_pin, orig_pin = pin; + int ret; + + ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); + if (!ret && entry) { +#ifdef CONFIG_X86_IO_APIC + acpi_reroute_boot_interrupt(dev, entry); +#endif /* CONFIG_X86_IO_APIC */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n", + pci_name(dev), pin_name(pin))); + return entry; + } + + /* + * Attempt to derive an IRQ for this device from a parent bridge's + * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). + */ + bridge = dev->bus->self; + while (bridge) { + pin = pci_swizzle_interrupt_pin(dev, pin); + + if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { + /* PC card has the same IRQ as its cardbridge */ + bridge_pin = bridge->pin; + if (!bridge_pin) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No interrupt pin configured for device %s\n", + pci_name(bridge))); + return NULL; + } + pin = bridge_pin; + } + + ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); + if (!ret && entry) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Derived GSI for %s INT %c from %s\n", + pci_name(dev), pin_name(orig_pin), + pci_name(bridge))); + return entry; + } + + dev = bridge; + bridge = dev->bus->self; + } + + dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n", + pin_name(orig_pin)); + return NULL; +} + +#if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA) +static int acpi_isa_register_gsi(struct pci_dev *dev) +{ + u32 dev_gsi; + + /* Interrupt Line values above 0xF are forbidden */ + if (dev->irq > 0 && (dev->irq <= 0xF) && + (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { + dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n", + pin_name(dev->pin), dev->irq); + acpi_register_gsi(&dev->dev, dev_gsi, + ACPI_LEVEL_SENSITIVE, + ACPI_ACTIVE_LOW); + return 0; + } + return -EINVAL; +} +#else +static inline int acpi_isa_register_gsi(struct pci_dev *dev) +{ + return -ENODEV; +} +#endif + +int acpi_pci_irq_enable(struct pci_dev *dev) +{ + struct acpi_prt_entry *entry; + int gsi; + u8 pin; + int triggering = ACPI_LEVEL_SENSITIVE; + int polarity = ACPI_ACTIVE_LOW; + char *link = NULL; + char link_desc[16]; + int rc; + + pin = dev->pin; + if (!pin) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No interrupt pin configured for device %s\n", + pci_name(dev))); + return 0; + } + + if (dev->irq_managed && dev->irq > 0) + return 0; + + entry = acpi_pci_irq_lookup(dev, pin); + if (!entry) { + /* + * IDE legacy mode controller IRQs are magic. Why do compat + * extensions always make such a nasty mess. + */ + if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && + (dev->class & 0x05) == 0) + return 0; + } + + if (entry) { + if (entry->link) + gsi = acpi_pci_link_allocate_irq(entry->link, + entry->index, + &triggering, &polarity, + &link); + else + gsi = entry->index; + } else + gsi = -1; + + /* + * No IRQ known to the ACPI subsystem - maybe the BIOS / + * driver reported one, then use it. Exit in any case. + */ + if (gsi < 0) { + if (acpi_isa_register_gsi(dev)) + dev_warn(&dev->dev, "PCI INT %c: no GSI\n", + pin_name(pin)); + + kfree(entry); + return 0; + } + + rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity); + if (rc < 0) { + dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", + pin_name(pin)); + kfree(entry); + return rc; + } + dev->irq = rc; + dev->irq_managed = 1; + + if (link) + snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link); + else + link_desc[0] = '\0'; + + dev_dbg(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n", + pin_name(pin), link_desc, gsi, + (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", + (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); + + kfree(entry); + return 0; +} + +void acpi_pci_irq_disable(struct pci_dev *dev) +{ + struct acpi_prt_entry *entry; + int gsi; + u8 pin; + + pin = dev->pin; + if (!pin || !dev->irq_managed || dev->irq <= 0) + return; + + /* Keep IOAPIC pin configuration when suspending */ + if (dev->dev.power.is_prepared) + return; +#ifdef CONFIG_PM + if (dev->dev.power.runtime_status == RPM_SUSPENDING) + return; +#endif + + entry = acpi_pci_irq_lookup(dev, pin); + if (!entry) + return; + + if (entry->link) + gsi = acpi_pci_link_free_irq(entry->link); + else + gsi = entry->index; + + kfree(entry); + + /* + * TBD: It might be worth clearing dev->irq by magic constant + * (e.g. PCI_UNDEFINED_IRQ). + */ + + dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); + if (gsi >= 0) { + acpi_unregister_gsi(gsi); + dev->irq_managed = 0; + } +} diff --git a/kernel/drivers/acpi/pci_link.c b/kernel/drivers/acpi/pci_link.c new file mode 100644 index 000000000..cfd7581cc --- /dev/null +++ b/kernel/drivers/acpi/pci_link.c @@ -0,0 +1,886 @@ +/* + * pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 34 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2002 Dominik Brodowski + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * TBD: + * 1. Support more than one IRQ resource entry per link device (index). + * 2. Implement start/stop mechanism and use ACPI Bus Driver facilities + * for IRQ management (e.g. start()->_SRS). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME("pci_link"); +#define ACPI_PCI_LINK_CLASS "pci_irq_routing" +#define ACPI_PCI_LINK_DEVICE_NAME "PCI Interrupt Link" +#define ACPI_PCI_LINK_FILE_INFO "info" +#define ACPI_PCI_LINK_FILE_STATUS "state" +#define ACPI_PCI_LINK_MAX_POSSIBLE 16 + +static int acpi_pci_link_add(struct acpi_device *device, + const struct acpi_device_id *not_used); +static void acpi_pci_link_remove(struct acpi_device *device); + +static const struct acpi_device_id link_device_ids[] = { + {"PNP0C0F", 0}, + {"", 0}, +}; + +static struct acpi_scan_handler pci_link_handler = { + .ids = link_device_ids, + .attach = acpi_pci_link_add, + .detach = acpi_pci_link_remove, +}; + +/* + * If a link is initialized, we never change its active and initialized + * later even the link is disable. Instead, we just repick the active irq + */ +struct acpi_pci_link_irq { + u8 active; /* Current IRQ */ + u8 triggering; /* All IRQs */ + u8 polarity; /* All IRQs */ + u8 resource_type; + u8 possible_count; + u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; + u8 initialized:1; + u8 reserved:7; +}; + +struct acpi_pci_link { + struct list_head list; + struct acpi_device *device; + struct acpi_pci_link_irq irq; + int refcnt; +}; + +static LIST_HEAD(acpi_link_list); +static DEFINE_MUTEX(acpi_link_lock); + +/* -------------------------------------------------------------------------- + PCI Link Device Management + -------------------------------------------------------------------------- */ + +/* + * set context (link) possible list from resource list + */ +static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource, + void *context) +{ + struct acpi_pci_link *link = context; + u32 i; + + switch (resource->type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + case ACPI_RESOURCE_TYPE_IRQ: + { + struct acpi_resource_irq *p = &resource->data.irq; + if (!p || !p->interrupt_count) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Blank _PRS IRQ resource\n")); + return AE_OK; + } + for (i = 0; + (i < p->interrupt_count + && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { + if (!p->interrupts[i]) { + printk(KERN_WARNING PREFIX + "Invalid _PRS IRQ %d\n", + p->interrupts[i]); + continue; + } + link->irq.possible[i] = p->interrupts[i]; + link->irq.possible_count++; + } + link->irq.triggering = p->triggering; + link->irq.polarity = p->polarity; + link->irq.resource_type = ACPI_RESOURCE_TYPE_IRQ; + break; + } + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + { + struct acpi_resource_extended_irq *p = + &resource->data.extended_irq; + if (!p || !p->interrupt_count) { + printk(KERN_WARNING PREFIX + "Blank _PRS EXT IRQ resource\n"); + return AE_OK; + } + for (i = 0; + (i < p->interrupt_count + && i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) { + if (!p->interrupts[i]) { + printk(KERN_WARNING PREFIX + "Invalid _PRS IRQ %d\n", + p->interrupts[i]); + continue; + } + link->irq.possible[i] = p->interrupts[i]; + link->irq.possible_count++; + } + link->irq.triggering = p->triggering; + link->irq.polarity = p->polarity; + link->irq.resource_type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ; + break; + } + default: + printk(KERN_ERR PREFIX "_PRS resource type 0x%x isn't an IRQ\n", + resource->type); + return AE_OK; + } + + return AE_CTRL_TERMINATE; +} + +static int acpi_pci_link_get_possible(struct acpi_pci_link *link) +{ + acpi_status status; + + status = acpi_walk_resources(link->device->handle, METHOD_NAME__PRS, + acpi_pci_link_check_possible, link); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRS")); + return -ENODEV; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found %d possible IRQs\n", + link->irq.possible_count)); + + return 0; +} + +static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource, + void *context) +{ + int *irq = context; + + switch (resource->type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + case ACPI_RESOURCE_TYPE_IRQ: + { + struct acpi_resource_irq *p = &resource->data.irq; + if (!p || !p->interrupt_count) { + /* + * IRQ descriptors may have no IRQ# bits set, + * particularly those those w/ _STA disabled + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Blank _CRS IRQ resource\n")); + return AE_OK; + } + *irq = p->interrupts[0]; + break; + } + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + { + struct acpi_resource_extended_irq *p = + &resource->data.extended_irq; + if (!p || !p->interrupt_count) { + /* + * extended IRQ descriptors must + * return at least 1 IRQ + */ + printk(KERN_WARNING PREFIX + "Blank _CRS EXT IRQ resource\n"); + return AE_OK; + } + *irq = p->interrupts[0]; + break; + } + break; + default: + printk(KERN_ERR PREFIX "_CRS resource type 0x%x isn't an IRQ\n", + resource->type); + return AE_OK; + } + + return AE_CTRL_TERMINATE; +} + +/* + * Run _CRS and set link->irq.active + * + * return value: + * 0 - success + * !0 - failure + */ +static int acpi_pci_link_get_current(struct acpi_pci_link *link) +{ + int result = 0; + acpi_status status; + int irq = 0; + + link->irq.active = 0; + + /* in practice, status disabled is meaningless, ignore it */ + if (acpi_strict) { + /* Query _STA, set link->device->status */ + result = acpi_bus_get_status(link->device); + if (result) { + printk(KERN_ERR PREFIX "Unable to read status\n"); + goto end; + } + + if (!link->device->status.enabled) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n")); + return 0; + } + } + + /* + * Query and parse _CRS to get the current IRQ assignment. + */ + + status = acpi_walk_resources(link->device->handle, METHOD_NAME__CRS, + acpi_pci_link_check_current, &irq); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _CRS")); + result = -ENODEV; + goto end; + } + + if (acpi_strict && !irq) { + printk(KERN_ERR PREFIX "_CRS returned 0\n"); + result = -ENODEV; + } + + link->irq.active = irq; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active)); + + end: + return result; +} + +static int acpi_pci_link_set(struct acpi_pci_link *link, int irq) +{ + int result; + acpi_status status; + struct { + struct acpi_resource res; + struct acpi_resource end; + } *resource; + struct acpi_buffer buffer = { 0, NULL }; + + if (!irq) + return -EINVAL; + + resource = kzalloc(sizeof(*resource) + 1, irqs_disabled() ? GFP_ATOMIC: GFP_KERNEL); + if (!resource) + return -ENOMEM; + + buffer.length = sizeof(*resource) + 1; + buffer.pointer = resource; + + switch (link->irq.resource_type) { + case ACPI_RESOURCE_TYPE_IRQ: + resource->res.type = ACPI_RESOURCE_TYPE_IRQ; + resource->res.length = sizeof(struct acpi_resource); + resource->res.data.irq.triggering = link->irq.triggering; + resource->res.data.irq.polarity = + link->irq.polarity; + if (link->irq.triggering == ACPI_EDGE_SENSITIVE) + resource->res.data.irq.sharable = + ACPI_EXCLUSIVE; + else + resource->res.data.irq.sharable = ACPI_SHARED; + resource->res.data.irq.interrupt_count = 1; + resource->res.data.irq.interrupts[0] = irq; + break; + + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + resource->res.type = ACPI_RESOURCE_TYPE_EXTENDED_IRQ; + resource->res.length = sizeof(struct acpi_resource); + resource->res.data.extended_irq.producer_consumer = + ACPI_CONSUMER; + resource->res.data.extended_irq.triggering = + link->irq.triggering; + resource->res.data.extended_irq.polarity = + link->irq.polarity; + if (link->irq.triggering == ACPI_EDGE_SENSITIVE) + resource->res.data.irq.sharable = + ACPI_EXCLUSIVE; + else + resource->res.data.irq.sharable = ACPI_SHARED; + resource->res.data.extended_irq.interrupt_count = 1; + resource->res.data.extended_irq.interrupts[0] = irq; + /* ignore resource_source, it's optional */ + break; + default: + printk(KERN_ERR PREFIX "Invalid Resource_type %d\n", link->irq.resource_type); + result = -EINVAL; + goto end; + + } + resource->end.type = ACPI_RESOURCE_TYPE_END_TAG; + resource->end.length = sizeof(struct acpi_resource); + + /* Attempt to set the resource */ + status = acpi_set_current_resources(link->device->handle, &buffer); + + /* check for total failure */ + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SRS")); + result = -ENODEV; + goto end; + } + + /* Query _STA, set device->status */ + result = acpi_bus_get_status(link->device); + if (result) { + printk(KERN_ERR PREFIX "Unable to read status\n"); + goto end; + } + if (!link->device->status.enabled) { + printk(KERN_WARNING PREFIX + "%s [%s] disabled and referenced, BIOS bug\n", + acpi_device_name(link->device), + acpi_device_bid(link->device)); + } + + /* Query _CRS, set link->irq.active */ + result = acpi_pci_link_get_current(link); + if (result) { + goto end; + } + + /* + * Is current setting not what we set? + * set link->irq.active + */ + if (link->irq.active != irq) { + /* + * policy: when _CRS doesn't return what we just _SRS + * assume _SRS worked and override _CRS value. + */ + printk(KERN_WARNING PREFIX + "%s [%s] BIOS reported IRQ %d, using IRQ %d\n", + acpi_device_name(link->device), + acpi_device_bid(link->device), link->irq.active, irq); + link->irq.active = irq; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active)); + + end: + kfree(resource); + return result; +} + +/* -------------------------------------------------------------------------- + PCI Link IRQ Management + -------------------------------------------------------------------------- */ + +/* + * "acpi_irq_balance" (default in APIC mode) enables ACPI to use PIC Interrupt + * Link Devices to move the PIRQs around to minimize sharing. + * + * "acpi_irq_nobalance" (default in PIC mode) tells ACPI not to move any PIC IRQs + * that the BIOS has already set to active. This is necessary because + * ACPI has no automatic means of knowing what ISA IRQs are used. Note that + * if the BIOS doesn't set a Link Device active, ACPI needs to program it + * even if acpi_irq_nobalance is set. + * + * A tables of penalties avoids directing PCI interrupts to well known + * ISA IRQs. Boot params are available to over-ride the default table: + * + * List interrupts that are free for PCI use. + * acpi_irq_pci=n[,m] + * + * List interrupts that should not be used for PCI: + * acpi_irq_isa=n[,m] + * + * Note that PCI IRQ routers have a list of possible IRQs, + * which may not include the IRQs this table says are available. + * + * Since this heuristic can't tell the difference between a link + * that no device will attach to, vs. a link which may be shared + * by multiple active devices -- it is not optimal. + * + * If interrupt performance is that important, get an IO-APIC system + * with a pin dedicated to each device. Or for that matter, an MSI + * enabled system. + */ + +#define ACPI_MAX_IRQS 256 +#define ACPI_MAX_ISA_IRQ 16 + +#define PIRQ_PENALTY_PCI_AVAILABLE (0) +#define PIRQ_PENALTY_PCI_POSSIBLE (16*16) +#define PIRQ_PENALTY_PCI_USING (16*16*16) +#define PIRQ_PENALTY_ISA_TYPICAL (16*16*16*16) +#define PIRQ_PENALTY_ISA_USED (16*16*16*16*16) +#define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16) + +static int acpi_irq_penalty[ACPI_MAX_IRQS] = { + PIRQ_PENALTY_ISA_ALWAYS, /* IRQ0 timer */ + PIRQ_PENALTY_ISA_ALWAYS, /* IRQ1 keyboard */ + PIRQ_PENALTY_ISA_ALWAYS, /* IRQ2 cascade */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ3 serial */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ4 serial */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ5 sometimes SoundBlaster */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ6 */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ7 parallel, spurious */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ8 rtc, sometimes */ + PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ9 PCI, often acpi */ + PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ10 PCI */ + PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ11 PCI */ + PIRQ_PENALTY_ISA_USED, /* IRQ12 mouse */ + PIRQ_PENALTY_ISA_USED, /* IRQ13 fpe, sometimes */ + PIRQ_PENALTY_ISA_USED, /* IRQ14 ide0 */ + PIRQ_PENALTY_ISA_USED, /* IRQ15 ide1 */ + /* >IRQ15 */ +}; + +int __init acpi_irq_penalty_init(void) +{ + struct acpi_pci_link *link; + int i; + + /* + * Update penalties to facilitate IRQ balancing. + */ + list_for_each_entry(link, &acpi_link_list, list) { + + /* + * reflect the possible and active irqs in the penalty table -- + * useful for breaking ties. + */ + if (link->irq.possible_count) { + int penalty = + PIRQ_PENALTY_PCI_POSSIBLE / + link->irq.possible_count; + + for (i = 0; i < link->irq.possible_count; i++) { + if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ) + acpi_irq_penalty[link->irq. + possible[i]] += + penalty; + } + + } else if (link->irq.active) { + acpi_irq_penalty[link->irq.active] += + PIRQ_PENALTY_PCI_POSSIBLE; + } + } + /* Add a penalty for the SCI */ + acpi_irq_penalty[acpi_gbl_FADT.sci_interrupt] += PIRQ_PENALTY_PCI_USING; + return 0; +} + +static int acpi_irq_balance = -1; /* 0: static, 1: balance */ + +static int acpi_pci_link_allocate(struct acpi_pci_link *link) +{ + int irq; + int i; + + if (link->irq.initialized) { + if (link->refcnt == 0) + /* This means the link is disabled but initialized */ + acpi_pci_link_set(link, link->irq.active); + return 0; + } + + /* + * search for active IRQ in list of possible IRQs. + */ + for (i = 0; i < link->irq.possible_count; ++i) { + if (link->irq.active == link->irq.possible[i]) + break; + } + /* + * forget active IRQ that is not in possible list + */ + if (i == link->irq.possible_count) { + if (acpi_strict) + printk(KERN_WARNING PREFIX "_CRS %d not found" + " in _PRS\n", link->irq.active); + link->irq.active = 0; + } + + /* + * if active found, use it; else pick entry from end of possible list. + */ + if (link->irq.active) + irq = link->irq.active; + else + irq = link->irq.possible[link->irq.possible_count - 1]; + + if (acpi_irq_balance || !link->irq.active) { + /* + * Select the best IRQ. This is done in reverse to promote + * the use of IRQs 9, 10, 11, and >15. + */ + for (i = (link->irq.possible_count - 1); i >= 0; i--) { + if (acpi_irq_penalty[irq] > + acpi_irq_penalty[link->irq.possible[i]]) + irq = link->irq.possible[i]; + } + } + + /* Attempt to enable the link device at this IRQ. */ + if (acpi_pci_link_set(link, irq)) { + printk(KERN_ERR PREFIX "Unable to set IRQ for %s [%s]. " + "Try pci=noacpi or acpi=off\n", + acpi_device_name(link->device), + acpi_device_bid(link->device)); + return -ENODEV; + } else { + acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_USING; + printk(KERN_WARNING PREFIX "%s [%s] enabled at IRQ %d\n", + acpi_device_name(link->device), + acpi_device_bid(link->device), link->irq.active); + } + + link->irq.initialized = 1; + return 0; +} + +/* + * acpi_pci_link_allocate_irq + * success: return IRQ >= 0 + * failure: return -1 + */ +int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, + int *polarity, char **name) +{ + int result; + struct acpi_device *device; + struct acpi_pci_link *link; + + result = acpi_bus_get_device(handle, &device); + if (result) { + printk(KERN_ERR PREFIX "Invalid link device\n"); + return -1; + } + + link = acpi_driver_data(device); + if (!link) { + printk(KERN_ERR PREFIX "Invalid link context\n"); + return -1; + } + + /* TBD: Support multiple index (IRQ) entries per Link Device */ + if (index) { + printk(KERN_ERR PREFIX "Invalid index %d\n", index); + return -1; + } + + mutex_lock(&acpi_link_lock); + if (acpi_pci_link_allocate(link)) { + mutex_unlock(&acpi_link_lock); + return -1; + } + + if (!link->irq.active) { + mutex_unlock(&acpi_link_lock); + printk(KERN_ERR PREFIX "Link active IRQ is 0!\n"); + return -1; + } + link->refcnt++; + mutex_unlock(&acpi_link_lock); + + if (triggering) + *triggering = link->irq.triggering; + if (polarity) + *polarity = link->irq.polarity; + if (name) + *name = acpi_device_bid(link->device); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is referenced\n", + acpi_device_bid(link->device))); + return (link->irq.active); +} + +/* + * We don't change link's irq information here. After it is reenabled, we + * continue use the info + */ +int acpi_pci_link_free_irq(acpi_handle handle) +{ + struct acpi_device *device; + struct acpi_pci_link *link; + acpi_status result; + + result = acpi_bus_get_device(handle, &device); + if (result) { + printk(KERN_ERR PREFIX "Invalid link device\n"); + return -1; + } + + link = acpi_driver_data(device); + if (!link) { + printk(KERN_ERR PREFIX "Invalid link context\n"); + return -1; + } + + mutex_lock(&acpi_link_lock); + if (!link->irq.initialized) { + mutex_unlock(&acpi_link_lock); + printk(KERN_ERR PREFIX "Link isn't initialized\n"); + return -1; + } +#ifdef FUTURE_USE + /* + * The Link reference count allows us to _DISable an unused link + * and suspend time, and set it again on resume. + * However, 2.6.12 still has irq_router.resume + * which blindly restores the link state. + * So we disable the reference count method + * to prevent duplicate acpi_pci_link_set() + * which would harm some systems + */ + link->refcnt--; +#endif + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is dereferenced\n", + acpi_device_bid(link->device))); + + if (link->refcnt == 0) + acpi_evaluate_object(link->device->handle, "_DIS", NULL, NULL); + + mutex_unlock(&acpi_link_lock); + return (link->irq.active); +} + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static int acpi_pci_link_add(struct acpi_device *device, + const struct acpi_device_id *not_used) +{ + int result; + struct acpi_pci_link *link; + int i; + int found = 0; + + link = kzalloc(sizeof(struct acpi_pci_link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + link->device = device; + strcpy(acpi_device_name(device), ACPI_PCI_LINK_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); + device->driver_data = link; + + mutex_lock(&acpi_link_lock); + result = acpi_pci_link_get_possible(link); + if (result) + goto end; + + /* query and set link->irq.active */ + acpi_pci_link_get_current(link); + + printk(KERN_INFO PREFIX "%s [%s] (IRQs", acpi_device_name(device), + acpi_device_bid(device)); + for (i = 0; i < link->irq.possible_count; i++) { + if (link->irq.active == link->irq.possible[i]) { + printk(KERN_CONT " *%d", link->irq.possible[i]); + found = 1; + } else + printk(KERN_CONT " %d", link->irq.possible[i]); + } + + printk(KERN_CONT ")"); + + if (!found) + printk(KERN_CONT " *%d", link->irq.active); + + if (!link->device->status.enabled) + printk(KERN_CONT ", disabled."); + + printk(KERN_CONT "\n"); + + list_add_tail(&link->list, &acpi_link_list); + + end: + /* disable all links -- to be activated on use */ + acpi_evaluate_object(device->handle, "_DIS", NULL, NULL); + mutex_unlock(&acpi_link_lock); + + if (result) + kfree(link); + + return result < 0 ? result : 1; +} + +static int acpi_pci_link_resume(struct acpi_pci_link *link) +{ + if (link->refcnt && link->irq.active && link->irq.initialized) + return (acpi_pci_link_set(link, link->irq.active)); + + return 0; +} + +static void irqrouter_resume(void) +{ + struct acpi_pci_link *link; + + list_for_each_entry(link, &acpi_link_list, list) { + acpi_pci_link_resume(link); + } +} + +static void acpi_pci_link_remove(struct acpi_device *device) +{ + struct acpi_pci_link *link; + + link = acpi_driver_data(device); + + mutex_lock(&acpi_link_lock); + list_del(&link->list); + mutex_unlock(&acpi_link_lock); + + kfree(link); +} + +/* + * modify acpi_irq_penalty[] from cmdline + */ +static int __init acpi_irq_penalty_update(char *str, int used) +{ + int i; + + for (i = 0; i < 16; i++) { + int retval; + int irq; + + retval = get_option(&str, &irq); + + if (!retval) + break; /* no number found */ + + if (irq < 0) + continue; + + if (irq >= ARRAY_SIZE(acpi_irq_penalty)) + continue; + + if (used) + acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED; + else + acpi_irq_penalty[irq] = PIRQ_PENALTY_PCI_AVAILABLE; + + if (retval != 2) /* no next number */ + break; + } + return 1; +} + +/* + * We'd like PNP to call this routine for the + * single ISA_USED value for each legacy device. + * But instead it calls us with each POSSIBLE setting. + * There is no ISA_POSSIBLE weight, so we simply use + * the (small) PCI_USING penalty. + */ +void acpi_penalize_isa_irq(int irq, int active) +{ + if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) { + if (active) + acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED; + else + acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING; + } +} + +/* + * Over-ride default table to reserve additional IRQs for use by ISA + * e.g. acpi_irq_isa=5 + * Useful for telling ACPI how not to interfere with your ISA sound card. + */ +static int __init acpi_irq_isa(char *str) +{ + return acpi_irq_penalty_update(str, 1); +} + +__setup("acpi_irq_isa=", acpi_irq_isa); + +/* + * Over-ride default table to free additional IRQs for use by PCI + * e.g. acpi_irq_pci=7,15 + * Used for acpi_irq_balance to free up IRQs to reduce PCI IRQ sharing. + */ +static int __init acpi_irq_pci(char *str) +{ + return acpi_irq_penalty_update(str, 0); +} + +__setup("acpi_irq_pci=", acpi_irq_pci); + +static int __init acpi_irq_nobalance_set(char *str) +{ + acpi_irq_balance = 0; + return 1; +} + +__setup("acpi_irq_nobalance", acpi_irq_nobalance_set); + +static int __init acpi_irq_balance_set(char *str) +{ + acpi_irq_balance = 1; + return 1; +} + +__setup("acpi_irq_balance", acpi_irq_balance_set); + +static struct syscore_ops irqrouter_syscore_ops = { + .resume = irqrouter_resume, +}; + +void __init acpi_pci_link_init(void) +{ + if (acpi_noirq) + return; + + if (acpi_irq_balance == -1) { + /* no command line switch: enable balancing in IOAPIC mode */ + if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC) + acpi_irq_balance = 1; + else + acpi_irq_balance = 0; + } + register_syscore_ops(&irqrouter_syscore_ops); + acpi_scan_add_handler(&pci_link_handler); +} diff --git a/kernel/drivers/acpi/pci_root.c b/kernel/drivers/acpi/pci_root.c new file mode 100644 index 000000000..1b5569c09 --- /dev/null +++ b/kernel/drivers/acpi/pci_root.c @@ -0,0 +1,667 @@ +/* + * pci_root.c - ACPI PCI Root Bridge Driver ($Revision: 40 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for acpi_hest_init() */ + +#include "internal.h" + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME("pci_root"); +#define ACPI_PCI_ROOT_CLASS "pci_bridge" +#define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge" +static int acpi_pci_root_add(struct acpi_device *device, + const struct acpi_device_id *not_used); +static void acpi_pci_root_remove(struct acpi_device *device); + +static int acpi_pci_root_scan_dependent(struct acpi_device *adev) +{ + acpiphp_check_host_bridge(adev); + return 0; +} + +#define ACPI_PCIE_REQ_SUPPORT (OSC_PCI_EXT_CONFIG_SUPPORT \ + | OSC_PCI_ASPM_SUPPORT \ + | OSC_PCI_CLOCK_PM_SUPPORT \ + | OSC_PCI_MSI_SUPPORT) + +static const struct acpi_device_id root_device_ids[] = { + {"PNP0A03", 0}, + {"", 0}, +}; + +static struct acpi_scan_handler pci_root_handler = { + .ids = root_device_ids, + .attach = acpi_pci_root_add, + .detach = acpi_pci_root_remove, + .hotplug = { + .enabled = true, + .scan_dependent = acpi_pci_root_scan_dependent, + }, +}; + +static DEFINE_MUTEX(osc_lock); + +/** + * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge + * @handle - the ACPI CA node in question. + * + * Note: we could make this API take a struct acpi_device * instead, but + * for now, it's more convenient to operate on an acpi_handle. + */ +int acpi_is_root_bridge(acpi_handle handle) +{ + int ret; + struct acpi_device *device; + + ret = acpi_bus_get_device(handle, &device); + if (ret) + return 0; + + ret = acpi_match_device_ids(device, root_device_ids); + if (ret) + return 0; + else + return 1; +} +EXPORT_SYMBOL_GPL(acpi_is_root_bridge); + +static acpi_status +get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) +{ + struct resource *res = data; + struct acpi_resource_address64 address; + acpi_status status; + + status = acpi_resource_to_address64(resource, &address); + if (ACPI_FAILURE(status)) + return AE_OK; + + if ((address.address.address_length > 0) && + (address.resource_type == ACPI_BUS_NUMBER_RANGE)) { + res->start = address.address.minimum; + res->end = address.address.minimum + address.address.address_length - 1; + } + + return AE_OK; +} + +static acpi_status try_get_root_bridge_busnr(acpi_handle handle, + struct resource *res) +{ + acpi_status status; + + res->start = -1; + status = + acpi_walk_resources(handle, METHOD_NAME__CRS, + get_root_bridge_busnr_callback, res); + if (ACPI_FAILURE(status)) + return status; + if (res->start == -1) + return AE_ERROR; + return AE_OK; +} + +struct pci_osc_bit_struct { + u32 bit; + char *desc; +}; + +static struct pci_osc_bit_struct pci_osc_support_bit[] = { + { OSC_PCI_EXT_CONFIG_SUPPORT, "ExtendedConfig" }, + { OSC_PCI_ASPM_SUPPORT, "ASPM" }, + { OSC_PCI_CLOCK_PM_SUPPORT, "ClockPM" }, + { OSC_PCI_SEGMENT_GROUPS_SUPPORT, "Segments" }, + { OSC_PCI_MSI_SUPPORT, "MSI" }, +}; + +static struct pci_osc_bit_struct pci_osc_control_bit[] = { + { OSC_PCI_EXPRESS_NATIVE_HP_CONTROL, "PCIeHotplug" }, + { OSC_PCI_SHPC_NATIVE_HP_CONTROL, "SHPCHotplug" }, + { OSC_PCI_EXPRESS_PME_CONTROL, "PME" }, + { OSC_PCI_EXPRESS_AER_CONTROL, "AER" }, + { OSC_PCI_EXPRESS_CAPABILITY_CONTROL, "PCIeCapability" }, +}; + +static void decode_osc_bits(struct acpi_pci_root *root, char *msg, u32 word, + struct pci_osc_bit_struct *table, int size) +{ + char buf[80]; + int i, len = 0; + struct pci_osc_bit_struct *entry; + + buf[0] = '\0'; + for (i = 0, entry = table; i < size; i++, entry++) + if (word & entry->bit) + len += snprintf(buf + len, sizeof(buf) - len, "%s%s", + len ? " " : "", entry->desc); + + dev_info(&root->device->dev, "_OSC: %s [%s]\n", msg, buf); +} + +static void decode_osc_support(struct acpi_pci_root *root, char *msg, u32 word) +{ + decode_osc_bits(root, msg, word, pci_osc_support_bit, + ARRAY_SIZE(pci_osc_support_bit)); +} + +static void decode_osc_control(struct acpi_pci_root *root, char *msg, u32 word) +{ + decode_osc_bits(root, msg, word, pci_osc_control_bit, + ARRAY_SIZE(pci_osc_control_bit)); +} + +static u8 pci_osc_uuid_str[] = "33DB4D5B-1FF7-401C-9657-7441C03DD766"; + +static acpi_status acpi_pci_run_osc(acpi_handle handle, + const u32 *capbuf, u32 *retval) +{ + struct acpi_osc_context context = { + .uuid_str = pci_osc_uuid_str, + .rev = 1, + .cap.length = 12, + .cap.pointer = (void *)capbuf, + }; + acpi_status status; + + status = acpi_run_osc(handle, &context); + if (ACPI_SUCCESS(status)) { + *retval = *((u32 *)(context.ret.pointer + 8)); + kfree(context.ret.pointer); + } + return status; +} + +static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, + u32 support, + u32 *control) +{ + acpi_status status; + u32 result, capbuf[3]; + + support &= OSC_PCI_SUPPORT_MASKS; + support |= root->osc_support_set; + + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_DWORD] = support; + if (control) { + *control &= OSC_PCI_CONTROL_MASKS; + capbuf[OSC_CONTROL_DWORD] = *control | root->osc_control_set; + } else { + /* Run _OSC query only with existing controls. */ + capbuf[OSC_CONTROL_DWORD] = root->osc_control_set; + } + + status = acpi_pci_run_osc(root->device->handle, capbuf, &result); + if (ACPI_SUCCESS(status)) { + root->osc_support_set = support; + if (control) + *control = result; + } + return status; +} + +static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) +{ + acpi_status status; + + mutex_lock(&osc_lock); + status = acpi_pci_query_osc(root, flags, NULL); + mutex_unlock(&osc_lock); + return status; +} + +struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) +{ + struct acpi_pci_root *root; + struct acpi_device *device; + + if (acpi_bus_get_device(handle, &device) || + acpi_match_device_ids(device, root_device_ids)) + return NULL; + + root = acpi_driver_data(device); + + return root; +} +EXPORT_SYMBOL_GPL(acpi_pci_find_root); + +struct acpi_handle_node { + struct list_head node; + acpi_handle handle; +}; + +/** + * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev + * @handle: the handle in question + * + * Given an ACPI CA handle, the desired PCI device is located in the + * list of PCI devices. + * + * If the device is found, its reference count is increased and this + * function returns a pointer to its data structure. The caller must + * decrement the reference count by calling pci_dev_put(). + * If no device is found, %NULL is returned. + */ +struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +{ + int dev, fn; + unsigned long long adr; + acpi_status status; + acpi_handle phandle; + struct pci_bus *pbus; + struct pci_dev *pdev = NULL; + struct acpi_handle_node *node, *tmp; + struct acpi_pci_root *root; + LIST_HEAD(device_list); + + /* + * Walk up the ACPI CA namespace until we reach a PCI root bridge. + */ + phandle = handle; + while (!acpi_is_root_bridge(phandle)) { + node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL); + if (!node) + goto out; + + INIT_LIST_HEAD(&node->node); + node->handle = phandle; + list_add(&node->node, &device_list); + + status = acpi_get_parent(phandle, &phandle); + if (ACPI_FAILURE(status)) + goto out; + } + + root = acpi_pci_find_root(phandle); + if (!root) + goto out; + + pbus = root->bus; + + /* + * Now, walk back down the PCI device tree until we return to our + * original handle. Assumes that everything between the PCI root + * bridge and the device we're looking for must be a P2P bridge. + */ + list_for_each_entry(node, &device_list, node) { + acpi_handle hnd = node->handle; + status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr); + if (ACPI_FAILURE(status)) + goto out; + dev = (adr >> 16) & 0xffff; + fn = adr & 0xffff; + + pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn)); + if (!pdev || hnd == handle) + break; + + pbus = pdev->subordinate; + pci_dev_put(pdev); + + /* + * This function may be called for a non-PCI device that has a + * PCI parent (eg. a disk under a PCI SATA controller). In that + * case pdev->subordinate will be NULL for the parent. + */ + if (!pbus) { + dev_dbg(&pdev->dev, "Not a PCI-to-PCI bridge\n"); + pdev = NULL; + break; + } + } +out: + list_for_each_entry_safe(node, tmp, &device_list, node) + kfree(node); + + return pdev; +} +EXPORT_SYMBOL_GPL(acpi_get_pci_dev); + +/** + * acpi_pci_osc_control_set - Request control of PCI root _OSC features. + * @handle: ACPI handle of a PCI root bridge (or PCIe Root Complex). + * @mask: Mask of _OSC bits to request control of, place to store control mask. + * @req: Mask of _OSC bits the control of is essential to the caller. + * + * Run _OSC query for @mask and if that is successful, compare the returned + * mask of control bits with @req. If all of the @req bits are set in the + * returned mask, run _OSC request for it. + * + * The variable at the @mask address may be modified regardless of whether or + * not the function returns success. On success it will contain the mask of + * _OSC bits the BIOS has granted control of, but its contents are meaningless + * on failure. + **/ +acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req) +{ + struct acpi_pci_root *root; + acpi_status status = AE_OK; + u32 ctrl, capbuf[3]; + + if (!mask) + return AE_BAD_PARAMETER; + + ctrl = *mask & OSC_PCI_CONTROL_MASKS; + if ((ctrl & req) != req) + return AE_TYPE; + + root = acpi_pci_find_root(handle); + if (!root) + return AE_NOT_EXIST; + + mutex_lock(&osc_lock); + + *mask = ctrl | root->osc_control_set; + /* No need to evaluate _OSC if the control was already granted. */ + if ((root->osc_control_set & ctrl) == ctrl) + goto out; + + /* Need to check the available controls bits before requesting them. */ + while (*mask) { + status = acpi_pci_query_osc(root, root->osc_support_set, mask); + if (ACPI_FAILURE(status)) + goto out; + if (ctrl == *mask) + break; + decode_osc_control(root, "platform does not support", + ctrl & ~(*mask)); + ctrl = *mask; + } + + if ((ctrl & req) != req) { + decode_osc_control(root, "not requesting control; platform does not support", + req & ~(ctrl)); + status = AE_SUPPORT; + goto out; + } + + capbuf[OSC_QUERY_DWORD] = 0; + capbuf[OSC_SUPPORT_DWORD] = root->osc_support_set; + capbuf[OSC_CONTROL_DWORD] = ctrl; + status = acpi_pci_run_osc(handle, capbuf, mask); + if (ACPI_SUCCESS(status)) + root->osc_control_set = *mask; +out: + mutex_unlock(&osc_lock); + return status; +} +EXPORT_SYMBOL(acpi_pci_osc_control_set); + +static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm) +{ + u32 support, control, requested; + acpi_status status; + struct acpi_device *device = root->device; + acpi_handle handle = device->handle; + + /* + * Apple always return failure on _OSC calls when _OSI("Darwin") has + * been called successfully. We know the feature set supported by the + * platform, so avoid calling _OSC at all + */ + + if (dmi_match(DMI_SYS_VENDOR, "Apple Inc.")) { + root->osc_control_set = ~OSC_PCI_EXPRESS_PME_CONTROL; + decode_osc_control(root, "OS assumes control of", + root->osc_control_set); + return; + } + + /* + * All supported architectures that use ACPI have support for + * PCI domains, so we indicate this in _OSC support capabilities. + */ + support = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + if (pci_ext_cfg_avail()) + support |= OSC_PCI_EXT_CONFIG_SUPPORT; + if (pcie_aspm_support_enabled()) + support |= OSC_PCI_ASPM_SUPPORT | OSC_PCI_CLOCK_PM_SUPPORT; + if (pci_msi_enabled()) + support |= OSC_PCI_MSI_SUPPORT; + + decode_osc_support(root, "OS supports", support); + status = acpi_pci_osc_support(root, support); + if (ACPI_FAILURE(status)) { + dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n", + acpi_format_exception(status)); + *no_aspm = 1; + return; + } + + if (pcie_ports_disabled) { + dev_info(&device->dev, "PCIe port services disabled; not requesting _OSC control\n"); + return; + } + + if ((support & ACPI_PCIE_REQ_SUPPORT) != ACPI_PCIE_REQ_SUPPORT) { + decode_osc_support(root, "not requesting OS control; OS requires", + ACPI_PCIE_REQ_SUPPORT); + return; + } + + control = OSC_PCI_EXPRESS_CAPABILITY_CONTROL + | OSC_PCI_EXPRESS_NATIVE_HP_CONTROL + | OSC_PCI_EXPRESS_PME_CONTROL; + + if (pci_aer_available()) { + if (aer_acpi_firmware_first()) + dev_info(&device->dev, + "PCIe AER handled by firmware\n"); + else + control |= OSC_PCI_EXPRESS_AER_CONTROL; + } + + requested = control; + status = acpi_pci_osc_control_set(handle, &control, + OSC_PCI_EXPRESS_CAPABILITY_CONTROL); + if (ACPI_SUCCESS(status)) { + decode_osc_control(root, "OS now controls", control); + if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { + /* + * We have ASPM control, but the FADT indicates that + * it's unsupported. Leave existing configuration + * intact and prevent the OS from touching it. + */ + dev_info(&device->dev, "FADT indicates ASPM is unsupported, using BIOS configuration\n"); + *no_aspm = 1; + } + } else { + decode_osc_control(root, "OS requested", requested); + decode_osc_control(root, "platform willing to grant", control); + dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n", + acpi_format_exception(status)); + + /* + * We want to disable ASPM here, but aspm_disabled + * needs to remain in its state from boot so that we + * properly handle PCIe 1.1 devices. So we set this + * flag here, to defer the action until after the ACPI + * root scan. + */ + *no_aspm = 1; + } +} + +static int acpi_pci_root_add(struct acpi_device *device, + const struct acpi_device_id *not_used) +{ + unsigned long long segment, bus; + acpi_status status; + int result; + struct acpi_pci_root *root; + acpi_handle handle = device->handle; + int no_aspm = 0; + bool hotadd = system_state != SYSTEM_BOOTING; + + root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); + if (!root) + return -ENOMEM; + + segment = 0; + status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, + &segment); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + dev_err(&device->dev, "can't evaluate _SEG\n"); + result = -ENODEV; + goto end; + } + + /* Check _CRS first, then _BBN. If no _BBN, default to zero. */ + root->secondary.flags = IORESOURCE_BUS; + status = try_get_root_bridge_busnr(handle, &root->secondary); + if (ACPI_FAILURE(status)) { + /* + * We need both the start and end of the downstream bus range + * to interpret _CBA (MMCONFIG base address), so it really is + * supposed to be in _CRS. If we don't find it there, all we + * can do is assume [_BBN-0xFF] or [0-0xFF]. + */ + root->secondary.end = 0xFF; + dev_warn(&device->dev, + FW_BUG "no secondary bus range in _CRS\n"); + status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, + NULL, &bus); + if (ACPI_SUCCESS(status)) + root->secondary.start = bus; + else if (status == AE_NOT_FOUND) + root->secondary.start = 0; + else { + dev_err(&device->dev, "can't evaluate _BBN\n"); + result = -ENODEV; + goto end; + } + } + + root->device = device; + root->segment = segment & 0xFFFF; + strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); + device->driver_data = root; + + if (hotadd && dmar_device_add(handle)) { + result = -ENXIO; + goto end; + } + + pr_info(PREFIX "%s [%s] (domain %04x %pR)\n", + acpi_device_name(device), acpi_device_bid(device), + root->segment, &root->secondary); + + root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle); + + negotiate_os_control(root, &no_aspm); + + /* + * TBD: Need PCI interface for enumeration/configuration of roots. + */ + + /* + * Scan the Root Bridge + * -------------------- + * Must do this prior to any attempt to bind the root device, as the + * PCI namespace does not get created until this call is made (and + * thus the root bridge's pci_dev does not exist). + */ + root->bus = pci_acpi_scan_root(root); + if (!root->bus) { + dev_err(&device->dev, + "Bus %04x:%02x not present in PCI namespace\n", + root->segment, (unsigned int)root->secondary.start); + device->driver_data = NULL; + result = -ENODEV; + goto remove_dmar; + } + + if (no_aspm) + pcie_no_aspm(); + + pci_acpi_add_bus_pm_notifier(device); + if (device->wakeup.flags.run_wake) + device_set_run_wake(root->bus->bridge, true); + + if (hotadd) { + pcibios_resource_survey_bus(root->bus); + pci_assign_unassigned_root_bus_resources(root->bus); + acpi_ioapic_add(root); + } + + pci_lock_rescan_remove(); + pci_bus_add_devices(root->bus); + pci_unlock_rescan_remove(); + return 1; + +remove_dmar: + if (hotadd) + dmar_device_remove(handle); +end: + kfree(root); + return result; +} + +static void acpi_pci_root_remove(struct acpi_device *device) +{ + struct acpi_pci_root *root = acpi_driver_data(device); + + pci_lock_rescan_remove(); + + pci_stop_root_bus(root->bus); + + WARN_ON(acpi_ioapic_remove(root)); + + device_set_run_wake(root->bus->bridge, false); + pci_acpi_remove_bus_pm_notifier(device); + + pci_remove_root_bus(root->bus); + + dmar_device_remove(device->handle); + + pci_unlock_rescan_remove(); + + kfree(root); +} + +void __init acpi_pci_root_init(void) +{ + acpi_hest_init(); + if (acpi_pci_disabled) + return; + + pci_acpi_crs_quirks(); + acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root"); +} diff --git a/kernel/drivers/acpi/pci_slot.c b/kernel/drivers/acpi/pci_slot.c new file mode 100644 index 000000000..139d9e479 --- /dev/null +++ b/kernel/drivers/acpi/pci_slot.c @@ -0,0 +1,219 @@ +/* + * pci_slot.c - ACPI PCI Slot Driver + * + * The code here is heavily leveraged from the acpiphp module. + * Thanks to Matthew Wilcox for much guidance. + * Thanks to Kenji Kaneshige for code + * review and fixes. + * + * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. + * Alex Chiang + * + * Copyright (C) 2013 Huawei Tech. Co., Ltd. + * Jiang Liu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool debug; +static int check_sta_before_sun; + +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "Alex Chiang " +#define DRIVER_DESC "ACPI PCI Slot Detection Driver" +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); +module_param(debug, bool, 0644); + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME("pci_slot"); + +#define MY_NAME "pci_slot" +#define err(format, arg...) pr_err("%s: " format , MY_NAME , ## arg) +#define info(format, arg...) pr_info("%s: " format , MY_NAME , ## arg) +#define dbg(format, arg...) \ + do { \ + if (debug) \ + pr_debug("%s: " format, MY_NAME , ## arg); \ + } while (0) + +#define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ + +struct acpi_pci_slot { + struct pci_slot *pci_slot; /* corresponding pci_slot */ + struct list_head list; /* node in the list of slots */ +}; + +static LIST_HEAD(slot_list); +static DEFINE_MUTEX(slot_list_lock); + +static int +check_slot(acpi_handle handle, unsigned long long *sun) +{ + int device = -1; + unsigned long long adr, sta; + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + dbg("Checking slot on path: %s\n", (char *)buffer.pointer); + + if (check_sta_before_sun) { + /* If SxFy doesn't have _STA, we just assume it's there */ + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) + goto out; + } + + status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); + if (ACPI_FAILURE(status)) { + dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer); + goto out; + } + + /* No _SUN == not a slot == bail */ + status = acpi_evaluate_integer(handle, "_SUN", NULL, sun); + if (ACPI_FAILURE(status)) { + dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer); + goto out; + } + + device = (adr >> 16) & 0xffff; +out: + kfree(buffer.pointer); + return device; +} + +/* + * Check whether handle has an associated slot and create PCI slot if it has. + */ +static acpi_status +register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + int device; + unsigned long long sun; + char name[SLOT_NAME_SIZE]; + struct acpi_pci_slot *slot; + struct pci_slot *pci_slot; + struct pci_bus *pci_bus = context; + + device = check_slot(handle, &sun); + if (device < 0) + return AE_OK; + + /* + * There may be multiple PCI functions associated with the same slot. + * Check whether PCI slot has already been created for this PCI device. + */ + list_for_each_entry(slot, &slot_list, list) { + pci_slot = slot->pci_slot; + if (pci_slot->bus == pci_bus && pci_slot->number == device) + return AE_OK; + } + + slot = kmalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) { + err("%s: cannot allocate memory\n", __func__); + return AE_OK; + } + + snprintf(name, sizeof(name), "%llu", sun); + pci_slot = pci_create_slot(pci_bus, device, name, NULL); + if (IS_ERR(pci_slot)) { + err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); + kfree(slot); + return AE_OK; + } + + slot->pci_slot = pci_slot; + list_add(&slot->list, &slot_list); + + get_device(&pci_bus->dev); + + dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n", + pci_slot, pci_bus->number, device, name); + + return AE_OK; +} + +void acpi_pci_slot_enumerate(struct pci_bus *bus) +{ + acpi_handle handle = ACPI_HANDLE(bus->bridge); + + if (handle) { + mutex_lock(&slot_list_lock); + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + register_slot, NULL, bus, NULL); + mutex_unlock(&slot_list_lock); + } +} + +void acpi_pci_slot_remove(struct pci_bus *bus) +{ + struct acpi_pci_slot *slot, *tmp; + + mutex_lock(&slot_list_lock); + list_for_each_entry_safe(slot, tmp, &slot_list, list) { + if (slot->pci_slot->bus == bus) { + list_del(&slot->list); + pci_destroy_slot(slot->pci_slot); + put_device(&bus->dev); + kfree(slot); + } + } + mutex_unlock(&slot_list_lock); +} + +static int do_sta_before_sun(const struct dmi_system_id *d) +{ + info("%s detected: will evaluate _STA before calling _SUN\n", d->ident); + check_sta_before_sun = 1; + return 0; +} + +static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { + /* + * Fujitsu Primequest machines will return 1023 to indicate an + * error if the _SUN method is evaluated on SxFy objects that + * are not present (as indicated by _STA), so for those machines, + * we want to check _STA before evaluating _SUN. + */ + { + .callback = do_sta_before_sun, + .ident = "Fujitsu PRIMEQUEST", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"), + DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"), + }, + }, + {} +}; + +void __init acpi_pci_slot_init(void) +{ + dmi_check_system(acpi_pci_slot_dmi_table); +} diff --git a/kernel/drivers/acpi/pmic/intel_pmic.c b/kernel/drivers/acpi/pmic/intel_pmic.c new file mode 100644 index 000000000..bd772cd56 --- /dev/null +++ b/kernel/drivers/acpi/pmic/intel_pmic.c @@ -0,0 +1,257 @@ +/* + * intel_pmic.c - Intel PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include "intel_pmic.h" + +#define PMIC_POWER_OPREGION_ID 0x8d +#define PMIC_THERMAL_OPREGION_ID 0x8c + +struct intel_pmic_opregion { + struct mutex lock; + struct acpi_lpat_conversion_table *lpat_table; + struct regmap *regmap; + struct intel_pmic_opregion_data *data; +}; + +static int pmic_get_reg_bit(int address, struct pmic_table *table, + int count, int *reg, int *bit) +{ + int i; + + for (i = 0; i < count; i++) { + if (table[i].address == address) { + *reg = table[i].reg; + if (bit) + *bit = table[i].bit; + return 0; + } + } + return -ENOENT; +} + +static acpi_status intel_pmic_power_handler(u32 function, + acpi_physical_address address, u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct intel_pmic_opregion *opregion = region_context; + struct regmap *regmap = opregion->regmap; + struct intel_pmic_opregion_data *d = opregion->data; + int reg, bit, result; + + if (bits != 32 || !value64) + return AE_BAD_PARAMETER; + + if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1)) + return AE_BAD_PARAMETER; + + result = pmic_get_reg_bit(address, d->power_table, + d->power_table_count, ®, &bit); + if (result == -ENOENT) + return AE_BAD_PARAMETER; + + mutex_lock(&opregion->lock); + + result = function == ACPI_READ ? + d->get_power(regmap, reg, bit, value64) : + d->update_power(regmap, reg, bit, *value64 == 1); + + mutex_unlock(&opregion->lock); + + return result ? AE_ERROR : AE_OK; +} + +static int pmic_read_temp(struct intel_pmic_opregion *opregion, + int reg, u64 *value) +{ + int raw_temp, temp; + + if (!opregion->data->get_raw_temp) + return -ENXIO; + + raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg); + if (raw_temp < 0) + return raw_temp; + + if (!opregion->lpat_table) { + *value = raw_temp; + return 0; + } + + temp = acpi_lpat_raw_to_temp(opregion->lpat_table, raw_temp); + if (temp < 0) + return temp; + + *value = temp; + return 0; +} + +static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg, + u32 function, u64 *value) +{ + return function == ACPI_READ ? + pmic_read_temp(opregion, reg, value) : -EINVAL; +} + +static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, + u32 function, u64 *value) +{ + int raw_temp; + + if (function == ACPI_READ) + return pmic_read_temp(opregion, reg, value); + + if (!opregion->data->update_aux) + return -ENXIO; + + if (opregion->lpat_table) { + raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value); + if (raw_temp < 0) + return raw_temp; + } else { + raw_temp = *value; + } + + return opregion->data->update_aux(opregion->regmap, reg, raw_temp); +} + +static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, + u32 function, u64 *value) +{ + struct intel_pmic_opregion_data *d = opregion->data; + struct regmap *regmap = opregion->regmap; + + if (!d->get_policy || !d->update_policy) + return -ENXIO; + + if (function == ACPI_READ) + return d->get_policy(regmap, reg, value); + + if (*value != 0 && *value != 1) + return -EINVAL; + + return d->update_policy(regmap, reg, *value); +} + +static bool pmic_thermal_is_temp(int address) +{ + return (address <= 0x3c) && !(address % 12); +} + +static bool pmic_thermal_is_aux(int address) +{ + return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) || + (address >= 8 && address <= 0x44 && !((address - 8) % 12)); +} + +static bool pmic_thermal_is_pen(int address) +{ + return address >= 0x48 && address <= 0x5c; +} + +static acpi_status intel_pmic_thermal_handler(u32 function, + acpi_physical_address address, u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct intel_pmic_opregion *opregion = region_context; + struct intel_pmic_opregion_data *d = opregion->data; + int reg, result; + + if (bits != 32 || !value64) + return AE_BAD_PARAMETER; + + result = pmic_get_reg_bit(address, d->thermal_table, + d->thermal_table_count, ®, NULL); + if (result == -ENOENT) + return AE_BAD_PARAMETER; + + mutex_lock(&opregion->lock); + + if (pmic_thermal_is_temp(address)) + result = pmic_thermal_temp(opregion, reg, function, value64); + else if (pmic_thermal_is_aux(address)) + result = pmic_thermal_aux(opregion, reg, function, value64); + else if (pmic_thermal_is_pen(address)) + result = pmic_thermal_pen(opregion, reg, function, value64); + else + result = -EINVAL; + + mutex_unlock(&opregion->lock); + + if (result < 0) { + if (result == -EINVAL) + return AE_BAD_PARAMETER; + else + return AE_ERROR; + } + + return AE_OK; +} + +int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, + struct regmap *regmap, + struct intel_pmic_opregion_data *d) +{ + acpi_status status; + struct intel_pmic_opregion *opregion; + int ret; + + if (!dev || !regmap || !d) + return -EINVAL; + + if (!handle) + return -ENODEV; + + opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); + if (!opregion) + return -ENOMEM; + + mutex_init(&opregion->lock); + opregion->regmap = regmap; + opregion->lpat_table = acpi_lpat_get_conversion_table(handle); + + status = acpi_install_address_space_handler(handle, + PMIC_POWER_OPREGION_ID, + intel_pmic_power_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto out_error; + } + + status = acpi_install_address_space_handler(handle, + PMIC_THERMAL_OPREGION_ID, + intel_pmic_thermal_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) { + acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, + intel_pmic_power_handler); + ret = -ENODEV; + goto out_error; + } + + opregion->data = d; + return 0; + +out_error: + acpi_lpat_free_conversion_table(opregion->lpat_table); + return ret; +} +EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); + +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/pmic/intel_pmic.h b/kernel/drivers/acpi/pmic/intel_pmic.h new file mode 100644 index 000000000..d4e90af8f --- /dev/null +++ b/kernel/drivers/acpi/pmic/intel_pmic.h @@ -0,0 +1,25 @@ +#ifndef __INTEL_PMIC_H +#define __INTEL_PMIC_H + +struct pmic_table { + int address; /* operation region address */ + int reg; /* corresponding thermal register */ + int bit; /* control bit for power */ +}; + +struct intel_pmic_opregion_data { + int (*get_power)(struct regmap *r, int reg, int bit, u64 *value); + int (*update_power)(struct regmap *r, int reg, int bit, bool on); + int (*get_raw_temp)(struct regmap *r, int reg); + int (*update_aux)(struct regmap *r, int reg, int raw_temp); + int (*get_policy)(struct regmap *r, int reg, u64 *value); + int (*update_policy)(struct regmap *r, int reg, int enable); + struct pmic_table *power_table; + int power_table_count; + struct pmic_table *thermal_table; + int thermal_table_count; +}; + +int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, struct regmap *regmap, struct intel_pmic_opregion_data *d); + +#endif diff --git a/kernel/drivers/acpi/pmic/intel_pmic_crc.c b/kernel/drivers/acpi/pmic/intel_pmic_crc.c new file mode 100644 index 000000000..42df46a86 --- /dev/null +++ b/kernel/drivers/acpi/pmic/intel_pmic_crc.c @@ -0,0 +1,211 @@ +/* + * intel_pmic_crc.c - Intel CrystalCove PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "intel_pmic.h" + +#define PWR_SOURCE_SELECT BIT(1) + +#define PMIC_A0LOCK_REG 0xc5 + +static struct pmic_table power_table[] = { + { + .address = 0x24, + .reg = 0x66, + .bit = 0x00, + }, + { + .address = 0x48, + .reg = 0x5d, + .bit = 0x00, + }, +}; + +static struct pmic_table thermal_table[] = { + { + .address = 0x00, + .reg = 0x75 + }, + { + .address = 0x04, + .reg = 0x95 + }, + { + .address = 0x08, + .reg = 0x97 + }, + { + .address = 0x0c, + .reg = 0x77 + }, + { + .address = 0x10, + .reg = 0x9a + }, + { + .address = 0x14, + .reg = 0x9c + }, + { + .address = 0x18, + .reg = 0x79 + }, + { + .address = 0x1c, + .reg = 0x9f + }, + { + .address = 0x20, + .reg = 0xa1 + }, + { + .address = 0x48, + .reg = 0x94 + }, + { + .address = 0x4c, + .reg = 0x99 + }, + { + .address = 0x50, + .reg = 0x9e + }, +}; + +static int intel_crc_pmic_get_power(struct regmap *regmap, int reg, + int bit, u64 *value) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = (data & PWR_SOURCE_SELECT) && (data & BIT(bit)) ? 1 : 0; + return 0; +} + +static int intel_crc_pmic_update_power(struct regmap *regmap, int reg, + int bit, bool on) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + if (on) { + data |= PWR_SOURCE_SELECT | BIT(bit); + } else { + data &= ~BIT(bit); + data |= PWR_SOURCE_SELECT; + } + + if (regmap_write(regmap, reg, data)) + return -EIO; + return 0; +} + +static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg) +{ + int temp_l, temp_h; + + /* + * Raw temperature value is 10bits: 8bits in reg + * and 2bits in reg-1: bit0,1 + */ + if (regmap_read(regmap, reg, &temp_l) || + regmap_read(regmap, reg - 1, &temp_h)) + return -EIO; + + return temp_l | (temp_h & 0x3) << 8; +} + +static int intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw) +{ + return regmap_write(regmap, reg, raw) || + regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0; +} + +static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value) +{ + int pen; + + if (regmap_read(regmap, reg, &pen)) + return -EIO; + *value = pen >> 7; + return 0; +} + +static int intel_crc_pmic_update_policy(struct regmap *regmap, + int reg, int enable) +{ + int alert0; + + /* Update to policy enable bit requires unlocking a0lock */ + if (regmap_read(regmap, PMIC_A0LOCK_REG, &alert0)) + return -EIO; + + if (regmap_update_bits(regmap, PMIC_A0LOCK_REG, 0x01, 0)) + return -EIO; + + if (regmap_update_bits(regmap, reg, 0x80, enable << 7)) + return -EIO; + + /* restore alert0 */ + if (regmap_write(regmap, PMIC_A0LOCK_REG, alert0)) + return -EIO; + + return 0; +} + +static struct intel_pmic_opregion_data intel_crc_pmic_opregion_data = { + .get_power = intel_crc_pmic_get_power, + .update_power = intel_crc_pmic_update_power, + .get_raw_temp = intel_crc_pmic_get_raw_temp, + .update_aux = intel_crc_pmic_update_aux, + .get_policy = intel_crc_pmic_get_policy, + .update_policy = intel_crc_pmic_update_policy, + .power_table = power_table, + .power_table_count= ARRAY_SIZE(power_table), + .thermal_table = thermal_table, + .thermal_table_count = ARRAY_SIZE(thermal_table), +}; + +static int intel_crc_pmic_opregion_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + return intel_pmic_install_opregion_handler(&pdev->dev, + ACPI_HANDLE(pdev->dev.parent), pmic->regmap, + &intel_crc_pmic_opregion_data); +} + +static struct platform_driver intel_crc_pmic_opregion_driver = { + .probe = intel_crc_pmic_opregion_probe, + .driver = { + .name = "crystal_cove_pmic", + }, +}; + +static int __init intel_crc_pmic_opregion_driver_init(void) +{ + return platform_driver_register(&intel_crc_pmic_opregion_driver); +} +module_init(intel_crc_pmic_opregion_driver_init); + +MODULE_DESCRIPTION("CrystalCove ACPI operation region driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/pmic/intel_pmic_xpower.c b/kernel/drivers/acpi/pmic/intel_pmic_xpower.c new file mode 100644 index 000000000..6a082d4de --- /dev/null +++ b/kernel/drivers/acpi/pmic/intel_pmic_xpower.c @@ -0,0 +1,268 @@ +/* + * intel_pmic_xpower.c - XPower AXP288 PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "intel_pmic.h" + +#define XPOWER_GPADC_LOW 0x5b + +static struct pmic_table power_table[] = { + { + .address = 0x00, + .reg = 0x13, + .bit = 0x05, + }, + { + .address = 0x04, + .reg = 0x13, + .bit = 0x06, + }, + { + .address = 0x08, + .reg = 0x13, + .bit = 0x07, + }, + { + .address = 0x0c, + .reg = 0x12, + .bit = 0x03, + }, + { + .address = 0x10, + .reg = 0x12, + .bit = 0x04, + }, + { + .address = 0x14, + .reg = 0x12, + .bit = 0x05, + }, + { + .address = 0x18, + .reg = 0x12, + .bit = 0x06, + }, + { + .address = 0x1c, + .reg = 0x12, + .bit = 0x00, + }, + { + .address = 0x20, + .reg = 0x12, + .bit = 0x01, + }, + { + .address = 0x24, + .reg = 0x12, + .bit = 0x02, + }, + { + .address = 0x28, + .reg = 0x13, + .bit = 0x02, + }, + { + .address = 0x2c, + .reg = 0x13, + .bit = 0x03, + }, + { + .address = 0x30, + .reg = 0x13, + .bit = 0x04, + }, + { + .address = 0x38, + .reg = 0x10, + .bit = 0x03, + }, + { + .address = 0x3c, + .reg = 0x10, + .bit = 0x06, + }, + { + .address = 0x40, + .reg = 0x10, + .bit = 0x05, + }, + { + .address = 0x44, + .reg = 0x10, + .bit = 0x04, + }, + { + .address = 0x48, + .reg = 0x10, + .bit = 0x01, + }, + { + .address = 0x4c, + .reg = 0x10, + .bit = 0x00 + }, +}; + +/* TMP0 - TMP5 are the same, all from GPADC */ +static struct pmic_table thermal_table[] = { + { + .address = 0x00, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x0c, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x18, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x24, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x30, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x3c, + .reg = XPOWER_GPADC_LOW + }, +}; + +static int intel_xpower_pmic_get_power(struct regmap *regmap, int reg, + int bit, u64 *value) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = (data & BIT(bit)) ? 1 : 0; + return 0; +} + +static int intel_xpower_pmic_update_power(struct regmap *regmap, int reg, + int bit, bool on) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + if (on) + data |= BIT(bit); + else + data &= ~BIT(bit); + + if (regmap_write(regmap, reg, data)) + return -EIO; + + return 0; +} + +/** + * intel_xpower_pmic_get_raw_temp(): Get raw temperature reading from the PMIC + * + * @regmap: regmap of the PMIC device + * @reg: register to get the reading + * + * We could get the sensor value by manipulating the HW regs here, but since + * the axp288 IIO driver may also access the same regs at the same time, the + * APIs provided by IIO subsystem are used here instead to avoid problems. As + * a result, the two passed in params are of no actual use. + * + * Return a positive value on success, errno on failure. + */ +static int intel_xpower_pmic_get_raw_temp(struct regmap *regmap, int reg) +{ + struct iio_channel *gpadc_chan; + int ret, val; + + gpadc_chan = iio_channel_get(NULL, "axp288-system-temp"); + if (IS_ERR_OR_NULL(gpadc_chan)) + return -EACCES; + + ret = iio_read_channel_raw(gpadc_chan, &val); + if (ret < 0) + val = ret; + + iio_channel_release(gpadc_chan); + return val; +} + +static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = { + .get_power = intel_xpower_pmic_get_power, + .update_power = intel_xpower_pmic_update_power, + .get_raw_temp = intel_xpower_pmic_get_raw_temp, + .power_table = power_table, + .power_table_count = ARRAY_SIZE(power_table), + .thermal_table = thermal_table, + .thermal_table_count = ARRAY_SIZE(thermal_table), +}; + +static acpi_status intel_xpower_pmic_gpio_handler(u32 function, + acpi_physical_address address, u32 bit_width, u64 *value, + void *handler_context, void *region_context) +{ + return AE_OK; +} + +static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev) +{ + struct device *parent = pdev->dev.parent; + struct axp20x_dev *axp20x = dev_get_drvdata(parent); + acpi_status status; + int result; + + status = acpi_install_address_space_handler(ACPI_HANDLE(parent), + ACPI_ADR_SPACE_GPIO, intel_xpower_pmic_gpio_handler, + NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + result = intel_pmic_install_opregion_handler(&pdev->dev, + ACPI_HANDLE(parent), axp20x->regmap, + &intel_xpower_pmic_opregion_data); + if (result) + acpi_remove_address_space_handler(ACPI_HANDLE(parent), + ACPI_ADR_SPACE_GPIO, + intel_xpower_pmic_gpio_handler); + + return result; +} + +static struct platform_driver intel_xpower_pmic_opregion_driver = { + .probe = intel_xpower_pmic_opregion_probe, + .driver = { + .name = "axp288_pmic_acpi", + }, +}; + +static int __init intel_xpower_pmic_opregion_driver_init(void) +{ + return platform_driver_register(&intel_xpower_pmic_opregion_driver); +} +module_init(intel_xpower_pmic_opregion_driver_init); + +MODULE_DESCRIPTION("XPower AXP288 ACPI operation region driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/acpi/power.c b/kernel/drivers/acpi/power.c new file mode 100644 index 000000000..e0bcfb642 --- /dev/null +++ b/kernel/drivers/acpi/power.c @@ -0,0 +1,858 @@ +/* + * acpi_power.c - ACPI Bus Power Management ($Revision: 39 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* + * ACPI power-managed devices may be controlled in two ways: + * 1. via "Device Specific (D-State) Control" + * 2. via "Power Resource Control". + * This module is used to manage devices relying on Power Resource Control. + * + * An ACPI "power resource object" describes a software controllable power + * plane, clock plane, or other resource used by a power managed device. + * A device may rely on multiple power resources, and a power resource + * may be shared by multiple devices. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sleep.h" +#include "internal.h" + +#define _COMPONENT ACPI_POWER_COMPONENT +ACPI_MODULE_NAME("power"); +#define ACPI_POWER_CLASS "power_resource" +#define ACPI_POWER_DEVICE_NAME "Power Resource" +#define ACPI_POWER_FILE_INFO "info" +#define ACPI_POWER_FILE_STATUS "state" +#define ACPI_POWER_RESOURCE_STATE_OFF 0x00 +#define ACPI_POWER_RESOURCE_STATE_ON 0x01 +#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF + +struct acpi_power_resource { + struct acpi_device device; + struct list_head list_node; + char *name; + u32 system_level; + u32 order; + unsigned int ref_count; + bool wakeup_enabled; + struct mutex resource_lock; +}; + +struct acpi_power_resource_entry { + struct list_head node; + struct acpi_power_resource *resource; +}; + +static LIST_HEAD(acpi_power_resource_list); +static DEFINE_MUTEX(power_resource_list_lock); + +/* -------------------------------------------------------------------------- + Power Resource Management + -------------------------------------------------------------------------- */ + +static inline +struct acpi_power_resource *to_power_resource(struct acpi_device *device) +{ + return container_of(device, struct acpi_power_resource, device); +} + +static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) +{ + struct acpi_device *device; + + if (acpi_bus_get_device(handle, &device)) + return NULL; + + return to_power_resource(device); +} + +static int acpi_power_resources_list_add(acpi_handle handle, + struct list_head *list) +{ + struct acpi_power_resource *resource = acpi_power_get_context(handle); + struct acpi_power_resource_entry *entry; + + if (!resource || !list) + return -EINVAL; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->resource = resource; + if (!list_empty(list)) { + struct acpi_power_resource_entry *e; + + list_for_each_entry(e, list, node) + if (e->resource->order > resource->order) { + list_add_tail(&entry->node, &e->node); + return 0; + } + } + list_add_tail(&entry->node, list); + return 0; +} + +void acpi_power_resources_list_free(struct list_head *list) +{ + struct acpi_power_resource_entry *entry, *e; + + list_for_each_entry_safe(entry, e, list, node) { + list_del(&entry->node); + kfree(entry); + } +} + +int acpi_extract_power_resources(union acpi_object *package, unsigned int start, + struct list_head *list) +{ + unsigned int i; + int err = 0; + + for (i = start; i < package->package.count; i++) { + union acpi_object *element = &package->package.elements[i]; + acpi_handle rhandle; + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { + err = -ENODATA; + break; + } + rhandle = element->reference.handle; + if (!rhandle) { + err = -ENODEV; + break; + } + err = acpi_add_power_resource(rhandle); + if (err) + break; + + err = acpi_power_resources_list_add(rhandle, list); + if (err) + break; + } + if (err) + acpi_power_resources_list_free(list); + + return err; +} + +static int acpi_power_get_state(acpi_handle handle, int *state) +{ + acpi_status status = AE_OK; + unsigned long long sta = 0; + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; + + + if (!handle || !state) + return -EINVAL; + + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + + *state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON: + ACPI_POWER_RESOURCE_STATE_OFF; + + acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n", + node_name, + *state ? "on" : "off")); + + return 0; +} + +static int acpi_power_get_list_state(struct list_head *list, int *state) +{ + struct acpi_power_resource_entry *entry; + int cur_state; + + if (!list || !state) + return -EINVAL; + + /* The state of the list is 'on' IFF all resources are 'on'. */ + list_for_each_entry(entry, list, node) { + struct acpi_power_resource *resource = entry->resource; + acpi_handle handle = resource->device.handle; + int result; + + mutex_lock(&resource->resource_lock); + result = acpi_power_get_state(handle, &cur_state); + mutex_unlock(&resource->resource_lock); + if (result) + return result; + + if (cur_state != ACPI_POWER_RESOURCE_STATE_ON) + break; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n", + cur_state ? "on" : "off")); + + *state = cur_state; + return 0; +} + +static int __acpi_power_on(struct acpi_power_resource *resource) +{ + acpi_status status = AE_OK; + + status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", + resource->name)); + + return 0; +} + +static int acpi_power_on_unlocked(struct acpi_power_resource *resource) +{ + int result = 0; + + if (resource->ref_count++) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] already on\n", + resource->name)); + } else { + result = __acpi_power_on(resource); + if (result) + resource->ref_count--; + } + return result; +} + +static int acpi_power_on(struct acpi_power_resource *resource) +{ + int result; + + mutex_lock(&resource->resource_lock); + result = acpi_power_on_unlocked(resource); + mutex_unlock(&resource->resource_lock); + return result; +} + +static int __acpi_power_off(struct acpi_power_resource *resource) +{ + acpi_status status; + + status = acpi_evaluate_object(resource->device.handle, "_OFF", + NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned off\n", + resource->name)); + return 0; +} + +static int acpi_power_off_unlocked(struct acpi_power_resource *resource) +{ + int result = 0; + + if (!resource->ref_count) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] already off\n", + resource->name)); + return 0; + } + + if (--resource->ref_count) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Power resource [%s] still in use\n", + resource->name)); + } else { + result = __acpi_power_off(resource); + if (result) + resource->ref_count++; + } + return result; +} + +static int acpi_power_off(struct acpi_power_resource *resource) +{ + int result; + + mutex_lock(&resource->resource_lock); + result = acpi_power_off_unlocked(resource); + mutex_unlock(&resource->resource_lock); + return result; +} + +static int acpi_power_off_list(struct list_head *list) +{ + struct acpi_power_resource_entry *entry; + int result = 0; + + list_for_each_entry_reverse(entry, list, node) { + result = acpi_power_off(entry->resource); + if (result) + goto err; + } + return 0; + + err: + list_for_each_entry_continue(entry, list, node) + acpi_power_on(entry->resource); + + return result; +} + +static int acpi_power_on_list(struct list_head *list) +{ + struct acpi_power_resource_entry *entry; + int result = 0; + + list_for_each_entry(entry, list, node) { + result = acpi_power_on(entry->resource); + if (result) + goto err; + } + return 0; + + err: + list_for_each_entry_continue_reverse(entry, list, node) + acpi_power_off(entry->resource); + + return result; +} + +static struct attribute *attrs[] = { + NULL, +}; + +static struct attribute_group attr_groups[] = { + [ACPI_STATE_D0] = { + .name = "power_resources_D0", + .attrs = attrs, + }, + [ACPI_STATE_D1] = { + .name = "power_resources_D1", + .attrs = attrs, + }, + [ACPI_STATE_D2] = { + .name = "power_resources_D2", + .attrs = attrs, + }, + [ACPI_STATE_D3_HOT] = { + .name = "power_resources_D3hot", + .attrs = attrs, + }, +}; + +static struct attribute_group wakeup_attr_group = { + .name = "power_resources_wakeup", + .attrs = attrs, +}; + +static void acpi_power_hide_list(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group) +{ + struct acpi_power_resource_entry *entry; + + if (list_empty(resources)) + return; + + list_for_each_entry_reverse(entry, resources, node) { + struct acpi_device *res_dev = &entry->resource->device; + + sysfs_remove_link_from_group(&adev->dev.kobj, + attr_group->name, + dev_name(&res_dev->dev)); + } + sysfs_remove_group(&adev->dev.kobj, attr_group); +} + +static void acpi_power_expose_list(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group) +{ + struct acpi_power_resource_entry *entry; + int ret; + + if (list_empty(resources)) + return; + + ret = sysfs_create_group(&adev->dev.kobj, attr_group); + if (ret) + return; + + list_for_each_entry(entry, resources, node) { + struct acpi_device *res_dev = &entry->resource->device; + + ret = sysfs_add_link_to_group(&adev->dev.kobj, + attr_group->name, + &res_dev->dev.kobj, + dev_name(&res_dev->dev)); + if (ret) { + acpi_power_hide_list(adev, resources, attr_group); + break; + } + } +} + +static void acpi_power_expose_hide(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group, + bool expose) +{ + if (expose) + acpi_power_expose_list(adev, resources, attr_group); + else + acpi_power_hide_list(adev, resources, attr_group); +} + +void acpi_power_add_remove_device(struct acpi_device *adev, bool add) +{ + int state; + + if (adev->wakeup.flags.valid) + acpi_power_expose_hide(adev, &adev->wakeup.resources, + &wakeup_attr_group, add); + + if (!adev->power.flags.power_resources) + return; + + for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) + acpi_power_expose_hide(adev, + &adev->power.states[state].resources, + &attr_groups[state], add); +} + +int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) +{ + struct acpi_power_resource_entry *entry; + int system_level = 5; + + list_for_each_entry(entry, list, node) { + struct acpi_power_resource *resource = entry->resource; + acpi_handle handle = resource->device.handle; + int result; + int state; + + mutex_lock(&resource->resource_lock); + + result = acpi_power_get_state(handle, &state); + if (result) { + mutex_unlock(&resource->resource_lock); + return result; + } + if (state == ACPI_POWER_RESOURCE_STATE_ON) { + resource->ref_count++; + resource->wakeup_enabled = true; + } + if (system_level > resource->system_level) + system_level = resource->system_level; + + mutex_unlock(&resource->resource_lock); + } + *system_level_p = system_level; + return 0; +} + +/* -------------------------------------------------------------------------- + Device Power Management + -------------------------------------------------------------------------- */ + +/** + * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in + * ACPI 3.0) _PSW (Power State Wake) + * @dev: Device to handle. + * @enable: 0 - disable, 1 - enable the wake capabilities of the device. + * @sleep_state: Target sleep state of the system. + * @dev_state: Target power state of the device. + * + * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power + * State Wake) for the device, if present. On failure reset the device's + * wakeup.flags.valid flag. + * + * RETURN VALUE: + * 0 if either _DSW or _PSW has been successfully executed + * 0 if neither _DSW nor _PSW has been found + * -ENODEV if the execution of either _DSW or _PSW has failed + */ +int acpi_device_sleep_wake(struct acpi_device *dev, + int enable, int sleep_state, int dev_state) +{ + union acpi_object in_arg[3]; + struct acpi_object_list arg_list = { 3, in_arg }; + acpi_status status = AE_OK; + + /* + * Try to execute _DSW first. + * + * Three agruments are needed for the _DSW object: + * Argument 0: enable/disable the wake capabilities + * Argument 1: target system state + * Argument 2: target device state + * When _DSW object is called to disable the wake capabilities, maybe + * the first argument is filled. The values of the other two agruments + * are meaningless. + */ + in_arg[0].type = ACPI_TYPE_INTEGER; + in_arg[0].integer.value = enable; + in_arg[1].type = ACPI_TYPE_INTEGER; + in_arg[1].integer.value = sleep_state; + in_arg[2].type = ACPI_TYPE_INTEGER; + in_arg[2].integer.value = dev_state; + status = acpi_evaluate_object(dev->handle, "_DSW", &arg_list, NULL); + if (ACPI_SUCCESS(status)) { + return 0; + } else if (status != AE_NOT_FOUND) { + printk(KERN_ERR PREFIX "_DSW execution failed\n"); + dev->wakeup.flags.valid = 0; + return -ENODEV; + } + + /* Execute _PSW */ + status = acpi_execute_simple_method(dev->handle, "_PSW", enable); + if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { + printk(KERN_ERR PREFIX "_PSW execution failed\n"); + dev->wakeup.flags.valid = 0; + return -ENODEV; + } + + return 0; +} + +/* + * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229): + * 1. Power on the power resources required for the wakeup device + * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power + * State Wake) for the device, if present + */ +int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) +{ + struct acpi_power_resource_entry *entry; + int err = 0; + + if (!dev || !dev->wakeup.flags.valid) + return -EINVAL; + + mutex_lock(&acpi_device_lock); + + if (dev->wakeup.prepare_count++) + goto out; + + list_for_each_entry(entry, &dev->wakeup.resources, node) { + struct acpi_power_resource *resource = entry->resource; + + mutex_lock(&resource->resource_lock); + + if (!resource->wakeup_enabled) { + err = acpi_power_on_unlocked(resource); + if (!err) + resource->wakeup_enabled = true; + } + + mutex_unlock(&resource->resource_lock); + + if (err) { + dev_err(&dev->dev, + "Cannot turn wakeup power resources on\n"); + dev->wakeup.flags.valid = 0; + goto out; + } + } + /* + * Passing 3 as the third argument below means the device may be + * put into arbitrary power state afterward. + */ + err = acpi_device_sleep_wake(dev, 1, sleep_state, 3); + if (err) + dev->wakeup.prepare_count = 0; + + out: + mutex_unlock(&acpi_device_lock); + return err; +} + +/* + * Shutdown a wakeup device, counterpart of above method + * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power + * State Wake) for the device, if present + * 2. Shutdown down the power resources + */ +int acpi_disable_wakeup_device_power(struct acpi_device *dev) +{ + struct acpi_power_resource_entry *entry; + int err = 0; + + if (!dev || !dev->wakeup.flags.valid) + return -EINVAL; + + mutex_lock(&acpi_device_lock); + + if (--dev->wakeup.prepare_count > 0) + goto out; + + /* + * Executing the code below even if prepare_count is already zero when + * the function is called may be useful, for example for initialisation. + */ + if (dev->wakeup.prepare_count < 0) + dev->wakeup.prepare_count = 0; + + err = acpi_device_sleep_wake(dev, 0, 0, 0); + if (err) + goto out; + + list_for_each_entry(entry, &dev->wakeup.resources, node) { + struct acpi_power_resource *resource = entry->resource; + + mutex_lock(&resource->resource_lock); + + if (resource->wakeup_enabled) { + err = acpi_power_off_unlocked(resource); + if (!err) + resource->wakeup_enabled = false; + } + + mutex_unlock(&resource->resource_lock); + + if (err) { + dev_err(&dev->dev, + "Cannot turn wakeup power resources off\n"); + dev->wakeup.flags.valid = 0; + break; + } + } + + out: + mutex_unlock(&acpi_device_lock); + return err; +} + +int acpi_power_get_inferred_state(struct acpi_device *device, int *state) +{ + int result = 0; + int list_state = 0; + int i = 0; + + if (!device || !state) + return -EINVAL; + + /* + * We know a device's inferred power state when all the resources + * required for a given D-state are 'on'. + */ + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { + struct list_head *list = &device->power.states[i].resources; + + if (list_empty(list)) + continue; + + result = acpi_power_get_list_state(list, &list_state); + if (result) + return result; + + if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { + *state = i; + return 0; + } + } + + *state = ACPI_STATE_D3_COLD; + return 0; +} + +int acpi_power_on_resources(struct acpi_device *device, int state) +{ + if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_HOT) + return -EINVAL; + + return acpi_power_on_list(&device->power.states[state].resources); +} + +int acpi_power_transition(struct acpi_device *device, int state) +{ + int result = 0; + + if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) + return -EINVAL; + + if (device->power.state == state || !device->flags.power_manageable) + return 0; + + if ((device->power.state < ACPI_STATE_D0) + || (device->power.state > ACPI_STATE_D3_COLD)) + return -ENODEV; + + /* TBD: Resources must be ordered. */ + + /* + * First we reference all power resources required in the target list + * (e.g. so the device doesn't lose power while transitioning). Then, + * we dereference all power resources used in the current list. + */ + if (state < ACPI_STATE_D3_COLD) + result = acpi_power_on_list( + &device->power.states[state].resources); + + if (!result && device->power.state < ACPI_STATE_D3_COLD) + acpi_power_off_list( + &device->power.states[device->power.state].resources); + + /* We shouldn't change the state unless the above operations succeed. */ + device->power.state = result ? ACPI_STATE_UNKNOWN : state; + + return result; +} + +static void acpi_release_power_resource(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct acpi_power_resource *resource; + + resource = container_of(device, struct acpi_power_resource, device); + + mutex_lock(&power_resource_list_lock); + list_del(&resource->list_node); + mutex_unlock(&power_resource_list_lock); + + acpi_free_pnp_ids(&device->pnp); + kfree(resource); +} + +static ssize_t acpi_power_in_use_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_power_resource *resource; + + resource = to_power_resource(to_acpi_device(dev)); + return sprintf(buf, "%u\n", !!resource->ref_count); +} +static DEVICE_ATTR(resource_in_use, 0444, acpi_power_in_use_show, NULL); + +static void acpi_power_sysfs_remove(struct acpi_device *device) +{ + device_remove_file(&device->dev, &dev_attr_resource_in_use); +} + +int acpi_add_power_resource(acpi_handle handle) +{ + struct acpi_power_resource *resource; + struct acpi_device *device = NULL; + union acpi_object acpi_object; + struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; + acpi_status status; + int state, result = -ENODEV; + + acpi_bus_get_device(handle, &device); + if (device) + return 0; + + resource = kzalloc(sizeof(*resource), GFP_KERNEL); + if (!resource) + return -ENOMEM; + + device = &resource->device; + acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER, + ACPI_STA_DEFAULT); + mutex_init(&resource->resource_lock); + INIT_LIST_HEAD(&resource->list_node); + resource->name = device->pnp.bus_id; + strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_POWER_CLASS); + device->power.state = ACPI_STATE_UNKNOWN; + + /* Evalute the object to get the system level and resource order. */ + status = acpi_evaluate_object(handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) + goto err; + + resource->system_level = acpi_object.power_resource.system_level; + resource->order = acpi_object.power_resource.resource_order; + + result = acpi_power_get_state(handle, &state); + if (result) + goto err; + + printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), + acpi_device_bid(device), state ? "on" : "off"); + + device->flags.match_driver = true; + result = acpi_device_add(device, acpi_release_power_resource); + if (result) + goto err; + + if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) + device->remove = acpi_power_sysfs_remove; + + mutex_lock(&power_resource_list_lock); + list_add(&resource->list_node, &acpi_power_resource_list); + mutex_unlock(&power_resource_list_lock); + acpi_device_add_finalize(device); + return 0; + + err: + acpi_release_power_resource(&device->dev); + return result; +} + +#ifdef CONFIG_ACPI_SLEEP +void acpi_resume_power_resources(void) +{ + struct acpi_power_resource *resource; + + mutex_lock(&power_resource_list_lock); + + list_for_each_entry(resource, &acpi_power_resource_list, list_node) { + int result, state; + + mutex_lock(&resource->resource_lock); + + result = acpi_power_get_state(resource->device.handle, &state); + if (result) { + mutex_unlock(&resource->resource_lock); + continue; + } + + if (state == ACPI_POWER_RESOURCE_STATE_OFF + && resource->ref_count) { + dev_info(&resource->device.dev, "Turning ON\n"); + __acpi_power_on(resource); + } else if (state == ACPI_POWER_RESOURCE_STATE_ON + && !resource->ref_count) { + dev_info(&resource->device.dev, "Turning OFF\n"); + __acpi_power_off(resource); + } + + mutex_unlock(&resource->resource_lock); + } + + mutex_unlock(&power_resource_list_lock); +} +#endif diff --git a/kernel/drivers/acpi/proc.c b/kernel/drivers/acpi/proc.c new file mode 100644 index 000000000..75c28eae8 --- /dev/null +++ b/kernel/drivers/acpi/proc.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "sleep.h" +#include "internal.h" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT + +/* + * this file provides support for: + * /proc/acpi/wakeup + */ + +ACPI_MODULE_NAME("sleep") + +static int +acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) +{ + struct list_head *node, *next; + + seq_printf(seq, "Device\tS-state\t Status Sysfs node\n"); + + mutex_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + struct acpi_device_physical_node *entry; + + if (!dev->wakeup.flags.valid) + continue; + + seq_printf(seq, "%s\t S%d\t", + dev->pnp.bus_id, + (u32) dev->wakeup.sleep_state); + + mutex_lock(&dev->physical_node_lock); + + if (!dev->physical_node_count) { + seq_printf(seq, "%c%-8s\n", + dev->wakeup.flags.run_wake ? '*' : ' ', + device_may_wakeup(&dev->dev) ? + "enabled" : "disabled"); + } else { + struct device *ldev; + list_for_each_entry(entry, &dev->physical_node_list, + node) { + ldev = get_device(entry->dev); + if (!ldev) + continue; + + if (&entry->node != + dev->physical_node_list.next) + seq_printf(seq, "\t\t"); + + seq_printf(seq, "%c%-8s %s:%s\n", + dev->wakeup.flags.run_wake ? '*' : ' ', + (device_may_wakeup(&dev->dev) || + device_may_wakeup(ldev)) ? + "enabled" : "disabled", + ldev->bus ? ldev->bus->name : + "no-bus", dev_name(ldev)); + put_device(ldev); + } + } + + mutex_unlock(&dev->physical_node_lock); + } + mutex_unlock(&acpi_device_lock); + return 0; +} + +static void physical_device_enable_wakeup(struct acpi_device *adev) +{ + struct acpi_device_physical_node *entry; + + mutex_lock(&adev->physical_node_lock); + + list_for_each_entry(entry, + &adev->physical_node_list, node) + if (entry->dev && device_can_wakeup(entry->dev)) { + bool enable = !device_may_wakeup(entry->dev); + device_set_wakeup_enable(entry->dev, enable); + } + + mutex_unlock(&adev->physical_node_lock); +} + +static ssize_t +acpi_system_write_wakeup_device(struct file *file, + const char __user * buffer, + size_t count, loff_t * ppos) +{ + struct list_head *node, *next; + char strbuf[5]; + char str[5] = ""; + + if (count > 4) + count = 4; + + if (copy_from_user(strbuf, buffer, count)) + return -EFAULT; + strbuf[count] = '\0'; + sscanf(strbuf, "%s", str); + + mutex_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + if (!dev->wakeup.flags.valid) + continue; + + if (!strncmp(dev->pnp.bus_id, str, 4)) { + if (device_can_wakeup(&dev->dev)) { + bool enable = !device_may_wakeup(&dev->dev); + device_set_wakeup_enable(&dev->dev, enable); + } else { + physical_device_enable_wakeup(dev); + } + break; + } + } + mutex_unlock(&acpi_device_lock); + return count; +} + +static int +acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_wakeup_device_seq_show, + PDE_DATA(inode)); +} + +static const struct file_operations acpi_system_wakeup_device_fops = { + .owner = THIS_MODULE, + .open = acpi_system_wakeup_device_open_fs, + .read = seq_read, + .write = acpi_system_write_wakeup_device, + .llseek = seq_lseek, + .release = single_release, +}; + +int __init acpi_sleep_proc_init(void) +{ + /* 'wakeup device' [R/W] */ + proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, + acpi_root_dir, &acpi_system_wakeup_device_fops); + + return 0; +} diff --git a/kernel/drivers/acpi/processor_core.c b/kernel/drivers/acpi/processor_core.c new file mode 100644 index 000000000..b1ec78b8a --- /dev/null +++ b/kernel/drivers/acpi/processor_core.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2005 Intel Corporation + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Alex Chiang + * - Unified x86/ia64 implementations + * + * I/O APIC hotplug support + * Yinghai Lu + * Jiang Liu + */ +#include +#include +#include + +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("processor_core"); + +static struct acpi_table_madt *get_madt_table(void) +{ + static struct acpi_table_madt *madt; + static int read_madt; + + if (!read_madt) { + if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, + (struct acpi_table_header **)&madt))) + madt = NULL; + read_madt++; + } + + return madt; +} + +static int map_lapic_id(struct acpi_subtable_header *entry, + u32 acpi_id, phys_cpuid_t *apic_id) +{ + struct acpi_madt_local_apic *lapic = + container_of(entry, struct acpi_madt_local_apic, header); + + if (!(lapic->lapic_flags & ACPI_MADT_ENABLED)) + return -ENODEV; + + if (lapic->processor_id != acpi_id) + return -EINVAL; + + *apic_id = lapic->id; + return 0; +} + +static int map_x2apic_id(struct acpi_subtable_header *entry, + int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id) +{ + struct acpi_madt_local_x2apic *apic = + container_of(entry, struct acpi_madt_local_x2apic, header); + + if (!(apic->lapic_flags & ACPI_MADT_ENABLED)) + return -ENODEV; + + if (device_declaration && (apic->uid == acpi_id)) { + *apic_id = apic->local_apic_id; + return 0; + } + + return -EINVAL; +} + +static int map_lsapic_id(struct acpi_subtable_header *entry, + int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id) +{ + struct acpi_madt_local_sapic *lsapic = + container_of(entry, struct acpi_madt_local_sapic, header); + + if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED)) + return -ENODEV; + + if (device_declaration) { + if ((entry->length < 16) || (lsapic->uid != acpi_id)) + return -EINVAL; + } else if (lsapic->processor_id != acpi_id) + return -EINVAL; + + *apic_id = (lsapic->id << 8) | lsapic->eid; + return 0; +} + +/* + * Retrieve the ARM CPU physical identifier (MPIDR) + */ +static int map_gicc_mpidr(struct acpi_subtable_header *entry, + int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr) +{ + struct acpi_madt_generic_interrupt *gicc = + container_of(entry, struct acpi_madt_generic_interrupt, header); + + if (!(gicc->flags & ACPI_MADT_ENABLED)) + return -ENODEV; + + /* device_declaration means Device object in DSDT, in the + * GIC interrupt model, logical processors are required to + * have a Processor Device object in the DSDT, so we should + * check device_declaration here + */ + if (device_declaration && (gicc->uid == acpi_id)) { + *mpidr = gicc->arm_mpidr; + return 0; + } + + return -EINVAL; +} + +static phys_cpuid_t map_madt_entry(int type, u32 acpi_id) +{ + unsigned long madt_end, entry; + phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */ + struct acpi_table_madt *madt; + + madt = get_madt_table(); + if (!madt) + return phys_id; + + entry = (unsigned long)madt; + madt_end = entry + madt->header.length; + + /* Parse all entries looking for a match. */ + + entry += sizeof(struct acpi_table_madt); + while (entry + sizeof(struct acpi_subtable_header) < madt_end) { + struct acpi_subtable_header *header = + (struct acpi_subtable_header *)entry; + if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) { + if (!map_lapic_id(header, acpi_id, &phys_id)) + break; + } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) { + if (!map_x2apic_id(header, type, acpi_id, &phys_id)) + break; + } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { + if (!map_lsapic_id(header, type, acpi_id, &phys_id)) + break; + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + if (!map_gicc_mpidr(header, type, acpi_id, &phys_id)) + break; + } + entry += header->length; + } + return phys_id; +} + +static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + struct acpi_subtable_header *header; + phys_cpuid_t phys_id = PHYS_CPUID_INVALID; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) + goto exit; + + if (!buffer.length || !buffer.pointer) + goto exit; + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_BUFFER || + obj->buffer.length < sizeof(struct acpi_subtable_header)) { + goto exit; + } + + header = (struct acpi_subtable_header *)obj->buffer.pointer; + if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) + map_lapic_id(header, acpi_id, &phys_id); + else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) + map_lsapic_id(header, type, acpi_id, &phys_id); + else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) + map_x2apic_id(header, type, acpi_id, &phys_id); + else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) + map_gicc_mpidr(header, type, acpi_id, &phys_id); + +exit: + kfree(buffer.pointer); + return phys_id; +} + +phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) +{ + phys_cpuid_t phys_id; + + phys_id = map_mat_entry(handle, type, acpi_id); + if (phys_id == PHYS_CPUID_INVALID) + phys_id = map_madt_entry(type, acpi_id); + + return phys_id; +} + +int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id) +{ +#ifdef CONFIG_SMP + int i; +#endif + + if (phys_id == PHYS_CPUID_INVALID) { + /* + * On UP processor, there is no _MAT or MADT table. + * So above phys_id is always set to PHYS_CPUID_INVALID. + * + * BIOS may define multiple CPU handles even for UP processor. + * For example, + * + * Scope (_PR) + * { + * Processor (CPU0, 0x00, 0x00000410, 0x06) {} + * Processor (CPU1, 0x01, 0x00000410, 0x06) {} + * Processor (CPU2, 0x02, 0x00000410, 0x06) {} + * Processor (CPU3, 0x03, 0x00000410, 0x06) {} + * } + * + * Ignores phys_id and always returns 0 for the processor + * handle with acpi id 0 if nr_cpu_ids is 1. + * This should be the case if SMP tables are not found. + * Return -1 for other CPU's handle. + */ + if (nr_cpu_ids <= 1 && acpi_id == 0) + return acpi_id; + else + return -1; + } + +#ifdef CONFIG_SMP + for_each_possible_cpu(i) { + if (cpu_physical_id(i) == phys_id) + return i; + } +#else + /* In UP kernel, only processor 0 is valid */ + if (phys_id == 0) + return phys_id; +#endif + return -1; +} + +int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) +{ + phys_cpuid_t phys_id; + + phys_id = acpi_get_phys_id(handle, type, acpi_id); + + return acpi_map_cpuid(phys_id, acpi_id); +} +EXPORT_SYMBOL_GPL(acpi_get_cpuid); + +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base, + u64 *phys_addr, int *ioapic_id) +{ + struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry; + + if (ioapic->global_irq_base != gsi_base) + return 0; + + *phys_addr = ioapic->address; + *ioapic_id = ioapic->id; + return 1; +} + +static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr) +{ + struct acpi_subtable_header *hdr; + unsigned long madt_end, entry; + struct acpi_table_madt *madt; + int apic_id = -1; + + madt = get_madt_table(); + if (!madt) + return apic_id; + + entry = (unsigned long)madt; + madt_end = entry + madt->header.length; + + /* Parse all entries looking for a match. */ + entry += sizeof(struct acpi_table_madt); + while (entry + sizeof(struct acpi_subtable_header) < madt_end) { + hdr = (struct acpi_subtable_header *)entry; + if (hdr->type == ACPI_MADT_TYPE_IO_APIC && + get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id)) + break; + else + entry += hdr->length; + } + + return apic_id; +} + +static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base, + u64 *phys_addr) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_subtable_header *header; + union acpi_object *obj; + int apic_id = -1; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) + goto exit; + + if (!buffer.length || !buffer.pointer) + goto exit; + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_BUFFER || + obj->buffer.length < sizeof(struct acpi_subtable_header)) + goto exit; + + header = (struct acpi_subtable_header *)obj->buffer.pointer; + if (header->type == ACPI_MADT_TYPE_IO_APIC) + get_ioapic_id(header, gsi_base, phys_addr, &apic_id); + +exit: + kfree(buffer.pointer); + return apic_id; +} + +/** + * acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base + * @handle: ACPI object for IOAPIC device + * @gsi_base: GSI base to match with + * @phys_addr: Pointer to store physical address of matching IOAPIC record + * + * Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search + * for an ACPI IOAPIC record matching @gsi_base. + * Return IOAPIC id and store physical address in @phys_addr if found a match, + * otherwise return <0. + */ +int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr) +{ + int apic_id; + + apic_id = parse_mat_ioapic_entry(handle, gsi_base, phys_addr); + if (apic_id == -1) + apic_id = parse_madt_ioapic_entry(gsi_base, phys_addr); + + return apic_id; +} +#endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */ diff --git a/kernel/drivers/acpi/processor_driver.c b/kernel/drivers/acpi/processor_driver.c new file mode 100644 index 000000000..d9f71581b --- /dev/null +++ b/kernel/drivers/acpi/processor_driver.c @@ -0,0 +1,307 @@ +/* + * processor_driver.c - ACPI Processor Driver + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004 Dominik Brodowski + * Copyright (C) 2004 Anil S Keshavamurthy + * - Added processor hotplug support + * Copyright (C) 2013, Intel Corporation + * Rafael J. Wysocki + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "internal.h" + +#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 +#define ACPI_PROCESSOR_NOTIFY_POWER 0x81 +#define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 + +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("processor_driver"); + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI Processor Driver"); +MODULE_LICENSE("GPL"); + +static int acpi_processor_start(struct device *dev); +static int acpi_processor_stop(struct device *dev); + +static const struct acpi_device_id processor_device_ids[] = { + {ACPI_PROCESSOR_OBJECT_HID, 0}, + {ACPI_PROCESSOR_DEVICE_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, processor_device_ids); + +static struct device_driver acpi_processor_driver = { + .name = "processor", + .bus = &cpu_subsys, + .acpi_match_table = processor_device_ids, + .probe = acpi_processor_start, + .remove = acpi_processor_stop, +}; + +static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_device *device = data; + struct acpi_processor *pr; + int saved; + + if (device->handle != handle) + return; + + pr = acpi_driver_data(device); + if (!pr) + return; + + switch (event) { + case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: + saved = pr->performance_platform_limit; + acpi_processor_ppc_has_changed(pr, 1); + if (saved == pr->performance_platform_limit) + break; + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, + pr->performance_platform_limit); + break; + case ACPI_PROCESSOR_NOTIFY_POWER: + acpi_processor_cst_has_changed(pr); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, 0); + break; + case ACPI_PROCESSOR_NOTIFY_THROTTLING: + acpi_processor_tstate_has_changed(pr); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, 0); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return; +} + +static int __acpi_processor_start(struct acpi_device *device); + +static int acpi_cpu_soft_notify(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + struct acpi_processor *pr = per_cpu(processors, cpu); + struct acpi_device *device; + action &= ~CPU_TASKS_FROZEN; + + /* + * CPU_STARTING and CPU_DYING must not sleep. Return here since + * acpi_bus_get_device() may sleep. + */ + if (action == CPU_STARTING || action == CPU_DYING) + return NOTIFY_DONE; + + if (!pr || acpi_bus_get_device(pr->handle, &device)) + return NOTIFY_DONE; + + if (action == CPU_ONLINE) { + /* + * CPU got physically hotplugged and onlined for the first time: + * Initialize missing things. + */ + if (pr->flags.need_hotplug_init) { + int ret; + + pr_info("Will online and init hotplugged CPU: %d\n", + pr->id); + pr->flags.need_hotplug_init = 0; + ret = __acpi_processor_start(device); + WARN(ret, "Failed to start CPU: %d\n", pr->id); + } else { + /* Normal CPU soft online event. */ + acpi_processor_ppc_has_changed(pr, 0); + acpi_processor_hotplug(pr); + acpi_processor_reevaluate_tstate(pr, action); + acpi_processor_tstate_has_changed(pr); + } + } else if (action == CPU_DEAD) { + /* Invalidate flag.throttling after the CPU is offline. */ + acpi_processor_reevaluate_tstate(pr, action); + } + return NOTIFY_OK; +} + +static struct notifier_block __refdata acpi_cpu_notifier = { + .notifier_call = acpi_cpu_soft_notify, +}; + +static int __acpi_processor_start(struct acpi_device *device) +{ + struct acpi_processor *pr = acpi_driver_data(device); + acpi_status status; + int result = 0; + + if (!pr) + return -ENODEV; + + if (pr->flags.need_hotplug_init) + return 0; + +#ifdef CONFIG_CPU_FREQ + acpi_processor_ppc_has_changed(pr, 0); +#endif + acpi_processor_get_throttling_info(pr); + + if (pr->flags.throttling) + pr->flags.limit = 1; + + if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) + acpi_processor_power_init(pr); + + pr->cdev = thermal_cooling_device_register("Processor", device, + &processor_cooling_ops); + if (IS_ERR(pr->cdev)) { + result = PTR_ERR(pr->cdev); + goto err_power_exit; + } + + dev_dbg(&device->dev, "registered as cooling_device%d\n", + pr->cdev->id); + + result = sysfs_create_link(&device->dev.kobj, + &pr->cdev->device.kobj, + "thermal_cooling"); + if (result) { + dev_err(&device->dev, + "Failed to create sysfs link 'thermal_cooling'\n"); + goto err_thermal_unregister; + } + result = sysfs_create_link(&pr->cdev->device.kobj, + &device->dev.kobj, + "device"); + if (result) { + dev_err(&pr->cdev->device, + "Failed to create sysfs link 'device'\n"); + goto err_remove_sysfs_thermal; + } + + status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify, device); + if (ACPI_SUCCESS(status)) + return 0; + + sysfs_remove_link(&pr->cdev->device.kobj, "device"); + err_remove_sysfs_thermal: + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); + err_thermal_unregister: + thermal_cooling_device_unregister(pr->cdev); + err_power_exit: + acpi_processor_power_exit(pr); + return result; +} + +static int acpi_processor_start(struct device *dev) +{ + struct acpi_device *device = ACPI_COMPANION(dev); + + if (!device) + return -ENODEV; + + return __acpi_processor_start(device); +} + +static int acpi_processor_stop(struct device *dev) +{ + struct acpi_device *device = ACPI_COMPANION(dev); + struct acpi_processor *pr; + + if (!device) + return 0; + + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify); + + pr = acpi_driver_data(device); + if (!pr) + return 0; + + acpi_processor_power_exit(pr); + + if (pr->cdev) { + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&pr->cdev->device.kobj, "device"); + thermal_cooling_device_unregister(pr->cdev); + pr->cdev = NULL; + } + return 0; +} + +/* + * We keep the driver loaded even when ACPI is not running. + * This is needed for the powernow-k8 driver, that works even without + * ACPI, but needs symbols from this driver + */ + +static int __init acpi_processor_driver_init(void) +{ + int result = 0; + + if (acpi_disabled) + return 0; + + result = driver_register(&acpi_processor_driver); + if (result < 0) + return result; + + acpi_processor_syscore_init(); + register_hotcpu_notifier(&acpi_cpu_notifier); + acpi_thermal_cpufreq_init(); + acpi_processor_ppc_init(); + acpi_processor_throttling_init(); + return 0; +} + +static void __exit acpi_processor_driver_exit(void) +{ + if (acpi_disabled) + return; + + acpi_processor_ppc_exit(); + acpi_thermal_cpufreq_exit(); + unregister_hotcpu_notifier(&acpi_cpu_notifier); + acpi_processor_syscore_exit(); + driver_unregister(&acpi_processor_driver); +} + +module_init(acpi_processor_driver_init); +module_exit(acpi_processor_driver_exit); + +MODULE_ALIAS("processor"); diff --git a/kernel/drivers/acpi/processor_idle.c b/kernel/drivers/acpi/processor_idle.c new file mode 100644 index 000000000..39e0c8e36 --- /dev/null +++ b/kernel/drivers/acpi/processor_idle.c @@ -0,0 +1,1145 @@ +/* + * processor_idle - idle state submodule to the ACPI processor driver + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004, 2005 Dominik Brodowski + * Copyright (C) 2004 Anil S Keshavamurthy + * - Added processor hotplug support + * Copyright (C) 2005 Venkatesh Pallipadi + * - Added support for C3 on SMP + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include /* need_resched() */ +#include +#include +#include +#include + +/* + * Include the apic definitions for x86 to have the APIC timer related defines + * available also for UP (on SMP it gets magically included via linux/smp.h). + * asm/acpi.h is not an option, as it would require more include magic. Also + * creating an empty asm-ia64/apic.h would just trade pest vs. cholera. + */ +#ifdef CONFIG_X86 +#include +#endif + +#define PREFIX "ACPI: " + +#define ACPI_PROCESSOR_CLASS "processor" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("processor_idle"); + +static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER; +module_param(max_cstate, uint, 0000); +static unsigned int nocst __read_mostly; +module_param(nocst, uint, 0000); +static int bm_check_disable __read_mostly; +module_param(bm_check_disable, uint, 0000); + +static unsigned int latency_factor __read_mostly = 2; +module_param(latency_factor, uint, 0644); + +static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device); + +static DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], + acpi_cstate); + +static int disabled_by_idle_boot_param(void) +{ + return boot_option_idle_override == IDLE_POLL || + boot_option_idle_override == IDLE_HALT; +} + +/* + * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3. + * For now disable this. Probably a bug somewhere else. + * + * To skip this limit, boot/load with a large max_cstate limit. + */ +static int set_max_cstate(const struct dmi_system_id *id) +{ + if (max_cstate > ACPI_PROCESSOR_MAX_POWER) + return 0; + + printk(KERN_NOTICE PREFIX "%s detected - limiting to C%ld max_cstate." + " Override with \"processor.max_cstate=%d\"\n", id->ident, + (long)id->driver_data, ACPI_PROCESSOR_MAX_POWER + 1); + + max_cstate = (long)id->driver_data; + + return 0; +} + +static struct dmi_system_id processor_power_dmi_table[] = { + { set_max_cstate, "Clevo 5600D", { + DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), + DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")}, + (void *)2}, + { set_max_cstate, "Pavilion zv5000", { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")}, + (void *)1}, + { set_max_cstate, "Asus L8400B", { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME,"L8400B series Notebook PC")}, + (void *)1}, + {}, +}; + + +/* + * Callers should disable interrupts before the call and enable + * interrupts after return. + */ +static void acpi_safe_halt(void) +{ + if (!tif_need_resched()) { + safe_halt(); + local_irq_disable(); + } +} + +#ifdef ARCH_APICTIMER_STOPS_ON_C3 + +/* + * Some BIOS implementations switch to C3 in the published C2 state. + * This seems to be a common problem on AMD boxen, but other vendors + * are affected too. We pick the most conservative approach: we assume + * that the local APIC stops in both C2 and C3. + */ +static void lapic_timer_check_state(int state, struct acpi_processor *pr, + struct acpi_processor_cx *cx) +{ + struct acpi_processor_power *pwr = &pr->power; + u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2; + + if (cpu_has(&cpu_data(pr->id), X86_FEATURE_ARAT)) + return; + + if (amd_e400_c1e_detected) + type = ACPI_STATE_C1; + + /* + * Check, if one of the previous states already marked the lapic + * unstable + */ + if (pwr->timer_broadcast_on_state < state) + return; + + if (cx->type >= type) + pr->power.timer_broadcast_on_state = state; +} + +static void __lapic_timer_propagate_broadcast(void *arg) +{ + struct acpi_processor *pr = (struct acpi_processor *) arg; + + if (pr->power.timer_broadcast_on_state < INT_MAX) + tick_broadcast_enable(); + else + tick_broadcast_disable(); +} + +static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) +{ + smp_call_function_single(pr->id, __lapic_timer_propagate_broadcast, + (void *)pr, 1); +} + +/* Power(C) State timer broadcast control */ +static void lapic_timer_state_broadcast(struct acpi_processor *pr, + struct acpi_processor_cx *cx, + int broadcast) +{ + int state = cx - pr->power.states; + + if (state >= pr->power.timer_broadcast_on_state) { + if (broadcast) + tick_broadcast_enter(); + else + tick_broadcast_exit(); + } +} + +#else + +static void lapic_timer_check_state(int state, struct acpi_processor *pr, + struct acpi_processor_cx *cstate) { } +static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { } +static void lapic_timer_state_broadcast(struct acpi_processor *pr, + struct acpi_processor_cx *cx, + int broadcast) +{ +} + +#endif + +#ifdef CONFIG_PM_SLEEP +static u32 saved_bm_rld; + +static int acpi_processor_suspend(void) +{ + acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &saved_bm_rld); + return 0; +} + +static void acpi_processor_resume(void) +{ + u32 resumed_bm_rld = 0; + + acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &resumed_bm_rld); + if (resumed_bm_rld == saved_bm_rld) + return; + + acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); +} + +static struct syscore_ops acpi_processor_syscore_ops = { + .suspend = acpi_processor_suspend, + .resume = acpi_processor_resume, +}; + +void acpi_processor_syscore_init(void) +{ + register_syscore_ops(&acpi_processor_syscore_ops); +} + +void acpi_processor_syscore_exit(void) +{ + unregister_syscore_ops(&acpi_processor_syscore_ops); +} +#endif /* CONFIG_PM_SLEEP */ + +#if defined(CONFIG_X86) +static void tsc_check_state(int state) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + case X86_VENDOR_INTEL: + /* + * AMD Fam10h TSC will tick in all + * C/P/S0/S1 states when this bit is set. + */ + if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + return; + + /*FALL THROUGH*/ + default: + /* TSC could halt in idle, so notify users */ + if (state > ACPI_STATE_C1) + mark_tsc_unstable("TSC halts in idle"); + } +} +#else +static void tsc_check_state(int state) { return; } +#endif + +static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr) +{ + + if (!pr->pblk) + return -ENODEV; + + /* if info is obtained from pblk/fadt, type equals state */ + pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2; + pr->power.states[ACPI_STATE_C3].type = ACPI_STATE_C3; + +#ifndef CONFIG_HOTPLUG_CPU + /* + * Check for P_LVL2_UP flag before entering C2 and above on + * an SMP system. + */ + if ((num_online_cpus() > 1) && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) + return -ENODEV; +#endif + + /* determine C2 and C3 address from pblk */ + pr->power.states[ACPI_STATE_C2].address = pr->pblk + 4; + pr->power.states[ACPI_STATE_C3].address = pr->pblk + 5; + + /* determine latencies from FADT */ + pr->power.states[ACPI_STATE_C2].latency = acpi_gbl_FADT.c2_latency; + pr->power.states[ACPI_STATE_C3].latency = acpi_gbl_FADT.c3_latency; + + /* + * FADT specified C2 latency must be less than or equal to + * 100 microseconds. + */ + if (acpi_gbl_FADT.c2_latency > ACPI_PROCESSOR_MAX_C2_LATENCY) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C2 latency too large [%d]\n", acpi_gbl_FADT.c2_latency)); + /* invalidate C2 */ + pr->power.states[ACPI_STATE_C2].address = 0; + } + + /* + * FADT supplied C3 latency must be less than or equal to + * 1000 microseconds. + */ + if (acpi_gbl_FADT.c3_latency > ACPI_PROCESSOR_MAX_C3_LATENCY) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 latency too large [%d]\n", acpi_gbl_FADT.c3_latency)); + /* invalidate C3 */ + pr->power.states[ACPI_STATE_C3].address = 0; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "lvl2[0x%08x] lvl3[0x%08x]\n", + pr->power.states[ACPI_STATE_C2].address, + pr->power.states[ACPI_STATE_C3].address)); + + return 0; +} + +static int acpi_processor_get_power_info_default(struct acpi_processor *pr) +{ + if (!pr->power.states[ACPI_STATE_C1].valid) { + /* set the first C-State to C1 */ + /* all processors need to support C1 */ + pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; + pr->power.states[ACPI_STATE_C1].valid = 1; + pr->power.states[ACPI_STATE_C1].entry_method = ACPI_CSTATE_HALT; + } + /* the C0 state only exists as a filler in our array */ + pr->power.states[ACPI_STATE_C0].valid = 1; + return 0; +} + +static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) +{ + acpi_status status; + u64 count; + int current_count; + int i, ret = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *cst; + + + if (nocst) + return -ENODEV; + + current_count = 0; + + status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _CST, giving up\n")); + return -ENODEV; + } + + cst = buffer.pointer; + + /* There must be at least 2 elements */ + if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) { + printk(KERN_ERR PREFIX "not enough elements in _CST\n"); + ret = -EFAULT; + goto end; + } + + count = cst->package.elements[0].integer.value; + + /* Validate number of power states. */ + if (count < 1 || count != cst->package.count - 1) { + printk(KERN_ERR PREFIX "count given by _CST is not valid\n"); + ret = -EFAULT; + goto end; + } + + /* Tell driver that at least _CST is supported. */ + pr->flags.has_cst = 1; + + for (i = 1; i <= count; i++) { + union acpi_object *element; + union acpi_object *obj; + struct acpi_power_register *reg; + struct acpi_processor_cx cx; + + memset(&cx, 0, sizeof(cx)); + + element = &(cst->package.elements[i]); + if (element->type != ACPI_TYPE_PACKAGE) + continue; + + if (element->package.count != 4) + continue; + + obj = &(element->package.elements[0]); + + if (obj->type != ACPI_TYPE_BUFFER) + continue; + + reg = (struct acpi_power_register *)obj->buffer.pointer; + + if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && + (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) + continue; + + /* There should be an easy way to extract an integer... */ + obj = &(element->package.elements[1]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.type = obj->integer.value; + /* + * Some buggy BIOSes won't list C1 in _CST - + * Let acpi_processor_get_power_info_default() handle them later + */ + if (i == 1 && cx.type != ACPI_STATE_C1) + current_count++; + + cx.address = reg->address; + cx.index = current_count + 1; + + cx.entry_method = ACPI_CSTATE_SYSTEMIO; + if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { + if (acpi_processor_ffh_cstate_probe + (pr->id, &cx, reg) == 0) { + cx.entry_method = ACPI_CSTATE_FFH; + } else if (cx.type == ACPI_STATE_C1) { + /* + * C1 is a special case where FIXED_HARDWARE + * can be handled in non-MWAIT way as well. + * In that case, save this _CST entry info. + * Otherwise, ignore this info and continue. + */ + cx.entry_method = ACPI_CSTATE_HALT; + snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT"); + } else { + continue; + } + if (cx.type == ACPI_STATE_C1 && + (boot_option_idle_override == IDLE_NOMWAIT)) { + /* + * In most cases the C1 space_id obtained from + * _CST object is FIXED_HARDWARE access mode. + * But when the option of idle=halt is added, + * the entry_method type should be changed from + * CSTATE_FFH to CSTATE_HALT. + * When the option of idle=nomwait is added, + * the C1 entry_method type should be + * CSTATE_HALT. + */ + cx.entry_method = ACPI_CSTATE_HALT; + snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT"); + } + } else { + snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI IOPORT 0x%x", + cx.address); + } + + if (cx.type == ACPI_STATE_C1) { + cx.valid = 1; + } + + obj = &(element->package.elements[2]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.latency = obj->integer.value; + + obj = &(element->package.elements[3]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + current_count++; + memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx)); + + /* + * We support total ACPI_PROCESSOR_MAX_POWER - 1 + * (From 1 through ACPI_PROCESSOR_MAX_POWER - 1) + */ + if (current_count >= (ACPI_PROCESSOR_MAX_POWER - 1)) { + printk(KERN_WARNING + "Limiting number of power states to max (%d)\n", + ACPI_PROCESSOR_MAX_POWER); + printk(KERN_WARNING + "Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); + break; + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d power states\n", + current_count)); + + /* Validate number of power states discovered */ + if (current_count < 2) + ret = -EFAULT; + + end: + kfree(buffer.pointer); + + return ret; +} + +static void acpi_processor_power_verify_c3(struct acpi_processor *pr, + struct acpi_processor_cx *cx) +{ + static int bm_check_flag = -1; + static int bm_control_flag = -1; + + + if (!cx->address) + return; + + /* + * PIIX4 Erratum #18: We don't support C3 when Type-F (fast) + * DMA transfers are used by any ISA device to avoid livelock. + * Note that we could disable Type-F DMA (as recommended by + * the erratum), but this is known to disrupt certain ISA + * devices thus we take the conservative approach. + */ + else if (errata.piix4.fdma) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 not supported on PIIX4 with Type-F DMA\n")); + return; + } + + /* All the logic here assumes flags.bm_check is same across all CPUs */ + if (bm_check_flag == -1) { + /* Determine whether bm_check is needed based on CPU */ + acpi_processor_power_init_bm_check(&(pr->flags), pr->id); + bm_check_flag = pr->flags.bm_check; + bm_control_flag = pr->flags.bm_control; + } else { + pr->flags.bm_check = bm_check_flag; + pr->flags.bm_control = bm_control_flag; + } + + if (pr->flags.bm_check) { + if (!pr->flags.bm_control) { + if (pr->flags.has_cst != 1) { + /* bus mastering control is necessary */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 support requires BM control\n")); + return; + } else { + /* Here we enter C3 without bus mastering */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 support without BM control\n")); + } + } + } else { + /* + * WBINVD should be set in fadt, for C3 state to be + * supported on when bm_check is not required. + */ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_WBINVD)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Cache invalidation should work properly" + " for C3 to be enabled on SMP systems\n")); + return; + } + } + + /* + * Otherwise we've met all of our C3 requirements. + * Normalize the C3 latency to expidite policy. Enable + * checking of bus mastering status (bm_check) so we can + * use this in our C3 policy + */ + cx->valid = 1; + + /* + * On older chipsets, BM_RLD needs to be set + * in order for Bus Master activity to wake the + * system from C3. Newer chipsets handle DMA + * during C3 automatically and BM_RLD is a NOP. + * In either case, the proper way to + * handle BM_RLD is to set it and leave it set. + */ + acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1); + + return; +} + +static int acpi_processor_power_verify(struct acpi_processor *pr) +{ + unsigned int i; + unsigned int working = 0; + + pr->power.timer_broadcast_on_state = INT_MAX; + + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { + struct acpi_processor_cx *cx = &pr->power.states[i]; + + switch (cx->type) { + case ACPI_STATE_C1: + cx->valid = 1; + break; + + case ACPI_STATE_C2: + if (!cx->address) + break; + cx->valid = 1; + break; + + case ACPI_STATE_C3: + acpi_processor_power_verify_c3(pr, cx); + break; + } + if (!cx->valid) + continue; + + lapic_timer_check_state(i, pr, cx); + tsc_check_state(cx->type); + working++; + } + + lapic_timer_propagate_broadcast(pr); + + return (working); +} + +static int acpi_processor_get_power_info(struct acpi_processor *pr) +{ + unsigned int i; + int result; + + + /* NOTE: the idle thread may not be running while calling + * this function */ + + /* Zero initialize all the C-states info. */ + memset(pr->power.states, 0, sizeof(pr->power.states)); + + result = acpi_processor_get_power_info_cst(pr); + if (result == -ENODEV) + result = acpi_processor_get_power_info_fadt(pr); + + if (result) + return result; + + acpi_processor_get_power_info_default(pr); + + pr->power.count = acpi_processor_power_verify(pr); + + /* + * if one state of type C2 or C3 is available, mark this + * CPU as being "idle manageable" + */ + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { + if (pr->power.states[i].valid) { + pr->power.count = i; + if (pr->power.states[i].type >= ACPI_STATE_C2) + pr->flags.power = 1; + } + } + + return 0; +} + +/** + * acpi_idle_bm_check - checks if bus master activity was detected + */ +static int acpi_idle_bm_check(void) +{ + u32 bm_status = 0; + + if (bm_check_disable) + return 0; + + acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status); + if (bm_status) + acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); + /* + * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect + * the true state of bus mastering activity; forcing us to + * manually check the BMIDEA bit of each IDE channel. + */ + else if (errata.piix4.bmisx) { + if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01) + || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01)) + bm_status = 1; + } + return bm_status; +} + +/** + * acpi_idle_do_entry - enter idle state using the appropriate method + * @cx: cstate data + * + * Caller disables interrupt before call and enables interrupt after return. + */ +static void acpi_idle_do_entry(struct acpi_processor_cx *cx) +{ + if (cx->entry_method == ACPI_CSTATE_FFH) { + /* Call into architectural FFH based C-state */ + acpi_processor_ffh_cstate_enter(cx); + } else if (cx->entry_method == ACPI_CSTATE_HALT) { + acpi_safe_halt(); + } else { + /* IO port based C-state */ + inb(cx->address); + /* Dummy wait op - must do something useless after P_LVL2 read + because chipsets cannot guarantee that STPCLK# signal + gets asserted in time to freeze execution properly. */ + inl(acpi_gbl_FADT.xpm_timer_block.address); + } +} + +/** + * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining) + * @dev: the target CPU + * @index: the index of suggested state + */ +static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) +{ + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); + + ACPI_FLUSH_CPU_CACHE(); + + while (1) { + + if (cx->entry_method == ACPI_CSTATE_HALT) + safe_halt(); + else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { + inb(cx->address); + /* See comment in acpi_idle_do_entry() */ + inl(acpi_gbl_FADT.xpm_timer_block.address); + } else + return -ENODEV; + } + + /* Never reached */ + return 0; +} + +static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr) +{ + return IS_ENABLED(CONFIG_HOTPLUG_CPU) && !pr->flags.has_cst && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED); +} + +static int c3_cpu_count; +static DEFINE_RAW_SPINLOCK(c3_lock); + +/** + * acpi_idle_enter_bm - enters C3 with proper BM handling + * @pr: Target processor + * @cx: Target state context + * @timer_bc: Whether or not to change timer mode to broadcast + */ +static void acpi_idle_enter_bm(struct acpi_processor *pr, + struct acpi_processor_cx *cx, bool timer_bc) +{ + acpi_unlazy_tlb(smp_processor_id()); + + /* + * Must be done before busmaster disable as we might need to + * access HPET ! + */ + if (timer_bc) + lapic_timer_state_broadcast(pr, cx, 1); + + /* + * disable bus master + * bm_check implies we need ARB_DIS + * bm_control implies whether we can do ARB_DIS + * + * That leaves a case where bm_check is set and bm_control is + * not set. In that case we cannot do much, we enter C3 + * without doing anything. + */ + if (pr->flags.bm_control) { + raw_spin_lock(&c3_lock); + c3_cpu_count++; + /* Disable bus master arbitration when all CPUs are in C3 */ + if (c3_cpu_count == num_online_cpus()) + acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1); + raw_spin_unlock(&c3_lock); + } + + acpi_idle_do_entry(cx); + + /* Re-enable bus master arbitration */ + if (pr->flags.bm_control) { + raw_spin_lock(&c3_lock); + acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0); + c3_cpu_count--; + raw_spin_unlock(&c3_lock); + } + + if (timer_bc) + lapic_timer_state_broadcast(pr, cx, 0); +} + +static int acpi_idle_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); + struct acpi_processor *pr; + + pr = __this_cpu_read(processors); + if (unlikely(!pr)) + return -EINVAL; + + if (cx->type != ACPI_STATE_C1) { + if (acpi_idle_fallback_to_c1(pr) && num_online_cpus() > 1) { + index = CPUIDLE_DRIVER_STATE_START; + cx = per_cpu(acpi_cstate[index], dev->cpu); + } else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) { + if (cx->bm_sts_skip || !acpi_idle_bm_check()) { + acpi_idle_enter_bm(pr, cx, true); + return index; + } else if (drv->safe_state_index >= 0) { + index = drv->safe_state_index; + cx = per_cpu(acpi_cstate[index], dev->cpu); + } else { + acpi_safe_halt(); + return -EBUSY; + } + } + } + + lapic_timer_state_broadcast(pr, cx, 1); + + if (cx->type == ACPI_STATE_C3) + ACPI_FLUSH_CPU_CACHE(); + + acpi_idle_do_entry(cx); + + lapic_timer_state_broadcast(pr, cx, 0); + + return index; +} + +static void acpi_idle_enter_freeze(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); + + if (cx->type == ACPI_STATE_C3) { + struct acpi_processor *pr = __this_cpu_read(processors); + + if (unlikely(!pr)) + return; + + if (pr->flags.bm_check) { + acpi_idle_enter_bm(pr, cx, false); + return; + } else { + ACPI_FLUSH_CPU_CACHE(); + } + } + acpi_idle_do_entry(cx); +} + +struct cpuidle_driver acpi_idle_driver = { + .name = "acpi_idle", + .owner = THIS_MODULE, +}; + +/** + * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE + * device i.e. per-cpu data + * + * @pr: the ACPI processor + * @dev : the cpuidle device + */ +static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, + struct cpuidle_device *dev) +{ + int i, count = CPUIDLE_DRIVER_STATE_START; + struct acpi_processor_cx *cx; + + if (!pr->flags.power_setup_done) + return -EINVAL; + + if (pr->flags.power == 0) { + return -EINVAL; + } + + if (!dev) + return -EINVAL; + + dev->cpu = pr->id; + + if (max_cstate == 0) + max_cstate = 1; + + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { + cx = &pr->power.states[i]; + + if (!cx->valid) + continue; + + per_cpu(acpi_cstate[count], dev->cpu) = cx; + + count++; + if (count == CPUIDLE_STATE_MAX) + break; + } + + if (!count) + return -EINVAL; + + return 0; +} + +/** + * acpi_processor_setup_cpuidle states- prepares and configures cpuidle + * global state data i.e. idle routines + * + * @pr: the ACPI processor + */ +static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) +{ + int i, count = CPUIDLE_DRIVER_STATE_START; + struct acpi_processor_cx *cx; + struct cpuidle_state *state; + struct cpuidle_driver *drv = &acpi_idle_driver; + + if (!pr->flags.power_setup_done) + return -EINVAL; + + if (pr->flags.power == 0) + return -EINVAL; + + drv->safe_state_index = -1; + for (i = CPUIDLE_DRIVER_STATE_START; i < CPUIDLE_STATE_MAX; i++) { + drv->states[i].name[0] = '\0'; + drv->states[i].desc[0] = '\0'; + } + + if (max_cstate == 0) + max_cstate = 1; + + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { + cx = &pr->power.states[i]; + + if (!cx->valid) + continue; + + state = &drv->states[count]; + snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); + strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); + state->exit_latency = cx->latency; + state->target_residency = cx->latency * latency_factor; + state->enter = acpi_idle_enter; + + state->flags = 0; + if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2) { + state->enter_dead = acpi_idle_play_dead; + drv->safe_state_index = count; + } + /* + * Halt-induced C1 is not good for ->enter_freeze, because it + * re-enables interrupts on exit. Moreover, C1 is generally not + * particularly interesting from the suspend-to-idle angle, so + * avoid C1 and the situations in which we may need to fall back + * to it altogether. + */ + if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr)) + state->enter_freeze = acpi_idle_enter_freeze; + + count++; + if (count == CPUIDLE_STATE_MAX) + break; + } + + drv->state_count = count; + + if (!count) + return -EINVAL; + + return 0; +} + +int acpi_processor_hotplug(struct acpi_processor *pr) +{ + int ret = 0; + struct cpuidle_device *dev; + + if (disabled_by_idle_boot_param()) + return 0; + + if (nocst) + return -ENODEV; + + if (!pr->flags.power_setup_done) + return -ENODEV; + + dev = per_cpu(acpi_cpuidle_device, pr->id); + cpuidle_pause_and_lock(); + cpuidle_disable_device(dev); + acpi_processor_get_power_info(pr); + if (pr->flags.power) { + acpi_processor_setup_cpuidle_cx(pr, dev); + ret = cpuidle_enable_device(dev); + } + cpuidle_resume_and_unlock(); + + return ret; +} + +int acpi_processor_cst_has_changed(struct acpi_processor *pr) +{ + int cpu; + struct acpi_processor *_pr; + struct cpuidle_device *dev; + + if (disabled_by_idle_boot_param()) + return 0; + + if (nocst) + return -ENODEV; + + if (!pr->flags.power_setup_done) + return -ENODEV; + + /* + * FIXME: Design the ACPI notification to make it once per + * system instead of once per-cpu. This condition is a hack + * to make the code that updates C-States be called once. + */ + + if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) { + + /* Protect against cpu-hotplug */ + get_online_cpus(); + cpuidle_pause_and_lock(); + + /* Disable all cpuidle devices */ + for_each_online_cpu(cpu) { + _pr = per_cpu(processors, cpu); + if (!_pr || !_pr->flags.power_setup_done) + continue; + dev = per_cpu(acpi_cpuidle_device, cpu); + cpuidle_disable_device(dev); + } + + /* Populate Updated C-state information */ + acpi_processor_get_power_info(pr); + acpi_processor_setup_cpuidle_states(pr); + + /* Enable all cpuidle devices */ + for_each_online_cpu(cpu) { + _pr = per_cpu(processors, cpu); + if (!_pr || !_pr->flags.power_setup_done) + continue; + acpi_processor_get_power_info(_pr); + if (_pr->flags.power) { + dev = per_cpu(acpi_cpuidle_device, cpu); + acpi_processor_setup_cpuidle_cx(_pr, dev); + cpuidle_enable_device(dev); + } + } + cpuidle_resume_and_unlock(); + put_online_cpus(); + } + + return 0; +} + +static int acpi_processor_registered; + +int acpi_processor_power_init(struct acpi_processor *pr) +{ + acpi_status status; + int retval; + struct cpuidle_device *dev; + static int first_run; + + if (disabled_by_idle_boot_param()) + return 0; + + if (!first_run) { + dmi_check_system(processor_power_dmi_table); + max_cstate = acpi_processor_cstate_check(max_cstate); + if (max_cstate < ACPI_C_STATES_MAX) + printk(KERN_NOTICE + "ACPI: processor limited to max C-state %d\n", + max_cstate); + first_run++; + } + + if (acpi_gbl_FADT.cst_control && !nocst) { + status = + acpi_os_write_port(acpi_gbl_FADT.smi_command, acpi_gbl_FADT.cst_control, 8); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Notifying BIOS of _CST ability failed")); + } + } + + acpi_processor_get_power_info(pr); + pr->flags.power_setup_done = 1; + + /* + * Install the idle handler if processor power management is supported. + * Note that we use previously set idle handler will be used on + * platforms that only support C1. + */ + if (pr->flags.power) { + /* Register acpi_idle_driver if not already registered */ + if (!acpi_processor_registered) { + acpi_processor_setup_cpuidle_states(pr); + retval = cpuidle_register_driver(&acpi_idle_driver); + if (retval) + return retval; + printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", + acpi_idle_driver.name); + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + per_cpu(acpi_cpuidle_device, pr->id) = dev; + + acpi_processor_setup_cpuidle_cx(pr, dev); + + /* Register per-cpu cpuidle_device. Cpuidle driver + * must already be registered before registering device + */ + retval = cpuidle_register_device(dev); + if (retval) { + if (acpi_processor_registered == 0) + cpuidle_unregister_driver(&acpi_idle_driver); + return retval; + } + acpi_processor_registered++; + } + return 0; +} + +int acpi_processor_power_exit(struct acpi_processor *pr) +{ + struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id); + + if (disabled_by_idle_boot_param()) + return 0; + + if (pr->flags.power) { + cpuidle_unregister_device(dev); + acpi_processor_registered--; + if (acpi_processor_registered == 0) + cpuidle_unregister_driver(&acpi_idle_driver); + } + + pr->flags.power_setup_done = 0; + return 0; +} diff --git a/kernel/drivers/acpi/processor_pdc.c b/kernel/drivers/acpi/processor_pdc.c new file mode 100644 index 000000000..e5dd80800 --- /dev/null +++ b/kernel/drivers/acpi/processor_pdc.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2005 Intel Corporation + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Venkatesh Pallipadi + * - Added _PDC for platforms with Intel CPUs + */ + +#define pr_fmt(fmt) "ACPI: " fmt + +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("processor_pdc"); + +static bool __init processor_physically_present(acpi_handle handle) +{ + int cpuid, type; + u32 acpi_id; + acpi_status status; + acpi_object_type acpi_type; + unsigned long long tmp; + union acpi_object object = { 0 }; + struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; + + status = acpi_get_type(handle, &acpi_type); + if (ACPI_FAILURE(status)) + return false; + + switch (acpi_type) { + case ACPI_TYPE_PROCESSOR: + status = acpi_evaluate_object(handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) + return false; + acpi_id = object.processor.proc_id; + break; + case ACPI_TYPE_DEVICE: + status = acpi_evaluate_integer(handle, "_UID", NULL, &tmp); + if (ACPI_FAILURE(status)) + return false; + acpi_id = tmp; + break; + default: + return false; + } + + type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0; + cpuid = acpi_get_cpuid(handle, type, acpi_id); + + if (cpuid == -1) + return false; + + return true; +} + +static void acpi_set_pdc_bits(u32 *buf) +{ + buf[0] = ACPI_PDC_REVISION_ID; + buf[1] = 1; + + /* Enable coordination with firmware's _TSD info */ + buf[2] = ACPI_PDC_SMP_T_SWCOORD; + + /* Twiddle arch-specific bits needed for _PDC */ + arch_acpi_set_pdc_bits(buf); +} + +static struct acpi_object_list *acpi_processor_alloc_pdc(void) +{ + struct acpi_object_list *obj_list; + union acpi_object *obj; + u32 *buf; + + /* allocate and initialize pdc. It will be used later. */ + obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL); + if (!obj_list) + goto out; + + obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL); + if (!obj) { + kfree(obj_list); + goto out; + } + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) { + kfree(obj); + kfree(obj_list); + goto out; + } + + acpi_set_pdc_bits(buf); + + obj->type = ACPI_TYPE_BUFFER; + obj->buffer.length = 12; + obj->buffer.pointer = (u8 *) buf; + obj_list->count = 1; + obj_list->pointer = obj; + + return obj_list; +out: + pr_err("Memory allocation error\n"); + return NULL; +} + +/* + * _PDC is required for a BIOS-OS handshake for most of the newer + * ACPI processor features. + */ +static acpi_status +acpi_processor_eval_pdc(acpi_handle handle, struct acpi_object_list *pdc_in) +{ + acpi_status status = AE_OK; + + if (boot_option_idle_override == IDLE_NOMWAIT) { + /* + * If mwait is disabled for CPU C-states, the C2C3_FFH access + * mode will be disabled in the parameter of _PDC object. + * Of course C1_FFH access mode will also be disabled. + */ + union acpi_object *obj; + u32 *buffer = NULL; + + obj = pdc_in->pointer; + buffer = (u32 *)(obj->buffer.pointer); + buffer[2] &= ~(ACPI_PDC_C_C2C3_FFH | ACPI_PDC_C_C1_FFH); + + } + status = acpi_evaluate_object(handle, "_PDC", pdc_in, NULL); + + if (ACPI_FAILURE(status)) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Could not evaluate _PDC, using legacy perf. control.\n")); + + return status; +} + +void acpi_processor_set_pdc(acpi_handle handle) +{ + struct acpi_object_list *obj_list; + + if (arch_has_acpi_pdc() == false) + return; + + obj_list = acpi_processor_alloc_pdc(); + if (!obj_list) + return; + + acpi_processor_eval_pdc(handle, obj_list); + + kfree(obj_list->pointer->buffer.pointer); + kfree(obj_list->pointer); + kfree(obj_list); +} + +static acpi_status __init +early_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + if (processor_physically_present(handle) == false) + return AE_OK; + + acpi_processor_set_pdc(handle); + return AE_OK; +} + +static int __init set_no_mwait(const struct dmi_system_id *id) +{ + pr_notice("%s detected - disabling mwait for CPU C-states\n", + id->ident); + boot_option_idle_override = IDLE_NOMWAIT; + return 0; +} + +static struct dmi_system_id processor_idle_dmi_table[] __initdata = { + { + set_no_mwait, "Extensa 5220", { + DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL}, + {}, +}; + +static void __init processor_dmi_check(void) +{ + /* + * Check whether the system is DMI table. If yes, OSPM + * should not use mwait for CPU-states. + */ + dmi_check_system(processor_idle_dmi_table); +} + +void __init acpi_early_processor_set_pdc(void) +{ + processor_dmi_check(); + + acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + early_init_pdc, NULL, NULL, NULL); + acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, early_init_pdc, NULL, NULL); +} diff --git a/kernel/drivers/acpi/processor_perflib.c b/kernel/drivers/acpi/processor_perflib.c new file mode 100644 index 000000000..cfc8aba72 --- /dev/null +++ b/kernel/drivers/acpi/processor_perflib.c @@ -0,0 +1,810 @@ +/* + * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004 Dominik Brodowski + * Copyright (C) 2004 Anil S Keshavamurthy + * - Added processor hotplug support + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_X86 +#include +#endif + +#define PREFIX "ACPI: " + +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("processor_perflib"); + +static DEFINE_MUTEX(performance_mutex); + +/* + * _PPC support is implemented as a CPUfreq policy notifier: + * This means each time a CPUfreq driver registered also with + * the ACPI core is asked to change the speed policy, the maximum + * value is adjusted so that it is within the platform limit. + * + * Also, when a new platform limit value is detected, the CPUfreq + * policy is adjusted accordingly. + */ + +/* ignore_ppc: + * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet + * ignore _PPC + * 0 -> cpufreq low level drivers initialized -> consider _PPC values + * 1 -> ignore _PPC totally -> forced by user through boot param + */ +static int ignore_ppc = -1; +module_param(ignore_ppc, int, 0644); +MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \ + "limited by BIOS, this should help"); + +#define PPC_REGISTERED 1 +#define PPC_IN_USE 2 + +static int acpi_processor_ppc_status; + +static int acpi_processor_ppc_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct cpufreq_policy *policy = data; + struct acpi_processor *pr; + unsigned int ppc = 0; + + if (event == CPUFREQ_START && ignore_ppc <= 0) { + ignore_ppc = 0; + return 0; + } + + if (ignore_ppc) + return 0; + + if (event != CPUFREQ_INCOMPATIBLE) + return 0; + + mutex_lock(&performance_mutex); + + pr = per_cpu(processors, policy->cpu); + if (!pr || !pr->performance) + goto out; + + ppc = (unsigned int)pr->performance_platform_limit; + + if (ppc >= pr->performance->state_count) + goto out; + + cpufreq_verify_within_limits(policy, 0, + pr->performance->states[ppc]. + core_frequency * 1000); + + out: + mutex_unlock(&performance_mutex); + + return 0; +} + +static struct notifier_block acpi_ppc_notifier_block = { + .notifier_call = acpi_processor_ppc_notifier, +}; + +static int acpi_processor_get_platform_limit(struct acpi_processor *pr) +{ + acpi_status status = 0; + unsigned long long ppc = 0; + + + if (!pr) + return -EINVAL; + + /* + * _PPC indicates the maximum state currently supported by the platform + * (e.g. 0 = states 0..n; 1 = states 1..n; etc. + */ + status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc); + + if (status != AE_NOT_FOUND) + acpi_processor_ppc_status |= PPC_IN_USE; + + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PPC")); + return -ENODEV; + } + + pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id, + (int)ppc, ppc ? "" : "not"); + + pr->performance_platform_limit = (int)ppc; + + return 0; +} + +#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 +/* + * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status + * @handle: ACPI processor handle + * @status: the status code of _PPC evaluation + * 0: success. OSPM is now using the performance state specificed. + * 1: failure. OSPM has not changed the number of P-states in use + */ +static void acpi_processor_ppc_ost(acpi_handle handle, int status) +{ + if (acpi_has_method(handle, "_OST")) + acpi_evaluate_ost(handle, ACPI_PROCESSOR_NOTIFY_PERFORMANCE, + status, NULL); +} + +int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) +{ + int ret; + + if (ignore_ppc) { + /* + * Only when it is notification event, the _OST object + * will be evaluated. Otherwise it is skipped. + */ + if (event_flag) + acpi_processor_ppc_ost(pr->handle, 1); + return 0; + } + + ret = acpi_processor_get_platform_limit(pr); + /* + * Only when it is notification event, the _OST object + * will be evaluated. Otherwise it is skipped. + */ + if (event_flag) { + if (ret < 0) + acpi_processor_ppc_ost(pr->handle, 1); + else + acpi_processor_ppc_ost(pr->handle, 0); + } + if (ret < 0) + return (ret); + else + return cpufreq_update_policy(pr->id); +} + +int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) +{ + struct acpi_processor *pr; + + pr = per_cpu(processors, cpu); + if (!pr || !pr->performance || !pr->performance->state_count) + return -ENODEV; + *limit = pr->performance->states[pr->performance_platform_limit]. + core_frequency * 1000; + return 0; +} +EXPORT_SYMBOL(acpi_processor_get_bios_limit); + +void acpi_processor_ppc_init(void) +{ + if (!cpufreq_register_notifier + (&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER)) + acpi_processor_ppc_status |= PPC_REGISTERED; + else + printk(KERN_DEBUG + "Warning: Processor Platform Limit not supported.\n"); +} + +void acpi_processor_ppc_exit(void) +{ + if (acpi_processor_ppc_status & PPC_REGISTERED) + cpufreq_unregister_notifier(&acpi_ppc_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + + acpi_processor_ppc_status &= ~PPC_REGISTERED; +} + +static int acpi_processor_get_performance_control(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *pct = NULL; + union acpi_object obj = { 0 }; + + + status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PCT")); + return -ENODEV; + } + + pct = (union acpi_object *)buffer.pointer; + if (!pct || (pct->type != ACPI_TYPE_PACKAGE) + || (pct->package.count != 2)) { + printk(KERN_ERR PREFIX "Invalid _PCT data\n"); + result = -EFAULT; + goto end; + } + + /* + * control_register + */ + + obj = pct->package.elements[0]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_pct_register)) + || (obj.buffer.pointer == NULL)) { + printk(KERN_ERR PREFIX "Invalid _PCT data (control_register)\n"); + result = -EFAULT; + goto end; + } + memcpy(&pr->performance->control_register, obj.buffer.pointer, + sizeof(struct acpi_pct_register)); + + /* + * status_register + */ + + obj = pct->package.elements[1]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_pct_register)) + || (obj.buffer.pointer == NULL)) { + printk(KERN_ERR PREFIX "Invalid _PCT data (status_register)\n"); + result = -EFAULT; + goto end; + } + + memcpy(&pr->performance->status_register, obj.buffer.pointer, + sizeof(struct acpi_pct_register)); + + end: + kfree(buffer.pointer); + + return result; +} + +#ifdef CONFIG_X86 +/* + * Some AMDs have 50MHz frequency multiples, but only provide 100MHz rounding + * in their ACPI data. Calculate the real values and fix up the _PSS data. + */ +static void amd_fixup_frequency(struct acpi_processor_px *px, int i) +{ + u32 hi, lo, fid, did; + int index = px->control & 0x00000007; + + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) + return; + + if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) + || boot_cpu_data.x86 == 0x11) { + rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi); + /* + * MSR C001_0064+: + * Bit 63: PstateEn. Read-write. If set, the P-state is valid. + */ + if (!(hi & BIT(31))) + return; + + fid = lo & 0x3f; + did = (lo >> 6) & 7; + if (boot_cpu_data.x86 == 0x10) + px->core_frequency = (100 * (fid + 0x10)) >> did; + else + px->core_frequency = (100 * (fid + 8)) >> did; + } +} +#else +static void amd_fixup_frequency(struct acpi_processor_px *px, int i) {}; +#endif + +static int acpi_processor_get_performance_states(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" }; + struct acpi_buffer state = { 0, NULL }; + union acpi_object *pss = NULL; + int i; + int last_invalid = -1; + + + status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PSS")); + return -ENODEV; + } + + pss = buffer.pointer; + if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_ERR PREFIX "Invalid _PSS data\n"); + result = -EFAULT; + goto end; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n", + pss->package.count)); + + pr->performance->state_count = pss->package.count; + pr->performance->states = + kmalloc(sizeof(struct acpi_processor_px) * pss->package.count, + GFP_KERNEL); + if (!pr->performance->states) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < pr->performance->state_count; i++) { + + struct acpi_processor_px *px = &(pr->performance->states[i]); + + state.length = sizeof(struct acpi_processor_px); + state.pointer = px; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i)); + + status = acpi_extract_package(&(pss->package.elements[i]), + &format, &state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid _PSS data")); + result = -EFAULT; + kfree(pr->performance->states); + goto end; + } + + amd_fixup_frequency(px, i); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n", + i, + (u32) px->core_frequency, + (u32) px->power, + (u32) px->transition_latency, + (u32) px->bus_master_latency, + (u32) px->control, (u32) px->status)); + + /* + * Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq + */ + if (!px->core_frequency || + ((u32)(px->core_frequency * 1000) != + (px->core_frequency * 1000))) { + printk(KERN_ERR FW_BUG PREFIX + "Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n", + pr->id, px->core_frequency); + if (last_invalid == -1) + last_invalid = i; + } else { + if (last_invalid != -1) { + /* + * Copy this valid entry over last_invalid entry + */ + memcpy(&(pr->performance->states[last_invalid]), + px, sizeof(struct acpi_processor_px)); + ++last_invalid; + } + } + } + + if (last_invalid == 0) { + printk(KERN_ERR FW_BUG PREFIX + "No valid BIOS _PSS frequency found for processor %d\n", pr->id); + result = -EFAULT; + kfree(pr->performance->states); + pr->performance->states = NULL; + } + + if (last_invalid > 0) + pr->performance->state_count = last_invalid; + + end: + kfree(buffer.pointer); + + return result; +} + +int acpi_processor_get_performance_info(struct acpi_processor *pr) +{ + int result = 0; + + if (!pr || !pr->performance || !pr->handle) + return -EINVAL; + + if (!acpi_has_method(pr->handle, "_PCT")) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "ACPI-based processor performance control unavailable\n")); + return -ENODEV; + } + + result = acpi_processor_get_performance_control(pr); + if (result) + goto update_bios; + + result = acpi_processor_get_performance_states(pr); + if (result) + goto update_bios; + + /* We need to call _PPC once when cpufreq starts */ + if (ignore_ppc != 1) + result = acpi_processor_get_platform_limit(pr); + + return result; + + /* + * Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that + * the BIOS is older than the CPU and does not know its frequencies + */ + update_bios: +#ifdef CONFIG_X86 + if (acpi_has_method(pr->handle, "_PPC")) { + if(boot_cpu_has(X86_FEATURE_EST)) + printk(KERN_WARNING FW_BUG "BIOS needs update for CPU " + "frequency support\n"); + } +#endif + return result; +} +EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info); +int acpi_processor_notify_smm(struct module *calling_module) +{ + acpi_status status; + static int is_done = 0; + + + if (!(acpi_processor_ppc_status & PPC_REGISTERED)) + return -EBUSY; + + if (!try_module_get(calling_module)) + return -EINVAL; + + /* is_done is set to negative if an error occurred, + * and to postitive if _no_ error occurred, but SMM + * was already notified. This avoids double notification + * which might lead to unexpected results... + */ + if (is_done > 0) { + module_put(calling_module); + return 0; + } else if (is_done < 0) { + module_put(calling_module); + return is_done; + } + + is_done = -EIO; + + /* Can't write pstate_control to smi_command if either value is zero */ + if ((!acpi_gbl_FADT.smi_command) || (!acpi_gbl_FADT.pstate_control)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No SMI port or pstate_control\n")); + module_put(calling_module); + return 0; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Writing pstate_control [0x%x] to smi_command [0x%x]\n", + acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command)); + + status = acpi_os_write_port(acpi_gbl_FADT.smi_command, + (u32) acpi_gbl_FADT.pstate_control, 8); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Failed to write pstate_control [0x%x] to " + "smi_command [0x%x]", acpi_gbl_FADT.pstate_control, + acpi_gbl_FADT.smi_command)); + module_put(calling_module); + return status; + } + + /* Success. If there's no _PPC, we need to fear nothing, so + * we can allow the cpufreq driver to be rmmod'ed. */ + is_done = 1; + + if (!(acpi_processor_ppc_status & PPC_IN_USE)) + module_put(calling_module); + + return 0; +} + +EXPORT_SYMBOL(acpi_processor_notify_smm); + +static int acpi_processor_get_psd(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"}; + struct acpi_buffer state = {0, NULL}; + union acpi_object *psd = NULL; + struct acpi_psd_package *pdomain; + + status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer); + if (ACPI_FAILURE(status)) { + return -ENODEV; + } + + psd = buffer.pointer; + if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_ERR PREFIX "Invalid _PSD data\n"); + result = -EFAULT; + goto end; + } + + if (psd->package.count != 1) { + printk(KERN_ERR PREFIX "Invalid _PSD data\n"); + result = -EFAULT; + goto end; + } + + pdomain = &(pr->performance->domain_info); + + state.length = sizeof(struct acpi_psd_package); + state.pointer = pdomain; + + status = acpi_extract_package(&(psd->package.elements[0]), + &format, &state); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Invalid _PSD data\n"); + result = -EFAULT; + goto end; + } + + if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) { + printk(KERN_ERR PREFIX "Unknown _PSD:num_entries\n"); + result = -EFAULT; + goto end; + } + + if (pdomain->revision != ACPI_PSD_REV0_REVISION) { + printk(KERN_ERR PREFIX "Unknown _PSD:revision\n"); + result = -EFAULT; + goto end; + } + + if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && + pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && + pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { + printk(KERN_ERR PREFIX "Invalid _PSD:coord_type\n"); + result = -EFAULT; + goto end; + } +end: + kfree(buffer.pointer); + return result; +} + +int acpi_processor_preregister_performance( + struct acpi_processor_performance __percpu *performance) +{ + int count_target; + int retval = 0; + unsigned int i, j; + cpumask_var_t covered_cpus; + struct acpi_processor *pr; + struct acpi_psd_package *pdomain; + struct acpi_processor *match_pr; + struct acpi_psd_package *match_pdomain; + + if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) + return -ENOMEM; + + mutex_lock(&performance_mutex); + + /* + * Check if another driver has already registered, and abort before + * changing pr->performance if it has. Check input data as well. + */ + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (!pr) { + /* Look only at processors in ACPI namespace */ + continue; + } + + if (pr->performance) { + retval = -EBUSY; + goto err_out; + } + + if (!performance || !per_cpu_ptr(performance, i)) { + retval = -EINVAL; + goto err_out; + } + } + + /* Call _PSD for all CPUs */ + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (!pr) + continue; + + pr->performance = per_cpu_ptr(performance, i); + cpumask_set_cpu(i, pr->performance->shared_cpu_map); + if (acpi_processor_get_psd(pr)) { + retval = -EINVAL; + continue; + } + } + if (retval) + goto err_ret; + + /* + * Now that we have _PSD data from all CPUs, lets setup P-state + * domain info. + */ + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (!pr) + continue; + + if (cpumask_test_cpu(i, covered_cpus)) + continue; + + pdomain = &(pr->performance->domain_info); + cpumask_set_cpu(i, pr->performance->shared_cpu_map); + cpumask_set_cpu(i, covered_cpus); + if (pdomain->num_processors <= 1) + continue; + + /* Validate the Domain info */ + count_target = pdomain->num_processors; + if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) + pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL; + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) + pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW; + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) + pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY; + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = per_cpu(processors, j); + if (!match_pr) + continue; + + match_pdomain = &(match_pr->performance->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* Here i and j are in the same domain */ + + if (match_pdomain->num_processors != count_target) { + retval = -EINVAL; + goto err_ret; + } + + if (pdomain->coord_type != match_pdomain->coord_type) { + retval = -EINVAL; + goto err_ret; + } + + cpumask_set_cpu(j, covered_cpus); + cpumask_set_cpu(j, pr->performance->shared_cpu_map); + } + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = per_cpu(processors, j); + if (!match_pr) + continue; + + match_pdomain = &(match_pr->performance->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + match_pr->performance->shared_type = + pr->performance->shared_type; + cpumask_copy(match_pr->performance->shared_cpu_map, + pr->performance->shared_cpu_map); + } + } + +err_ret: + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (!pr || !pr->performance) + continue; + + /* Assume no coordination on any error parsing domain info */ + if (retval) { + cpumask_clear(pr->performance->shared_cpu_map); + cpumask_set_cpu(i, pr->performance->shared_cpu_map); + pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL; + } + pr->performance = NULL; /* Will be set for real in register */ + } + +err_out: + mutex_unlock(&performance_mutex); + free_cpumask_var(covered_cpus); + return retval; +} +EXPORT_SYMBOL(acpi_processor_preregister_performance); + +int +acpi_processor_register_performance(struct acpi_processor_performance + *performance, unsigned int cpu) +{ + struct acpi_processor *pr; + + if (!(acpi_processor_ppc_status & PPC_REGISTERED)) + return -EINVAL; + + mutex_lock(&performance_mutex); + + pr = per_cpu(processors, cpu); + if (!pr) { + mutex_unlock(&performance_mutex); + return -ENODEV; + } + + if (pr->performance) { + mutex_unlock(&performance_mutex); + return -EBUSY; + } + + WARN_ON(!performance); + + pr->performance = performance; + + if (acpi_processor_get_performance_info(pr)) { + pr->performance = NULL; + mutex_unlock(&performance_mutex); + return -EIO; + } + + mutex_unlock(&performance_mutex); + return 0; +} + +EXPORT_SYMBOL(acpi_processor_register_performance); + +void +acpi_processor_unregister_performance(struct acpi_processor_performance + *performance, unsigned int cpu) +{ + struct acpi_processor *pr; + + mutex_lock(&performance_mutex); + + pr = per_cpu(processors, cpu); + if (!pr) { + mutex_unlock(&performance_mutex); + return; + } + + if (pr->performance) + kfree(pr->performance->states); + pr->performance = NULL; + + mutex_unlock(&performance_mutex); + + return; +} + +EXPORT_SYMBOL(acpi_processor_unregister_performance); diff --git a/kernel/drivers/acpi/processor_thermal.c b/kernel/drivers/acpi/processor_thermal.c new file mode 100644 index 000000000..e003663b2 --- /dev/null +++ b/kernel/drivers/acpi/processor_thermal.c @@ -0,0 +1,276 @@ +/* + * processor_thermal.c - Passive cooling submodule of the ACPI processor driver + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004 Dominik Brodowski + * Copyright (C) 2004 Anil S Keshavamurthy + * - Added processor hotplug support + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +#define ACPI_PROCESSOR_CLASS "processor" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("processor_thermal"); + +#ifdef CONFIG_CPU_FREQ + +/* If a passive cooling situation is detected, primarily CPUfreq is used, as it + * offers (in most cases) voltage scaling in addition to frequency scaling, and + * thus a cubic (instead of linear) reduction of energy. Also, we allow for + * _any_ cpufreq driver and not only the acpi-cpufreq driver. + */ + +#define CPUFREQ_THERMAL_MIN_STEP 0 +#define CPUFREQ_THERMAL_MAX_STEP 3 + +static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg); +static unsigned int acpi_thermal_cpufreq_is_init = 0; + +#define reduction_pctg(cpu) \ + per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu)) + +/* + * Emulate "per package data" using per cpu data (which should really be + * provided elsewhere) + * + * Note we can lose a CPU on cpu hotunplug, in this case we forget the state + * temporarily. Fortunately that's not a big issue here (I hope) + */ +static int phys_package_first_cpu(int cpu) +{ + int i; + int id = topology_physical_package_id(cpu); + + for_each_online_cpu(i) + if (topology_physical_package_id(i) == id) + return i; + return 0; +} + +static int cpu_has_cpufreq(unsigned int cpu) +{ + struct cpufreq_policy policy; + if (!acpi_thermal_cpufreq_is_init || cpufreq_get_policy(&policy, cpu)) + return 0; + return 1; +} + +static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct cpufreq_policy *policy = data; + unsigned long max_freq = 0; + + if (event != CPUFREQ_ADJUST) + goto out; + + max_freq = ( + policy->cpuinfo.max_freq * + (100 - reduction_pctg(policy->cpu) * 20) + ) / 100; + + cpufreq_verify_within_limits(policy, 0, max_freq); + + out: + return 0; +} + +static struct notifier_block acpi_thermal_cpufreq_notifier_block = { + .notifier_call = acpi_thermal_cpufreq_notifier, +}; + +static int cpufreq_get_max_state(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + return CPUFREQ_THERMAL_MAX_STEP; +} + +static int cpufreq_get_cur_state(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + return reduction_pctg(cpu); +} + +static int cpufreq_set_cur_state(unsigned int cpu, int state) +{ + int i; + + if (!cpu_has_cpufreq(cpu)) + return 0; + + reduction_pctg(cpu) = state; + + /* + * Update all the CPUs in the same package because they all + * contribute to the temperature and often share the same + * frequency. + */ + for_each_online_cpu(i) { + if (topology_physical_package_id(i) == + topology_physical_package_id(cpu)) + cpufreq_update_policy(i); + } + return 0; +} + +void acpi_thermal_cpufreq_init(void) +{ + int i; + + i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + if (!i) + acpi_thermal_cpufreq_is_init = 1; +} + +void acpi_thermal_cpufreq_exit(void) +{ + if (acpi_thermal_cpufreq_is_init) + cpufreq_unregister_notifier + (&acpi_thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + + acpi_thermal_cpufreq_is_init = 0; +} + +#else /* ! CONFIG_CPU_FREQ */ +static int cpufreq_get_max_state(unsigned int cpu) +{ + return 0; +} + +static int cpufreq_get_cur_state(unsigned int cpu) +{ + return 0; +} + +static int cpufreq_set_cur_state(unsigned int cpu, int state) +{ + return 0; +} + +#endif + +/* thermal cooling device callbacks */ +static int acpi_processor_max_state(struct acpi_processor *pr) +{ + int max_state = 0; + + /* + * There exists four states according to + * cpufreq_thermal_reduction_pctg. 0, 1, 2, 3 + */ + max_state += cpufreq_get_max_state(pr->id); + if (pr->flags.throttling) + max_state += (pr->throttling.state_count -1); + + return max_state; +} +static int +processor_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr; + + if (!device) + return -EINVAL; + + pr = acpi_driver_data(device); + if (!pr) + return -EINVAL; + + *state = acpi_processor_max_state(pr); + return 0; +} + +static int +processor_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *cur_state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr; + + if (!device) + return -EINVAL; + + pr = acpi_driver_data(device); + if (!pr) + return -EINVAL; + + *cur_state = cpufreq_get_cur_state(pr->id); + if (pr->flags.throttling) + *cur_state += pr->throttling.state; + return 0; +} + +static int +processor_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr; + int result = 0; + int max_pstate; + + if (!device) + return -EINVAL; + + pr = acpi_driver_data(device); + if (!pr) + return -EINVAL; + + max_pstate = cpufreq_get_max_state(pr->id); + + if (state > acpi_processor_max_state(pr)) + return -EINVAL; + + if (state <= max_pstate) { + if (pr->flags.throttling && pr->throttling.state) + result = acpi_processor_set_throttling(pr, 0, false); + cpufreq_set_cur_state(pr->id, state); + } else { + cpufreq_set_cur_state(pr->id, max_pstate); + result = acpi_processor_set_throttling(pr, + state - max_pstate, false); + } + return result; +} + +const struct thermal_cooling_device_ops processor_cooling_ops = { + .get_max_state = processor_get_max_state, + .get_cur_state = processor_get_cur_state, + .set_cur_state = processor_set_cur_state, +}; diff --git a/kernel/drivers/acpi/processor_throttling.c b/kernel/drivers/acpi/processor_throttling.c new file mode 100644 index 000000000..84243c32e --- /dev/null +++ b/kernel/drivers/acpi/processor_throttling.c @@ -0,0 +1,1264 @@ +/* + * processor_throttling.c - Throttling submodule of the ACPI processor driver + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004 Dominik Brodowski + * Copyright (C) 2004 Anil S Keshavamurthy + * - Added processor hotplug support + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +#define ACPI_PROCESSOR_CLASS "processor" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME("processor_throttling"); + +/* ignore_tpc: + * 0 -> acpi processor driver doesn't ignore _TPC values + * 1 -> acpi processor driver ignores _TPC values + */ +static int ignore_tpc; +module_param(ignore_tpc, int, 0644); +MODULE_PARM_DESC(ignore_tpc, "Disable broken BIOS _TPC throttling support"); + +struct throttling_tstate { + unsigned int cpu; /* cpu nr */ + int target_state; /* target T-state */ +}; + +struct acpi_processor_throttling_arg { + struct acpi_processor *pr; + int target_state; + bool force; +}; + +#define THROTTLING_PRECHANGE (1) +#define THROTTLING_POSTCHANGE (2) + +static int acpi_processor_get_throttling(struct acpi_processor *pr); +int acpi_processor_set_throttling(struct acpi_processor *pr, + int state, bool force); + +static int acpi_processor_update_tsd_coord(void) +{ + int count, count_target; + int retval = 0; + unsigned int i, j; + cpumask_var_t covered_cpus; + struct acpi_processor *pr, *match_pr; + struct acpi_tsd_package *pdomain, *match_pdomain; + struct acpi_processor_throttling *pthrottling, *match_pthrottling; + + if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) + return -ENOMEM; + + /* + * Now that we have _TSD data from all CPUs, lets setup T-state + * coordination between all CPUs. + */ + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (!pr) + continue; + + /* Basic validity check for domain info */ + pthrottling = &(pr->throttling); + + /* + * If tsd package for one cpu is invalid, the coordination + * among all CPUs is thought as invalid. + * Maybe it is ugly. + */ + if (!pthrottling->tsd_valid_flag) { + retval = -EINVAL; + break; + } + } + if (retval) + goto err_ret; + + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (!pr) + continue; + + if (cpumask_test_cpu(i, covered_cpus)) + continue; + pthrottling = &pr->throttling; + + pdomain = &(pthrottling->domain_info); + cpumask_set_cpu(i, pthrottling->shared_cpu_map); + cpumask_set_cpu(i, covered_cpus); + /* + * If the number of processor in the TSD domain is 1, it is + * unnecessary to parse the coordination for this CPU. + */ + if (pdomain->num_processors <= 1) + continue; + + /* Validate the Domain info */ + count_target = pdomain->num_processors; + count = 1; + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = per_cpu(processors, j); + if (!match_pr) + continue; + + match_pthrottling = &(match_pr->throttling); + match_pdomain = &(match_pthrottling->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* Here i and j are in the same domain. + * If two TSD packages have the same domain, they + * should have the same num_porcessors and + * coordination type. Otherwise it will be regarded + * as illegal. + */ + if (match_pdomain->num_processors != count_target) { + retval = -EINVAL; + goto err_ret; + } + + if (pdomain->coord_type != match_pdomain->coord_type) { + retval = -EINVAL; + goto err_ret; + } + + cpumask_set_cpu(j, covered_cpus); + cpumask_set_cpu(j, pthrottling->shared_cpu_map); + count++; + } + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = per_cpu(processors, j); + if (!match_pr) + continue; + + match_pthrottling = &(match_pr->throttling); + match_pdomain = &(match_pthrottling->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* + * If some CPUS have the same domain, they + * will have the same shared_cpu_map. + */ + cpumask_copy(match_pthrottling->shared_cpu_map, + pthrottling->shared_cpu_map); + } + } + +err_ret: + free_cpumask_var(covered_cpus); + + for_each_possible_cpu(i) { + pr = per_cpu(processors, i); + if (!pr) + continue; + + /* + * Assume no coordination on any error parsing domain info. + * The coordination type will be forced as SW_ALL. + */ + if (retval) { + pthrottling = &(pr->throttling); + cpumask_clear(pthrottling->shared_cpu_map); + cpumask_set_cpu(i, pthrottling->shared_cpu_map); + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } + } + + return retval; +} + +/* + * Update the T-state coordination after the _TSD + * data for all cpus is obtained. + */ +void acpi_processor_throttling_init(void) +{ + if (acpi_processor_update_tsd_coord()) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Assume no T-state coordination\n")); + } + + return; +} + +static int acpi_processor_throttling_notifier(unsigned long event, void *data) +{ + struct throttling_tstate *p_tstate = data; + struct acpi_processor *pr; + unsigned int cpu ; + int target_state; + struct acpi_processor_limit *p_limit; + struct acpi_processor_throttling *p_throttling; + + cpu = p_tstate->cpu; + pr = per_cpu(processors, cpu); + if (!pr) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid pr pointer\n")); + return 0; + } + if (!pr->flags.throttling) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Throttling control is " + "unsupported on CPU %d\n", cpu)); + return 0; + } + target_state = p_tstate->target_state; + p_throttling = &(pr->throttling); + switch (event) { + case THROTTLING_PRECHANGE: + /* + * Prechange event is used to choose one proper t-state, + * which meets the limits of thermal, user and _TPC. + */ + p_limit = &pr->limit; + if (p_limit->thermal.tx > target_state) + target_state = p_limit->thermal.tx; + if (p_limit->user.tx > target_state) + target_state = p_limit->user.tx; + if (pr->throttling_platform_limit > target_state) + target_state = pr->throttling_platform_limit; + if (target_state >= p_throttling->state_count) { + printk(KERN_WARNING + "Exceed the limit of T-state \n"); + target_state = p_throttling->state_count - 1; + } + p_tstate->target_state = target_state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PreChange Event:" + "target T-state of CPU %d is T%d\n", + cpu, target_state)); + break; + case THROTTLING_POSTCHANGE: + /* + * Postchange event is only used to update the + * T-state flag of acpi_processor_throttling. + */ + p_throttling->state = target_state; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PostChange Event:" + "CPU %d is switched to T%d\n", + cpu, target_state)); + break; + default: + printk(KERN_WARNING + "Unsupported Throttling notifier event\n"); + break; + } + + return 0; +} + +/* + * _TPC - Throttling Present Capabilities + */ +static int acpi_processor_get_platform_limit(struct acpi_processor *pr) +{ + acpi_status status = 0; + unsigned long long tpc = 0; + + if (!pr) + return -EINVAL; + + if (ignore_tpc) + goto end; + + status = acpi_evaluate_integer(pr->handle, "_TPC", NULL, &tpc); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TPC")); + } + return -ENODEV; + } + +end: + pr->throttling_platform_limit = (int)tpc; + return 0; +} + +int acpi_processor_tstate_has_changed(struct acpi_processor *pr) +{ + int result = 0; + int throttling_limit; + int current_state; + struct acpi_processor_limit *limit; + int target_state; + + if (ignore_tpc) + return 0; + + result = acpi_processor_get_platform_limit(pr); + if (result) { + /* Throttling Limit is unsupported */ + return result; + } + + throttling_limit = pr->throttling_platform_limit; + if (throttling_limit >= pr->throttling.state_count) { + /* Uncorrect Throttling Limit */ + return -EINVAL; + } + + current_state = pr->throttling.state; + if (current_state > throttling_limit) { + /* + * The current state can meet the requirement of + * _TPC limit. But it is reasonable that OSPM changes + * t-states from high to low for better performance. + * Of course the limit condition of thermal + * and user should be considered. + */ + limit = &pr->limit; + target_state = throttling_limit; + if (limit->thermal.tx > target_state) + target_state = limit->thermal.tx; + if (limit->user.tx > target_state) + target_state = limit->user.tx; + } else if (current_state == throttling_limit) { + /* + * Unnecessary to change the throttling state + */ + return 0; + } else { + /* + * If the current state is lower than the limit of _TPC, it + * will be forced to switch to the throttling state defined + * by throttling_platfor_limit. + * Because the previous state meets with the limit condition + * of thermal and user, it is unnecessary to check it again. + */ + target_state = throttling_limit; + } + return acpi_processor_set_throttling(pr, target_state, false); +} + +/* + * This function is used to reevaluate whether the T-state is valid + * after one CPU is onlined/offlined. + * It is noted that it won't reevaluate the following properties for + * the T-state. + * 1. Control method. + * 2. the number of supported T-state + * 3. TSD domain + */ +void acpi_processor_reevaluate_tstate(struct acpi_processor *pr, + unsigned long action) +{ + int result = 0; + + if (action == CPU_DEAD) { + /* When one CPU is offline, the T-state throttling + * will be invalidated. + */ + pr->flags.throttling = 0; + return; + } + /* the following is to recheck whether the T-state is valid for + * the online CPU + */ + if (!pr->throttling.state_count) { + /* If the number of T-state is invalid, it is + * invalidated. + */ + pr->flags.throttling = 0; + return; + } + pr->flags.throttling = 1; + + /* Disable throttling (if enabled). We'll let subsequent + * policy (e.g.thermal) decide to lower performance if it + * so chooses, but for now we'll crank up the speed. + */ + + result = acpi_processor_get_throttling(pr); + if (result) + goto end; + + if (pr->throttling.state) { + result = acpi_processor_set_throttling(pr, 0, false); + if (result) + goto end; + } + +end: + if (result) + pr->flags.throttling = 0; +} +/* + * _PTC - Processor Throttling Control (and status) register location + */ +static int acpi_processor_get_throttling_control(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *ptc = NULL; + union acpi_object obj = { 0 }; + struct acpi_processor_throttling *throttling; + + status = acpi_evaluate_object(pr->handle, "_PTC", NULL, &buffer); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTC")); + } + return -ENODEV; + } + + ptc = (union acpi_object *)buffer.pointer; + if (!ptc || (ptc->type != ACPI_TYPE_PACKAGE) + || (ptc->package.count != 2)) { + printk(KERN_ERR PREFIX "Invalid _PTC data\n"); + result = -EFAULT; + goto end; + } + + /* + * control_register + */ + + obj = ptc->package.elements[0]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_ptc_register)) + || (obj.buffer.pointer == NULL)) { + printk(KERN_ERR PREFIX + "Invalid _PTC data (control_register)\n"); + result = -EFAULT; + goto end; + } + memcpy(&pr->throttling.control_register, obj.buffer.pointer, + sizeof(struct acpi_ptc_register)); + + /* + * status_register + */ + + obj = ptc->package.elements[1]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_ptc_register)) + || (obj.buffer.pointer == NULL)) { + printk(KERN_ERR PREFIX "Invalid _PTC data (status_register)\n"); + result = -EFAULT; + goto end; + } + + memcpy(&pr->throttling.status_register, obj.buffer.pointer, + sizeof(struct acpi_ptc_register)); + + throttling = &pr->throttling; + + if ((throttling->control_register.bit_width + + throttling->control_register.bit_offset) > 32) { + printk(KERN_ERR PREFIX "Invalid _PTC control register\n"); + result = -EFAULT; + goto end; + } + + if ((throttling->status_register.bit_width + + throttling->status_register.bit_offset) > 32) { + printk(KERN_ERR PREFIX "Invalid _PTC status register\n"); + result = -EFAULT; + goto end; + } + + end: + kfree(buffer.pointer); + + return result; +} + +/* + * _TSS - Throttling Supported States + */ +static int acpi_processor_get_throttling_states(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; + struct acpi_buffer state = { 0, NULL }; + union acpi_object *tss = NULL; + int i; + + status = acpi_evaluate_object(pr->handle, "_TSS", NULL, &buffer); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TSS")); + } + return -ENODEV; + } + + tss = buffer.pointer; + if (!tss || (tss->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_ERR PREFIX "Invalid _TSS data\n"); + result = -EFAULT; + goto end; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", + tss->package.count)); + + pr->throttling.state_count = tss->package.count; + pr->throttling.states_tss = + kmalloc(sizeof(struct acpi_processor_tx_tss) * tss->package.count, + GFP_KERNEL); + if (!pr->throttling.states_tss) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < pr->throttling.state_count; i++) { + + struct acpi_processor_tx_tss *tx = + (struct acpi_processor_tx_tss *)&(pr->throttling. + states_tss[i]); + + state.length = sizeof(struct acpi_processor_tx_tss); + state.pointer = tx; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i)); + + status = acpi_extract_package(&(tss->package.elements[i]), + &format, &state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid _TSS data")); + result = -EFAULT; + kfree(pr->throttling.states_tss); + goto end; + } + + if (!tx->freqpercentage) { + printk(KERN_ERR PREFIX + "Invalid _TSS data: freq is zero\n"); + result = -EFAULT; + kfree(pr->throttling.states_tss); + goto end; + } + } + + end: + kfree(buffer.pointer); + + return result; +} + +/* + * _TSD - T-State Dependencies + */ +static int acpi_processor_get_tsd(struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; + struct acpi_buffer state = { 0, NULL }; + union acpi_object *tsd = NULL; + struct acpi_tsd_package *pdomain; + struct acpi_processor_throttling *pthrottling; + + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 0; + + status = acpi_evaluate_object(pr->handle, "_TSD", NULL, &buffer); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _TSD")); + } + return -ENODEV; + } + + tsd = buffer.pointer; + if (!tsd || (tsd->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_ERR PREFIX "Invalid _TSD data\n"); + result = -EFAULT; + goto end; + } + + if (tsd->package.count != 1) { + printk(KERN_ERR PREFIX "Invalid _TSD data\n"); + result = -EFAULT; + goto end; + } + + pdomain = &(pr->throttling.domain_info); + + state.length = sizeof(struct acpi_tsd_package); + state.pointer = pdomain; + + status = acpi_extract_package(&(tsd->package.elements[0]), + &format, &state); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Invalid _TSD data\n"); + result = -EFAULT; + goto end; + } + + if (pdomain->num_entries != ACPI_TSD_REV0_ENTRIES) { + printk(KERN_ERR PREFIX "Unknown _TSD:num_entries\n"); + result = -EFAULT; + goto end; + } + + if (pdomain->revision != ACPI_TSD_REV0_REVISION) { + printk(KERN_ERR PREFIX "Unknown _TSD:revision\n"); + result = -EFAULT; + goto end; + } + + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 1; + pthrottling->shared_type = pdomain->coord_type; + cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map); + /* + * If the coordination type is not defined in ACPI spec, + * the tsd_valid_flag will be clear and coordination type + * will be forecd as DOMAIN_COORD_TYPE_SW_ALL. + */ + if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && + pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && + pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { + pthrottling->tsd_valid_flag = 0; + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } + + end: + kfree(buffer.pointer); + return result; +} + +/* -------------------------------------------------------------------------- + Throttling Control + -------------------------------------------------------------------------- */ +static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr) +{ + int state = 0; + u32 value = 0; + u32 duty_mask = 0; + u32 duty_value = 0; + + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + pr->throttling.state = 0; + + duty_mask = pr->throttling.state_count - 1; + + duty_mask <<= pr->throttling.duty_offset; + + local_irq_disable(); + + value = inl(pr->throttling.address); + + /* + * Compute the current throttling state when throttling is enabled + * (bit 4 is on). + */ + if (value & 0x10) { + duty_value = value & duty_mask; + duty_value >>= pr->throttling.duty_offset; + + if (duty_value) + state = pr->throttling.state_count - duty_value; + } + + pr->throttling.state = state; + + local_irq_enable(); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling state is T%d (%d%% throttling applied)\n", + state, pr->throttling.states[state].performance)); + + return 0; +} + +#ifdef CONFIG_X86 +static int acpi_throttling_rdmsr(u64 *value) +{ + u64 msr_high, msr_low; + u64 msr = 0; + int ret = -1; + + if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) || + !this_cpu_has(X86_FEATURE_ACPI)) { + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + } else { + msr_low = 0; + msr_high = 0; + rdmsr_safe(MSR_IA32_THERM_CONTROL, + (u32 *)&msr_low , (u32 *) &msr_high); + msr = (msr_high << 32) | msr_low; + *value = (u64) msr; + ret = 0; + } + return ret; +} + +static int acpi_throttling_wrmsr(u64 value) +{ + int ret = -1; + u64 msr; + + if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) || + !this_cpu_has(X86_FEATURE_ACPI)) { + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + } else { + msr = value; + wrmsr_safe(MSR_IA32_THERM_CONTROL, + msr & 0xffffffff, msr >> 32); + ret = 0; + } + return ret; +} +#else +static int acpi_throttling_rdmsr(u64 *value) +{ + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + return -1; +} + +static int acpi_throttling_wrmsr(u64 value) +{ + printk(KERN_ERR PREFIX + "HARDWARE addr space,NOT supported yet\n"); + return -1; +} +#endif + +static int acpi_read_throttling_status(struct acpi_processor *pr, + u64 *value) +{ + u32 bit_width, bit_offset; + u32 ptc_value; + u64 ptc_mask; + struct acpi_processor_throttling *throttling; + int ret = -1; + + throttling = &pr->throttling; + switch (throttling->status_register.space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + bit_width = throttling->status_register.bit_width; + bit_offset = throttling->status_register.bit_offset; + + acpi_os_read_port((acpi_io_address) throttling->status_register. + address, &ptc_value, + (u32) (bit_width + bit_offset)); + ptc_mask = (1 << bit_width) - 1; + *value = (u64) ((ptc_value >> bit_offset) & ptc_mask); + ret = 0; + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + ret = acpi_throttling_rdmsr(value); + break; + default: + printk(KERN_ERR PREFIX "Unknown addr space %d\n", + (u32) (throttling->status_register.space_id)); + } + return ret; +} + +static int acpi_write_throttling_state(struct acpi_processor *pr, + u64 value) +{ + u32 bit_width, bit_offset; + u64 ptc_value; + u64 ptc_mask; + struct acpi_processor_throttling *throttling; + int ret = -1; + + throttling = &pr->throttling; + switch (throttling->control_register.space_id) { + case ACPI_ADR_SPACE_SYSTEM_IO: + bit_width = throttling->control_register.bit_width; + bit_offset = throttling->control_register.bit_offset; + ptc_mask = (1 << bit_width) - 1; + ptc_value = value & ptc_mask; + + acpi_os_write_port((acpi_io_address) throttling-> + control_register.address, + (u32) (ptc_value << bit_offset), + (u32) (bit_width + bit_offset)); + ret = 0; + break; + case ACPI_ADR_SPACE_FIXED_HARDWARE: + ret = acpi_throttling_wrmsr(value); + break; + default: + printk(KERN_ERR PREFIX "Unknown addr space %d\n", + (u32) (throttling->control_register.space_id)); + } + return ret; +} + +static int acpi_get_throttling_state(struct acpi_processor *pr, + u64 value) +{ + int i; + + for (i = 0; i < pr->throttling.state_count; i++) { + struct acpi_processor_tx_tss *tx = + (struct acpi_processor_tx_tss *)&(pr->throttling. + states_tss[i]); + if (tx->control == value) + return i; + } + return -1; +} + +static int acpi_get_throttling_value(struct acpi_processor *pr, + int state, u64 *value) +{ + int ret = -1; + + if (state >= 0 && state <= pr->throttling.state_count) { + struct acpi_processor_tx_tss *tx = + (struct acpi_processor_tx_tss *)&(pr->throttling. + states_tss[state]); + *value = tx->control; + ret = 0; + } + return ret; +} + +static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) +{ + int state = 0; + int ret; + u64 value; + + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + pr->throttling.state = 0; + + value = 0; + ret = acpi_read_throttling_status(pr, &value); + if (ret >= 0) { + state = acpi_get_throttling_state(pr, value); + if (state == -1) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Invalid throttling state, reset\n")); + state = 0; + ret = acpi_processor_set_throttling(pr, state, true); + if (ret) + return ret; + } + pr->throttling.state = state; + } + + return 0; +} + +static int acpi_processor_get_throttling(struct acpi_processor *pr) +{ + cpumask_var_t saved_mask; + int ret; + + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + if (!alloc_cpumask_var(&saved_mask, GFP_KERNEL)) + return -ENOMEM; + + /* + * Migrate task to the cpu pointed by pr. + */ + cpumask_copy(saved_mask, ¤t->cpus_allowed); + /* FIXME: use work_on_cpu() */ + if (set_cpus_allowed_ptr(current, cpumask_of(pr->id))) { + /* Can't migrate to the target pr->id CPU. Exit */ + free_cpumask_var(saved_mask); + return -ENODEV; + } + ret = pr->throttling.acpi_processor_get_throttling(pr); + /* restore the previous state */ + set_cpus_allowed_ptr(current, saved_mask); + free_cpumask_var(saved_mask); + + return ret; +} + +static int acpi_processor_get_fadt_info(struct acpi_processor *pr) +{ + int i, step; + + if (!pr->throttling.address) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n")); + return -EINVAL; + } else if (!pr->throttling.duty_width) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling states\n")); + return -EINVAL; + } + /* TBD: Support duty_cycle values that span bit 4. */ + else if ((pr->throttling.duty_offset + pr->throttling.duty_width) > 4) { + printk(KERN_WARNING PREFIX "duty_cycle spans bit 4\n"); + return -EINVAL; + } + + pr->throttling.state_count = 1 << acpi_gbl_FADT.duty_width; + + /* + * Compute state values. Note that throttling displays a linear power + * performance relationship (at 50% performance the CPU will consume + * 50% power). Values are in 1/10th of a percent to preserve accuracy. + */ + + step = (1000 / pr->throttling.state_count); + + for (i = 0; i < pr->throttling.state_count; i++) { + pr->throttling.states[i].performance = 1000 - step * i; + pr->throttling.states[i].power = 1000 - step * i; + } + return 0; +} + +static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr, + int state, bool force) +{ + u32 value = 0; + u32 duty_mask = 0; + u32 duty_value = 0; + + if (!pr) + return -EINVAL; + + if ((state < 0) || (state > (pr->throttling.state_count - 1))) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + if (!force && (state == pr->throttling.state)) + return 0; + + if (state < pr->throttling_platform_limit) + return -EPERM; + /* + * Calculate the duty_value and duty_mask. + */ + if (state) { + duty_value = pr->throttling.state_count - state; + + duty_value <<= pr->throttling.duty_offset; + + /* Used to clear all duty_value bits */ + duty_mask = pr->throttling.state_count - 1; + + duty_mask <<= acpi_gbl_FADT.duty_offset; + duty_mask = ~duty_mask; + } + + local_irq_disable(); + + /* + * Disable throttling by writing a 0 to bit 4. Note that we must + * turn it off before you can change the duty_value. + */ + value = inl(pr->throttling.address); + if (value & 0x10) { + value &= 0xFFFFFFEF; + outl(value, pr->throttling.address); + } + + /* + * Write the new duty_value and then enable throttling. Note + * that a state value of 0 leaves throttling disabled. + */ + if (state) { + value &= duty_mask; + value |= duty_value; + outl(value, pr->throttling.address); + + value |= 0x00000010; + outl(value, pr->throttling.address); + } + + pr->throttling.state = state; + + local_irq_enable(); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling state set to T%d (%d%%)\n", state, + (pr->throttling.states[state].performance ? pr-> + throttling.states[state].performance / 10 : 0))); + + return 0; +} + +static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr, + int state, bool force) +{ + int ret; + u64 value; + + if (!pr) + return -EINVAL; + + if ((state < 0) || (state > (pr->throttling.state_count - 1))) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + if (!force && (state == pr->throttling.state)) + return 0; + + if (state < pr->throttling_platform_limit) + return -EPERM; + + value = 0; + ret = acpi_get_throttling_value(pr, state, &value); + if (ret >= 0) { + acpi_write_throttling_state(pr, value); + pr->throttling.state = state; + } + + return 0; +} + +static long acpi_processor_throttling_fn(void *data) +{ + struct acpi_processor_throttling_arg *arg = data; + struct acpi_processor *pr = arg->pr; + + return pr->throttling.acpi_processor_set_throttling(pr, + arg->target_state, arg->force); +} + +int acpi_processor_set_throttling(struct acpi_processor *pr, + int state, bool force) +{ + int ret = 0; + unsigned int i; + struct acpi_processor *match_pr; + struct acpi_processor_throttling *p_throttling; + struct acpi_processor_throttling_arg arg; + struct throttling_tstate t_state; + + if (!pr) + return -EINVAL; + + if (!pr->flags.throttling) + return -ENODEV; + + if ((state < 0) || (state > (pr->throttling.state_count - 1))) + return -EINVAL; + + if (cpu_is_offline(pr->id)) { + /* + * the cpu pointed by pr->id is offline. Unnecessary to change + * the throttling state any more. + */ + return -ENODEV; + } + + t_state.target_state = state; + p_throttling = &(pr->throttling); + + /* + * The throttling notifier will be called for every + * affected cpu in order to get one proper T-state. + * The notifier event is THROTTLING_PRECHANGE. + */ + for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) { + t_state.cpu = i; + acpi_processor_throttling_notifier(THROTTLING_PRECHANGE, + &t_state); + } + /* + * The function of acpi_processor_set_throttling will be called + * to switch T-state. If the coordination type is SW_ALL or HW_ALL, + * it is necessary to call it for every affected cpu. Otherwise + * it can be called only for the cpu pointed by pr. + */ + if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) { + arg.pr = pr; + arg.target_state = state; + arg.force = force; + ret = work_on_cpu(pr->id, acpi_processor_throttling_fn, &arg); + } else { + /* + * When the T-state coordination is SW_ALL or HW_ALL, + * it is necessary to set T-state for every affected + * cpus. + */ + for_each_cpu_and(i, cpu_online_mask, + p_throttling->shared_cpu_map) { + match_pr = per_cpu(processors, i); + /* + * If the pointer is invalid, we will report the + * error message and continue. + */ + if (!match_pr) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Invalid Pointer for CPU %d\n", i)); + continue; + } + /* + * If the throttling control is unsupported on CPU i, + * we will report the error message and continue. + */ + if (!match_pr->flags.throttling) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling Control is unsupported " + "on CPU %d\n", i)); + continue; + } + + arg.pr = match_pr; + arg.target_state = state; + arg.force = force; + ret = work_on_cpu(pr->id, acpi_processor_throttling_fn, + &arg); + } + } + /* + * After the set_throttling is called, the + * throttling notifier is called for every + * affected cpu to update the T-states. + * The notifier event is THROTTLING_POSTCHANGE + */ + for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) { + t_state.cpu = i; + acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE, + &t_state); + } + + return ret; +} + +int acpi_processor_get_throttling_info(struct acpi_processor *pr) +{ + int result = 0; + struct acpi_processor_throttling *pthrottling; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", + pr->throttling.address, + pr->throttling.duty_offset, + pr->throttling.duty_width)); + + /* + * Evaluate _PTC, _TSS and _TPC + * They must all be present or none of them can be used. + */ + if (acpi_processor_get_throttling_control(pr) || + acpi_processor_get_throttling_states(pr) || + acpi_processor_get_platform_limit(pr)) + { + pr->throttling.acpi_processor_get_throttling = + &acpi_processor_get_throttling_fadt; + pr->throttling.acpi_processor_set_throttling = + &acpi_processor_set_throttling_fadt; + if (acpi_processor_get_fadt_info(pr)) + return 0; + } else { + pr->throttling.acpi_processor_get_throttling = + &acpi_processor_get_throttling_ptc; + pr->throttling.acpi_processor_set_throttling = + &acpi_processor_set_throttling_ptc; + } + + /* + * If TSD package for one CPU can't be parsed successfully, it means + * that this CPU will have no coordination with other CPUs. + */ + if (acpi_processor_get_tsd(pr)) { + pthrottling = &pr->throttling; + pthrottling->tsd_valid_flag = 0; + cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map); + pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL; + } + + /* + * PIIX4 Errata: We don't support throttling on the original PIIX4. + * This shouldn't be an issue as few (if any) mobile systems ever + * used this part. + */ + if (errata.piix4.throttle) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling not supported on PIIX4 A- or B-step\n")); + return 0; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", + pr->throttling.state_count)); + + pr->flags.throttling = 1; + + /* + * Disable throttling (if enabled). We'll let subsequent policy (e.g. + * thermal) decide to lower performance if it so chooses, but for now + * we'll crank up the speed. + */ + + result = acpi_processor_get_throttling(pr); + if (result) + goto end; + + if (pr->throttling.state) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Disabling throttling (was T%d)\n", + pr->throttling.state)); + result = acpi_processor_set_throttling(pr, 0, false); + if (result) + goto end; + } + + end: + if (result) + pr->flags.throttling = 0; + + return result; +} + diff --git a/kernel/drivers/acpi/property.c b/kernel/drivers/acpi/property.c new file mode 100644 index 000000000..0d083736e --- /dev/null +++ b/kernel/drivers/acpi/property.c @@ -0,0 +1,551 @@ +/* + * ACPI device specific properties support. + * + * Copyright (C) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Mika Westerberg + * Darren Hart + * Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "internal.h" + +/* ACPI _DSD device properties UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ +static const u8 prp_uuid[16] = { + 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d, + 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01 +}; + +static bool acpi_property_value_ok(const union acpi_object *value) +{ + int j; + + /* + * The value must be an integer, a string, a reference, or a package + * whose every element must be an integer, a string, or a reference. + */ + switch (value->type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_LOCAL_REFERENCE: + return true; + + case ACPI_TYPE_PACKAGE: + for (j = 0; j < value->package.count; j++) + switch (value->package.elements[j].type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_LOCAL_REFERENCE: + continue; + + default: + return false; + } + + return true; + } + return false; +} + +static bool acpi_properties_format_valid(const union acpi_object *properties) +{ + int i; + + for (i = 0; i < properties->package.count; i++) { + const union acpi_object *property; + + property = &properties->package.elements[i]; + /* + * Only two elements allowed, the first one must be a string and + * the second one has to satisfy certain conditions. + */ + if (property->package.count != 2 + || property->package.elements[0].type != ACPI_TYPE_STRING + || !acpi_property_value_ok(&property->package.elements[1])) + return false; + } + return true; +} + +static void acpi_init_of_compatible(struct acpi_device *adev) +{ + const union acpi_object *of_compatible; + struct acpi_hardware_id *hwid; + bool acpi_of = false; + int ret; + + /* + * Check if the special PRP0001 ACPI ID is present and in that + * case we fill in Device Tree compatible properties for this + * device. + */ + list_for_each_entry(hwid, &adev->pnp.ids, list) { + if (!strcmp(hwid->id, "PRP0001")) { + acpi_of = true; + break; + } + } + + if (!acpi_of) + return; + + ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING, + &of_compatible); + if (ret) { + ret = acpi_dev_get_property(adev, "compatible", + ACPI_TYPE_STRING, &of_compatible); + if (ret) { + acpi_handle_warn(adev->handle, + "PRP0001 requires compatible property\n"); + return; + } + } + adev->data.of_compatible = of_compatible; +} + +void acpi_init_properties(struct acpi_device *adev) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + const union acpi_object *desc; + acpi_status status; + int i; + + status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return; + + desc = buf.pointer; + if (desc->package.count % 2) + goto fail; + + /* Look for the device properties UUID. */ + for (i = 0; i < desc->package.count; i += 2) { + const union acpi_object *uuid, *properties; + + uuid = &desc->package.elements[i]; + properties = &desc->package.elements[i + 1]; + + /* + * The first element must be a UUID and the second one must be + * a package. + */ + if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16 + || properties->type != ACPI_TYPE_PACKAGE) + break; + + if (memcmp(uuid->buffer.pointer, prp_uuid, sizeof(prp_uuid))) + continue; + + /* + * We found the matching UUID. Now validate the format of the + * package immediately following it. + */ + if (!acpi_properties_format_valid(properties)) + break; + + adev->data.pointer = buf.pointer; + adev->data.properties = properties; + + acpi_init_of_compatible(adev); + return; + } + + fail: + dev_warn(&adev->dev, "Returned _DSD data is not valid, skipping\n"); + ACPI_FREE(buf.pointer); +} + +void acpi_free_properties(struct acpi_device *adev) +{ + ACPI_FREE((void *)adev->data.pointer); + adev->data.of_compatible = NULL; + adev->data.pointer = NULL; + adev->data.properties = NULL; +} + +/** + * acpi_dev_get_property - return an ACPI property with given name + * @adev: ACPI device to get property + * @name: Name of the property + * @type: Expected property type + * @obj: Location to store the property value (if not %NULL) + * + * Look up a property with @name and store a pointer to the resulting ACPI + * object at the location pointed to by @obj if found. + * + * Callers must not attempt to free the returned objects. These objects will be + * freed by the ACPI core automatically during the removal of @adev. + * + * Return: %0 if property with @name has been found (success), + * %-EINVAL if the arguments are invalid, + * %-ENODATA if the property doesn't exist, + * %-EPROTO if the property value type doesn't match @type. + */ +int acpi_dev_get_property(struct acpi_device *adev, const char *name, + acpi_object_type type, const union acpi_object **obj) +{ + const union acpi_object *properties; + int i; + + if (!adev || !name) + return -EINVAL; + + if (!adev->data.pointer || !adev->data.properties) + return -ENODATA; + + properties = adev->data.properties; + for (i = 0; i < properties->package.count; i++) { + const union acpi_object *propname, *propvalue; + const union acpi_object *property; + + property = &properties->package.elements[i]; + + propname = &property->package.elements[0]; + propvalue = &property->package.elements[1]; + + if (!strcmp(name, propname->string.pointer)) { + if (type != ACPI_TYPE_ANY && propvalue->type != type) + return -EPROTO; + else if (obj) + *obj = propvalue; + + return 0; + } + } + return -ENODATA; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property); + +/** + * acpi_dev_get_property_array - return an ACPI array property with given name + * @adev: ACPI device to get property + * @name: Name of the property + * @type: Expected type of array elements + * @obj: Location to store a pointer to the property value (if not NULL) + * + * Look up an array property with @name and store a pointer to the resulting + * ACPI object at the location pointed to by @obj if found. + * + * Callers must not attempt to free the returned objects. Those objects will be + * freed by the ACPI core automatically during the removal of @adev. + * + * Return: %0 if array property (package) with @name has been found (success), + * %-EINVAL if the arguments are invalid, + * %-ENODATA if the property doesn't exist, + * %-EPROTO if the property is not a package or the type of its elements + * doesn't match @type. + */ +int acpi_dev_get_property_array(struct acpi_device *adev, const char *name, + acpi_object_type type, + const union acpi_object **obj) +{ + const union acpi_object *prop; + int ret, i; + + ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &prop); + if (ret) + return ret; + + if (type != ACPI_TYPE_ANY) { + /* Check that all elements are of correct type. */ + for (i = 0; i < prop->package.count; i++) + if (prop->package.elements[i].type != type) + return -EPROTO; + } + if (obj) + *obj = prop; + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_array); + +/** + * acpi_dev_get_property_reference - returns handle to the referenced object + * @adev: ACPI device to get property + * @name: Name of the property + * @index: Index of the reference to return + * @args: Location to store the returned reference with optional arguments + * + * Find property with @name, verifify that it is a package containing at least + * one object reference and if so, store the ACPI device object pointer to the + * target object in @args->adev. If the reference includes arguments, store + * them in the @args->args[] array. + * + * If there's more than one reference in the property value package, @index is + * used to select the one to return. + * + * Return: %0 on success, negative error code on failure. + */ +int acpi_dev_get_property_reference(struct acpi_device *adev, + const char *name, size_t index, + struct acpi_reference_args *args) +{ + const union acpi_object *element, *end; + const union acpi_object *obj; + struct acpi_device *device; + int ret, idx = 0; + + ret = acpi_dev_get_property(adev, name, ACPI_TYPE_ANY, &obj); + if (ret) + return ret; + + /* + * The simplest case is when the value is a single reference. Just + * return that reference then. + */ + if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) { + if (index) + return -EINVAL; + + ret = acpi_bus_get_device(obj->reference.handle, &device); + if (ret) + return ret; + + args->adev = device; + args->nargs = 0; + return 0; + } + + /* + * If it is not a single reference, then it is a package of + * references followed by number of ints as follows: + * + * Package () { REF, INT, REF, INT, INT } + * + * The index argument is then used to determine which reference + * the caller wants (along with the arguments). + */ + if (obj->type != ACPI_TYPE_PACKAGE || index >= obj->package.count) + return -EPROTO; + + element = obj->package.elements; + end = element + obj->package.count; + + while (element < end) { + u32 nargs, i; + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) + return -EPROTO; + + ret = acpi_bus_get_device(element->reference.handle, &device); + if (ret) + return -ENODEV; + + element++; + nargs = 0; + + /* assume following integer elements are all args */ + for (i = 0; element + i < end; i++) { + int type = element[i].type; + + if (type == ACPI_TYPE_INTEGER) + nargs++; + else if (type == ACPI_TYPE_LOCAL_REFERENCE) + break; + else + return -EPROTO; + } + + if (idx++ == index) { + args->adev = device; + args->nargs = nargs; + for (i = 0; i < nargs; i++) + args->args[i] = element[i].integer.value; + + return 0; + } + + element += nargs; + } + + return -EPROTO; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference); + +int acpi_dev_prop_get(struct acpi_device *adev, const char *propname, + void **valptr) +{ + return acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, + (const union acpi_object **)valptr); +} + +int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val) +{ + const union acpi_object *obj; + int ret; + + if (!val) + return -EINVAL; + + if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) { + ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_INTEGER, &obj); + if (ret) + return ret; + + switch (proptype) { + case DEV_PROP_U8: + if (obj->integer.value > U8_MAX) + return -EOVERFLOW; + *(u8 *)val = obj->integer.value; + break; + case DEV_PROP_U16: + if (obj->integer.value > U16_MAX) + return -EOVERFLOW; + *(u16 *)val = obj->integer.value; + break; + case DEV_PROP_U32: + if (obj->integer.value > U32_MAX) + return -EOVERFLOW; + *(u32 *)val = obj->integer.value; + break; + default: + *(u64 *)val = obj->integer.value; + break; + } + } else if (proptype == DEV_PROP_STRING) { + ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_STRING, &obj); + if (ret) + return ret; + + *(char **)val = obj->string.pointer; + } else { + ret = -EINVAL; + } + return ret; +} + +static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val, + size_t nval) +{ + int i; + + for (i = 0; i < nval; i++) { + if (items[i].type != ACPI_TYPE_INTEGER) + return -EPROTO; + if (items[i].integer.value > U8_MAX) + return -EOVERFLOW; + + val[i] = items[i].integer.value; + } + return 0; +} + +static int acpi_copy_property_array_u16(const union acpi_object *items, + u16 *val, size_t nval) +{ + int i; + + for (i = 0; i < nval; i++) { + if (items[i].type != ACPI_TYPE_INTEGER) + return -EPROTO; + if (items[i].integer.value > U16_MAX) + return -EOVERFLOW; + + val[i] = items[i].integer.value; + } + return 0; +} + +static int acpi_copy_property_array_u32(const union acpi_object *items, + u32 *val, size_t nval) +{ + int i; + + for (i = 0; i < nval; i++) { + if (items[i].type != ACPI_TYPE_INTEGER) + return -EPROTO; + if (items[i].integer.value > U32_MAX) + return -EOVERFLOW; + + val[i] = items[i].integer.value; + } + return 0; +} + +static int acpi_copy_property_array_u64(const union acpi_object *items, + u64 *val, size_t nval) +{ + int i; + + for (i = 0; i < nval; i++) { + if (items[i].type != ACPI_TYPE_INTEGER) + return -EPROTO; + + val[i] = items[i].integer.value; + } + return 0; +} + +static int acpi_copy_property_array_string(const union acpi_object *items, + char **val, size_t nval) +{ + int i; + + for (i = 0; i < nval; i++) { + if (items[i].type != ACPI_TYPE_STRING) + return -EPROTO; + + val[i] = items[i].string.pointer; + } + return 0; +} + +int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval) +{ + const union acpi_object *obj; + const union acpi_object *items; + int ret; + + if (val && nval == 1) { + ret = acpi_dev_prop_read_single(adev, propname, proptype, val); + if (!ret) + return ret; + } + + ret = acpi_dev_get_property_array(adev, propname, ACPI_TYPE_ANY, &obj); + if (ret) + return ret; + + if (!val) + return obj->package.count; + else if (nval <= 0) + return -EINVAL; + + if (nval > obj->package.count) + return -EOVERFLOW; + + items = obj->package.elements; + switch (proptype) { + case DEV_PROP_U8: + ret = acpi_copy_property_array_u8(items, (u8 *)val, nval); + break; + case DEV_PROP_U16: + ret = acpi_copy_property_array_u16(items, (u16 *)val, nval); + break; + case DEV_PROP_U32: + ret = acpi_copy_property_array_u32(items, (u32 *)val, nval); + break; + case DEV_PROP_U64: + ret = acpi_copy_property_array_u64(items, (u64 *)val, nval); + break; + case DEV_PROP_STRING: + ret = acpi_copy_property_array_string(items, (char **)val, nval); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} diff --git a/kernel/drivers/acpi/reboot.c b/kernel/drivers/acpi/reboot.c new file mode 100644 index 000000000..a6c77e8b3 --- /dev/null +++ b/kernel/drivers/acpi/reboot.c @@ -0,0 +1,54 @@ + +#include +#include +#include + +void acpi_reboot(void) +{ + struct acpi_generic_address *rr; + struct pci_bus *bus0; + u8 reset_value; + unsigned int devfn; + + if (acpi_disabled) + return; + + rr = &acpi_gbl_FADT.reset_register; + + /* ACPI reset register was only introduced with v2 of the FADT */ + + if (acpi_gbl_FADT.header.revision < 2) + return; + + /* Is the reset register supported? The spec says we should be + * checking the bit width and bit offset, but Windows ignores + * these fields */ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER)) + return; + + reset_value = acpi_gbl_FADT.reset_value; + + /* The reset register can only exist in I/O, Memory or PCI config space + * on a device on bus 0. */ + switch (rr->space_id) { + case ACPI_ADR_SPACE_PCI_CONFIG: + /* The reset register can only live on bus 0. */ + bus0 = pci_find_bus(0, 0); + if (!bus0) + return; + /* Form PCI device/function pair. */ + devfn = PCI_DEVFN((rr->address >> 32) & 0xffff, + (rr->address >> 16) & 0xffff); + printk(KERN_DEBUG "Resetting with ACPI PCI RESET_REG."); + /* Write the value that resets us. */ + pci_bus_write_config_byte(bus0, devfn, + (rr->address & 0xffff), reset_value); + break; + + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + case ACPI_ADR_SPACE_SYSTEM_IO: + printk(KERN_DEBUG "ACPI MEMORY or I/O RESET_REG.\n"); + acpi_reset(); + break; + } +} diff --git a/kernel/drivers/acpi/resource.c b/kernel/drivers/acpi/resource.c new file mode 100644 index 000000000..fcb7807ea --- /dev/null +++ b/kernel/drivers/acpi/resource.c @@ -0,0 +1,783 @@ +/* + * drivers/acpi/resource.c - ACPI device resources interpretation. + * + * Copyright (C) 2012, Intel Corp. + * Author: Rafael J. Wysocki + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_X86 +#define valid_IRQ(i) (((i) != 0) && ((i) != 2)) +#else +#define valid_IRQ(i) (true) +#endif + +static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io) +{ + u64 reslen = end - start + 1; + + /* + * CHECKME: len might be required to check versus a minimum + * length as well. 1 for io is fine, but for memory it does + * not make any sense at all. + * Note: some BIOSes report incorrect length for ACPI address space + * descriptor, so remove check of 'reslen == len' to avoid regression. + */ + if (len && reslen && start <= end) + return true; + + pr_debug("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n", + io ? "io" : "mem", start, end, len); + + return false; +} + +static void acpi_dev_memresource_flags(struct resource *res, u64 len, + u8 write_protect) +{ + res->flags = IORESOURCE_MEM; + + if (!acpi_dev_resource_len_valid(res->start, res->end, len, false)) + res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; + + if (write_protect == ACPI_READ_WRITE_MEMORY) + res->flags |= IORESOURCE_MEM_WRITEABLE; +} + +static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len, + u8 write_protect) +{ + res->start = start; + res->end = start + len - 1; + acpi_dev_memresource_flags(res, len, write_protect); +} + +/** + * acpi_dev_resource_memory - Extract ACPI memory resource information. + * @ares: Input ACPI resource object. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents a memory resource and + * if that's the case, use the information in it to populate the generic + * resource object pointed to by @res. + * + * Return: + * 1) false with res->flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource + * 3) true: valid assigned resource + */ +bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) +{ + struct acpi_resource_memory24 *memory24; + struct acpi_resource_memory32 *memory32; + struct acpi_resource_fixed_memory32 *fixed_memory32; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_MEMORY24: + memory24 = &ares->data.memory24; + acpi_dev_get_memresource(res, memory24->minimum << 8, + memory24->address_length << 8, + memory24->write_protect); + break; + case ACPI_RESOURCE_TYPE_MEMORY32: + memory32 = &ares->data.memory32; + acpi_dev_get_memresource(res, memory32->minimum, + memory32->address_length, + memory32->write_protect); + break; + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + fixed_memory32 = &ares->data.fixed_memory32; + acpi_dev_get_memresource(res, fixed_memory32->address, + fixed_memory32->address_length, + fixed_memory32->write_protect); + break; + default: + res->flags = 0; + return false; + } + + return !(res->flags & IORESOURCE_DISABLED); +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); + +static void acpi_dev_ioresource_flags(struct resource *res, u64 len, + u8 io_decode) +{ + res->flags = IORESOURCE_IO; + + if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) + res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; + + if (res->end >= 0x10003) + res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; + + if (io_decode == ACPI_DECODE_16) + res->flags |= IORESOURCE_IO_16BIT_ADDR; +} + +static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, + u8 io_decode) +{ + res->start = start; + res->end = start + len - 1; + acpi_dev_ioresource_flags(res, len, io_decode); +} + +/** + * acpi_dev_resource_io - Extract ACPI I/O resource information. + * @ares: Input ACPI resource object. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents an I/O resource and + * if that's the case, use the information in it to populate the generic + * resource object pointed to by @res. + * + * Return: + * 1) false with res->flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource + * 3) true: valid assigned resource + */ +bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res) +{ + struct acpi_resource_io *io; + struct acpi_resource_fixed_io *fixed_io; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_IO: + io = &ares->data.io; + acpi_dev_get_ioresource(res, io->minimum, + io->address_length, + io->io_decode); + break; + case ACPI_RESOURCE_TYPE_FIXED_IO: + fixed_io = &ares->data.fixed_io; + acpi_dev_get_ioresource(res, fixed_io->address, + fixed_io->address_length, + ACPI_DECODE_10); + break; + default: + res->flags = 0; + return false; + } + + return !(res->flags & IORESOURCE_DISABLED); +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_io); + +static bool acpi_decode_space(struct resource_win *win, + struct acpi_resource_address *addr, + struct acpi_address64_attribute *attr) +{ + u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16; + bool wp = addr->info.mem.write_protect; + u64 len = attr->address_length; + struct resource *res = &win->res; + + /* + * Filter out invalid descriptor according to ACPI Spec 5.0, section + * 6.4.3.5 Address Space Resource Descriptors. + */ + if ((addr->min_address_fixed != addr->max_address_fixed && len) || + (addr->min_address_fixed && addr->max_address_fixed && !len)) + pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n", + addr->min_address_fixed, addr->max_address_fixed, len); + + res->start = attr->minimum; + res->end = attr->maximum; + + /* + * For bridges that translate addresses across the bridge, + * translation_offset is the offset that must be added to the + * address on the secondary side to obtain the address on the + * primary side. Non-bridge devices must list 0 for all Address + * Translation offset bits. + */ + if (addr->producer_consumer == ACPI_PRODUCER) { + res->start += attr->translation_offset; + res->end += attr->translation_offset; + } else if (attr->translation_offset) { + pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n", + attr->translation_offset); + } + + switch (addr->resource_type) { + case ACPI_MEMORY_RANGE: + acpi_dev_memresource_flags(res, len, wp); + break; + case ACPI_IO_RANGE: + acpi_dev_ioresource_flags(res, len, iodec); + break; + case ACPI_BUS_NUMBER_RANGE: + res->flags = IORESOURCE_BUS; + break; + default: + return false; + } + + win->offset = attr->translation_offset; + + if (addr->producer_consumer == ACPI_PRODUCER) + res->flags |= IORESOURCE_WINDOW; + + if (addr->info.mem.caching == ACPI_PREFETCHABLE_MEMORY) + res->flags |= IORESOURCE_PREFETCH; + + return !(res->flags & IORESOURCE_DISABLED); +} + +/** + * acpi_dev_resource_address_space - Extract ACPI address space information. + * @ares: Input ACPI resource object. + * @win: Output generic resource object. + * + * Check if the given ACPI resource object represents an address space resource + * and if that's the case, use the information in it to populate the generic + * resource object pointed to by @win. + * + * Return: + * 1) false with win->res.flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned + * resource + * 3) true: valid assigned resource + */ +bool acpi_dev_resource_address_space(struct acpi_resource *ares, + struct resource_win *win) +{ + struct acpi_resource_address64 addr; + + win->res.flags = 0; + if (ACPI_FAILURE(acpi_resource_to_address64(ares, &addr))) + return false; + + return acpi_decode_space(win, (struct acpi_resource_address *)&addr, + &addr.address); +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space); + +/** + * acpi_dev_resource_ext_address_space - Extract ACPI address space information. + * @ares: Input ACPI resource object. + * @win: Output generic resource object. + * + * Check if the given ACPI resource object represents an extended address space + * resource and if that's the case, use the information in it to populate the + * generic resource object pointed to by @win. + * + * Return: + * 1) false with win->res.flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned + * resource + * 3) true: valid assigned resource + */ +bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, + struct resource_win *win) +{ + struct acpi_resource_extended_address64 *ext_addr; + + win->res.flags = 0; + if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64) + return false; + + ext_addr = &ares->data.ext_address64; + + return acpi_decode_space(win, (struct acpi_resource_address *)ext_addr, + &ext_addr->address); +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space); + +/** + * acpi_dev_irq_flags - Determine IRQ resource flags. + * @triggering: Triggering type as provided by ACPI. + * @polarity: Interrupt polarity as provided by ACPI. + * @shareable: Whether or not the interrupt is shareable. + */ +unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable) +{ + unsigned long flags; + + if (triggering == ACPI_LEVEL_SENSITIVE) + flags = polarity == ACPI_ACTIVE_LOW ? + IORESOURCE_IRQ_LOWLEVEL : IORESOURCE_IRQ_HIGHLEVEL; + else + flags = polarity == ACPI_ACTIVE_LOW ? + IORESOURCE_IRQ_LOWEDGE : IORESOURCE_IRQ_HIGHEDGE; + + if (shareable == ACPI_SHARED) + flags |= IORESOURCE_IRQ_SHAREABLE; + + return flags | IORESOURCE_IRQ; +} +EXPORT_SYMBOL_GPL(acpi_dev_irq_flags); + +static void acpi_dev_irqresource_disabled(struct resource *res, u32 gsi) +{ + res->start = gsi; + res->end = gsi; + res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED | IORESOURCE_UNSET; +} + +static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, + u8 triggering, u8 polarity, u8 shareable, + bool legacy) +{ + int irq, p, t; + + if (!valid_IRQ(gsi)) { + acpi_dev_irqresource_disabled(res, gsi); + return; + } + + /* + * In IO-APIC mode, use overrided attribute. Two reasons: + * 1. BIOS bug in DSDT + * 2. BIOS uses IO-APIC mode Interrupt Source Override + * + * We do this only if we are dealing with IRQ() or IRQNoFlags() + * resource (the legacy ISA resources). With modern ACPI 5 devices + * using extended IRQ descriptors we take the IRQ configuration + * from _CRS directly. + */ + if (legacy && !acpi_get_override_irq(gsi, &t, &p)) { + u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; + u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; + + if (triggering != trig || polarity != pol) { + pr_warning("ACPI: IRQ %d override to %s, %s\n", gsi, + t ? "level" : "edge", p ? "low" : "high"); + triggering = trig; + polarity = pol; + } + } + + res->flags = acpi_dev_irq_flags(triggering, polarity, shareable); + irq = acpi_register_gsi(NULL, gsi, triggering, polarity); + if (irq >= 0) { + res->start = irq; + res->end = irq; + } else { + acpi_dev_irqresource_disabled(res, gsi); + } +} + +/** + * acpi_dev_resource_interrupt - Extract ACPI interrupt resource information. + * @ares: Input ACPI resource object. + * @index: Index into the array of GSIs represented by the resource. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents an interrupt resource + * and @index does not exceed the resource's interrupt count (true is returned + * in that case regardless of the results of the other checks)). If that's the + * case, register the GSI corresponding to @index from the array of interrupts + * represented by the resource and populate the generic resource object pointed + * to by @res accordingly. If the registration of the GSI is not successful, + * IORESOURCE_DISABLED will be set it that object's flags. + * + * Return: + * 1) false with res->flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource + * 3) true: valid assigned resource + */ +bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, + struct resource *res) +{ + struct acpi_resource_irq *irq; + struct acpi_resource_extended_irq *ext_irq; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_IRQ: + /* + * Per spec, only one interrupt per descriptor is allowed in + * _CRS, but some firmware violates this, so parse them all. + */ + irq = &ares->data.irq; + if (index >= irq->interrupt_count) { + acpi_dev_irqresource_disabled(res, 0); + return false; + } + acpi_dev_get_irqresource(res, irq->interrupts[index], + irq->triggering, irq->polarity, + irq->sharable, true); + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + ext_irq = &ares->data.extended_irq; + if (index >= ext_irq->interrupt_count) { + acpi_dev_irqresource_disabled(res, 0); + return false; + } + acpi_dev_get_irqresource(res, ext_irq->interrupts[index], + ext_irq->triggering, ext_irq->polarity, + ext_irq->sharable, false); + break; + default: + res->flags = 0; + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); + +/** + * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). + * @list: The head of the resource list to free. + */ +void acpi_dev_free_resource_list(struct list_head *list) +{ + resource_list_free(list); +} +EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); + +struct res_proc_context { + struct list_head *list; + int (*preproc)(struct acpi_resource *, void *); + void *preproc_data; + int count; + int error; +}; + +static acpi_status acpi_dev_new_resource_entry(struct resource_win *win, + struct res_proc_context *c) +{ + struct resource_entry *rentry; + + rentry = resource_list_create_entry(NULL, 0); + if (!rentry) { + c->error = -ENOMEM; + return AE_NO_MEMORY; + } + *rentry->res = win->res; + rentry->offset = win->offset; + resource_list_add_tail(rentry, c->list); + c->count++; + return AE_OK; +} + +static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, + void *context) +{ + struct res_proc_context *c = context; + struct resource_win win; + struct resource *res = &win.res; + int i; + + if (c->preproc) { + int ret; + + ret = c->preproc(ares, c->preproc_data); + if (ret < 0) { + c->error = ret; + return AE_CTRL_TERMINATE; + } else if (ret > 0) { + return AE_OK; + } + } + + memset(&win, 0, sizeof(win)); + + if (acpi_dev_resource_memory(ares, res) + || acpi_dev_resource_io(ares, res) + || acpi_dev_resource_address_space(ares, &win) + || acpi_dev_resource_ext_address_space(ares, &win)) + return acpi_dev_new_resource_entry(&win, c); + + for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) { + acpi_status status; + + status = acpi_dev_new_resource_entry(&win, c); + if (ACPI_FAILURE(status)) + return status; + } + + return AE_OK; +} + +/** + * acpi_dev_get_resources - Get current resources of a device. + * @adev: ACPI device node to get the resources for. + * @list: Head of the resultant list of resources (must be empty). + * @preproc: The caller's preprocessing routine. + * @preproc_data: Pointer passed to the caller's preprocessing routine. + * + * Evaluate the _CRS method for the given device node and process its output by + * (1) executing the @preproc() rountine provided by the caller, passing the + * resource pointer and @preproc_data to it as arguments, for each ACPI resource + * returned and (2) converting all of the returned ACPI resources into struct + * resource objects if possible. If the return value of @preproc() in step (1) + * is different from 0, step (2) is not applied to the given ACPI resource and + * if that value is negative, the whole processing is aborted and that value is + * returned as the final error code. + * + * The resultant struct resource objects are put on the list pointed to by + * @list, that must be empty initially, as members of struct resource_entry + * objects. Callers of this routine should use %acpi_dev_free_resource_list() to + * free that list. + * + * The number of resources in the output list is returned on success, an error + * code reflecting the error condition is returned otherwise. + */ +int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, + int (*preproc)(struct acpi_resource *, void *), + void *preproc_data) +{ + struct res_proc_context c; + acpi_status status; + + if (!adev || !adev->handle || !list_empty(list)) + return -EINVAL; + + if (!acpi_has_method(adev->handle, METHOD_NAME__CRS)) + return 0; + + c.list = list; + c.preproc = preproc; + c.preproc_data = preproc_data; + c.count = 0; + c.error = 0; + status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, + acpi_dev_process_resource, &c); + if (ACPI_FAILURE(status)) { + acpi_dev_free_resource_list(list); + return c.error ? c.error : -EIO; + } + + return c.count; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_resources); + +/** + * acpi_dev_filter_resource_type - Filter ACPI resource according to resource + * types + * @ares: Input ACPI resource object. + * @types: Valid resource types of IORESOURCE_XXX + * + * This is a helper function to support acpi_dev_get_resources(), which filters + * ACPI resource objects according to resource types. + */ +int acpi_dev_filter_resource_type(struct acpi_resource *ares, + unsigned long types) +{ + unsigned long type = 0; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_MEMORY24: + case ACPI_RESOURCE_TYPE_MEMORY32: + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + type = IORESOURCE_MEM; + break; + case ACPI_RESOURCE_TYPE_IO: + case ACPI_RESOURCE_TYPE_FIXED_IO: + type = IORESOURCE_IO; + break; + case ACPI_RESOURCE_TYPE_IRQ: + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + type = IORESOURCE_IRQ; + break; + case ACPI_RESOURCE_TYPE_DMA: + case ACPI_RESOURCE_TYPE_FIXED_DMA: + type = IORESOURCE_DMA; + break; + case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: + type = IORESOURCE_REG; + break; + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + if (ares->data.address.resource_type == ACPI_MEMORY_RANGE) + type = IORESOURCE_MEM; + else if (ares->data.address.resource_type == ACPI_IO_RANGE) + type = IORESOURCE_IO; + else if (ares->data.address.resource_type == + ACPI_BUS_NUMBER_RANGE) + type = IORESOURCE_BUS; + break; + default: + break; + } + + return (type & types) ? 0 : 1; +} +EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); + +struct reserved_region { + struct list_head node; + u64 start; + u64 end; +}; + +static LIST_HEAD(reserved_io_regions); +static LIST_HEAD(reserved_mem_regions); + +static int request_range(u64 start, u64 end, u8 space_id, unsigned long flags, + char *desc) +{ + unsigned int length = end - start + 1; + struct resource *res; + + res = space_id == ACPI_ADR_SPACE_SYSTEM_IO ? + request_region(start, length, desc) : + request_mem_region(start, length, desc); + if (!res) + return -EIO; + + res->flags &= ~flags; + return 0; +} + +static int add_region_before(u64 start, u64 end, u8 space_id, + unsigned long flags, char *desc, + struct list_head *head) +{ + struct reserved_region *reg; + int error; + + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + error = request_range(start, end, space_id, flags, desc); + if (error) + return error; + + reg->start = start; + reg->end = end; + list_add_tail(®->node, head); + return 0; +} + +/** + * acpi_reserve_region - Reserve an I/O or memory region as a system resource. + * @start: Starting address of the region. + * @length: Length of the region. + * @space_id: Identifier of address space to reserve the region from. + * @flags: Resource flags to clear for the region after requesting it. + * @desc: Region description (for messages). + * + * Reserve an I/O or memory region as a system resource to prevent others from + * using it. If the new region overlaps with one of the regions (in the given + * address space) already reserved by this routine, only the non-overlapping + * parts of it will be reserved. + * + * Returned is either 0 (success) or a negative error code indicating a resource + * reservation problem. It is the code of the first encountered error, but the + * routine doesn't abort until it has attempted to request all of the parts of + * the new region that don't overlap with other regions reserved previously. + * + * The resources requested by this routine are never released. + */ +int acpi_reserve_region(u64 start, unsigned int length, u8 space_id, + unsigned long flags, char *desc) +{ + struct list_head *regions; + struct reserved_region *reg; + u64 end = start + length - 1; + int ret = 0, error = 0; + + if (space_id == ACPI_ADR_SPACE_SYSTEM_IO) + regions = &reserved_io_regions; + else if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) + regions = &reserved_mem_regions; + else + return -EINVAL; + + if (list_empty(regions)) + return add_region_before(start, end, space_id, flags, desc, regions); + + list_for_each_entry(reg, regions, node) + if (reg->start == end + 1) { + /* The new region can be prepended to this one. */ + ret = request_range(start, end, space_id, flags, desc); + if (!ret) + reg->start = start; + + return ret; + } else if (reg->start > end) { + /* No overlap. Add the new region here and get out. */ + return add_region_before(start, end, space_id, flags, + desc, ®->node); + } else if (reg->end == start - 1) { + goto combine; + } else if (reg->end >= start) { + goto overlap; + } + + /* The new region goes after the last existing one. */ + return add_region_before(start, end, space_id, flags, desc, regions); + + overlap: + /* + * The new region overlaps an existing one. + * + * The head part of the new region immediately preceding the existing + * overlapping one can be combined with it right away. + */ + if (reg->start > start) { + error = request_range(start, reg->start - 1, space_id, flags, desc); + if (error) + ret = error; + else + reg->start = start; + } + + combine: + /* + * The new region is adjacent to an existing one. If it extends beyond + * that region all the way to the next one, it is possible to combine + * all three of them. + */ + while (reg->end < end) { + struct reserved_region *next = NULL; + u64 a = reg->end + 1, b = end; + + if (!list_is_last(®->node, regions)) { + next = list_next_entry(reg, node); + if (next->start <= end) + b = next->start - 1; + } + error = request_range(a, b, space_id, flags, desc); + if (!error) { + if (next && next->start == b + 1) { + reg->end = next->end; + list_del(&next->node); + kfree(next); + } else { + reg->end = end; + break; + } + } else if (next) { + if (!ret) + ret = error; + + reg = next; + } else { + break; + } + } + + return ret ? ret : error; +} +EXPORT_SYMBOL_GPL(acpi_reserve_region); diff --git a/kernel/drivers/acpi/sbs.c b/kernel/drivers/acpi/sbs.c new file mode 100644 index 000000000..01504c819 --- /dev/null +++ b/kernel/drivers/acpi/sbs.c @@ -0,0 +1,773 @@ +/* + * sbs.c - ACPI Smart Battery System Driver ($Revision: 2.0 $) + * + * Copyright (c) 2007 Alexey Starikovskiy + * Copyright (c) 2005-2007 Vladimir Lebedev + * Copyright (c) 2005 Rich Townsend + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sbshc.h" +#include "battery.h" + +#define PREFIX "ACPI: " + +#define ACPI_SBS_CLASS "sbs" +#define ACPI_AC_CLASS "ac_adapter" +#define ACPI_SBS_DEVICE_NAME "Smart Battery System" +#define ACPI_SBS_FILE_INFO "info" +#define ACPI_SBS_FILE_STATE "state" +#define ACPI_SBS_FILE_ALARM "alarm" +#define ACPI_BATTERY_DIR_NAME "BAT%i" +#define ACPI_AC_DIR_NAME "AC0" + +#define ACPI_SBS_NOTIFY_STATUS 0x80 +#define ACPI_SBS_NOTIFY_INFO 0x81 + +MODULE_AUTHOR("Alexey Starikovskiy "); +MODULE_DESCRIPTION("Smart Battery System ACPI interface driver"); +MODULE_LICENSE("GPL"); + +static unsigned int cache_time = 1000; +module_param(cache_time, uint, 0644); +MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); + +static bool sbs_manager_broken; + +#define MAX_SBS_BAT 4 +#define ACPI_SBS_BLOCK_MAX 32 + +static const struct acpi_device_id sbs_device_ids[] = { + {"ACPI0002", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, sbs_device_ids); + +struct acpi_battery { + struct power_supply *bat; + struct power_supply_desc bat_desc; + struct acpi_sbs *sbs; + unsigned long update_time; + char name[8]; + char manufacturer_name[ACPI_SBS_BLOCK_MAX]; + char device_name[ACPI_SBS_BLOCK_MAX]; + char device_chemistry[ACPI_SBS_BLOCK_MAX]; + u16 alarm_capacity; + u16 full_charge_capacity; + u16 design_capacity; + u16 design_voltage; + u16 serial_number; + u16 cycle_count; + u16 temp_now; + u16 voltage_now; + s16 rate_now; + s16 rate_avg; + u16 capacity_now; + u16 state_of_charge; + u16 state; + u16 mode; + u16 spec; + u8 id; + u8 present:1; + u8 have_sysfs_alarm:1; +}; + +#define to_acpi_battery(x) power_supply_get_drvdata(x) + +struct acpi_sbs { + struct power_supply *charger; + struct acpi_device *device; + struct acpi_smb_hc *hc; + struct mutex lock; + struct acpi_battery battery[MAX_SBS_BAT]; + u8 batteries_supported:4; + u8 manager_present:1; + u8 charger_present:1; + u8 charger_exists:1; +}; + +#define to_acpi_sbs(x) power_supply_get_drvdata(x) + +static int acpi_sbs_remove(struct acpi_device *device); +static int acpi_battery_get_state(struct acpi_battery *battery); + +static inline int battery_scale(int log) +{ + int scale = 1; + while (log--) + scale *= 10; + return scale; +} + +static inline int acpi_battery_vscale(struct acpi_battery *battery) +{ + return battery_scale((battery->spec & 0x0f00) >> 8); +} + +static inline int acpi_battery_ipscale(struct acpi_battery *battery) +{ + return battery_scale((battery->spec & 0xf000) >> 12); +} + +static inline int acpi_battery_mode(struct acpi_battery *battery) +{ + return (battery->mode & 0x8000); +} + +static inline int acpi_battery_scale(struct acpi_battery *battery) +{ + return (acpi_battery_mode(battery) ? 10 : 1) * + acpi_battery_ipscale(battery); +} + +static int sbs_get_ac_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct acpi_sbs *sbs = to_acpi_sbs(psy); + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = sbs->charger_present; + break; + default: + return -EINVAL; + } + return 0; +} + +static int acpi_battery_technology(struct acpi_battery *battery) +{ + if (!strcasecmp("NiCd", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_NiCd; + if (!strcasecmp("NiMH", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_NiMH; + if (!strcasecmp("LION", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_LION; + if (!strcasecmp("LiP", battery->device_chemistry)) + return POWER_SUPPLY_TECHNOLOGY_LIPO; + return POWER_SUPPLY_TECHNOLOGY_UNKNOWN; +} + +static int acpi_sbs_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct acpi_battery *battery = to_acpi_battery(psy); + + if ((!battery->present) && psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + + acpi_battery_get_state(battery); + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (battery->rate_now < 0) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (battery->rate_now > 0) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = battery->present; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = acpi_battery_technology(battery); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = battery->cycle_count; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = battery->design_voltage * + acpi_battery_vscale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = battery->voltage_now * + acpi_battery_vscale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_POWER_NOW: + val->intval = abs(battery->rate_now) * + acpi_battery_ipscale(battery) * 1000; + val->intval *= (acpi_battery_mode(battery)) ? + (battery->voltage_now * + acpi_battery_vscale(battery) / 1000) : 1; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + case POWER_SUPPLY_PROP_POWER_AVG: + val->intval = abs(battery->rate_avg) * + acpi_battery_ipscale(battery) * 1000; + val->intval *= (acpi_battery_mode(battery)) ? + (battery->voltage_now * + acpi_battery_vscale(battery) / 1000) : 1; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery->state_of_charge; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval = battery->design_capacity * + acpi_battery_scale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL: + val->intval = battery->full_charge_capacity * + acpi_battery_scale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_ENERGY_NOW: + val->intval = battery->capacity_now * + acpi_battery_scale(battery) * 1000; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = battery->temp_now - 2730; // dK -> dC + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = battery->device_name; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = battery->manufacturer_name; + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property sbs_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property sbs_charge_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static enum power_supply_property sbs_energy_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static const struct power_supply_desc acpi_sbs_charger_desc = { + .name = "sbs-charger", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = sbs_ac_props, + .num_properties = ARRAY_SIZE(sbs_ac_props), + .get_property = sbs_get_ac_property, +}; + +/* -------------------------------------------------------------------------- + Smart Battery System Management + -------------------------------------------------------------------------- */ + +struct acpi_battery_reader { + u8 command; /* command for battery */ + u8 mode; /* word or block? */ + size_t offset; /* offset inside struct acpi_sbs_battery */ +}; + +static struct acpi_battery_reader info_readers[] = { + {0x01, SMBUS_READ_WORD, offsetof(struct acpi_battery, alarm_capacity)}, + {0x03, SMBUS_READ_WORD, offsetof(struct acpi_battery, mode)}, + {0x10, SMBUS_READ_WORD, offsetof(struct acpi_battery, full_charge_capacity)}, + {0x17, SMBUS_READ_WORD, offsetof(struct acpi_battery, cycle_count)}, + {0x18, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_capacity)}, + {0x19, SMBUS_READ_WORD, offsetof(struct acpi_battery, design_voltage)}, + {0x1a, SMBUS_READ_WORD, offsetof(struct acpi_battery, spec)}, + {0x1c, SMBUS_READ_WORD, offsetof(struct acpi_battery, serial_number)}, + {0x20, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, manufacturer_name)}, + {0x21, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_name)}, + {0x22, SMBUS_READ_BLOCK, offsetof(struct acpi_battery, device_chemistry)}, +}; + +static struct acpi_battery_reader state_readers[] = { + {0x08, SMBUS_READ_WORD, offsetof(struct acpi_battery, temp_now)}, + {0x09, SMBUS_READ_WORD, offsetof(struct acpi_battery, voltage_now)}, + {0x0a, SMBUS_READ_WORD, offsetof(struct acpi_battery, rate_now)}, + {0x0b, SMBUS_READ_WORD, offsetof(struct acpi_battery, rate_avg)}, + {0x0f, SMBUS_READ_WORD, offsetof(struct acpi_battery, capacity_now)}, + {0x0e, SMBUS_READ_WORD, offsetof(struct acpi_battery, state_of_charge)}, + {0x16, SMBUS_READ_WORD, offsetof(struct acpi_battery, state)}, +}; + +static int acpi_manager_get_info(struct acpi_sbs *sbs) +{ + int result = 0; + u16 battery_system_info; + + result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER, + 0x04, (u8 *)&battery_system_info); + if (!result) + sbs->batteries_supported = battery_system_info & 0x000f; + return result; +} + +static int acpi_battery_get_info(struct acpi_battery *battery) +{ + int i, result = 0; + + for (i = 0; i < ARRAY_SIZE(info_readers); ++i) { + result = acpi_smbus_read(battery->sbs->hc, + info_readers[i].mode, + ACPI_SBS_BATTERY, + info_readers[i].command, + (u8 *) battery + + info_readers[i].offset); + if (result) + break; + } + return result; +} + +static int acpi_battery_get_state(struct acpi_battery *battery) +{ + int i, result = 0; + + if (battery->update_time && + time_before(jiffies, battery->update_time + + msecs_to_jiffies(cache_time))) + return 0; + for (i = 0; i < ARRAY_SIZE(state_readers); ++i) { + result = acpi_smbus_read(battery->sbs->hc, + state_readers[i].mode, + ACPI_SBS_BATTERY, + state_readers[i].command, + (u8 *)battery + + state_readers[i].offset); + if (result) + goto end; + } + end: + battery->update_time = jiffies; + return result; +} + +static int acpi_battery_get_alarm(struct acpi_battery *battery) +{ + return acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, + ACPI_SBS_BATTERY, 0x01, + (u8 *)&battery->alarm_capacity); +} + +static int acpi_battery_set_alarm(struct acpi_battery *battery) +{ + struct acpi_sbs *sbs = battery->sbs; + u16 value, sel = 1 << (battery->id + 12); + + int ret; + + + if (sbs->manager_present) { + ret = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_MANAGER, + 0x01, (u8 *)&value); + if (ret) + goto end; + if ((value & 0xf000) != sel) { + value &= 0x0fff; + value |= sel; + ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD, + ACPI_SBS_MANAGER, + 0x01, (u8 *)&value, 2); + if (ret) + goto end; + } + } + ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, + 0x01, (u8 *)&battery->alarm_capacity, 2); + end: + return ret; +} + +static int acpi_ac_get_present(struct acpi_sbs *sbs) +{ + int result; + u16 status; + + result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER, + 0x13, (u8 *) & status); + + if (result) + return result; + + /* + * The spec requires that bit 4 always be 1. If it's not set, assume + * that the implementation doesn't support an SBS charger + */ + if (!((status >> 4) & 0x1)) + return -ENODEV; + + sbs->charger_present = (status >> 15) & 0x1; + return 0; +} + +static ssize_t acpi_battery_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + acpi_battery_get_alarm(battery); + return sprintf(buf, "%d\n", battery->alarm_capacity * + acpi_battery_scale(battery) * 1000); +} + +static ssize_t acpi_battery_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long x; + struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); + if (sscanf(buf, "%lu\n", &x) == 1) + battery->alarm_capacity = x / + (1000 * acpi_battery_scale(battery)); + if (battery->present) + acpi_battery_set_alarm(battery); + return count; +} + +static struct device_attribute alarm_attr = { + .attr = {.name = "alarm", .mode = 0644}, + .show = acpi_battery_alarm_show, + .store = acpi_battery_alarm_store, +}; + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ +static int acpi_battery_read(struct acpi_battery *battery) +{ + int result = 0, saved_present = battery->present; + u16 state; + + if (battery->sbs->manager_present) { + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, + ACPI_SBS_MANAGER, 0x01, (u8 *)&state); + if (!result) + battery->present = state & (1 << battery->id); + state &= 0x0fff; + state |= 1 << (battery->id + 12); + acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, + ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2); + } else if (battery->id == 0) + battery->present = 1; + + if (result || !battery->present) + return result; + + if (saved_present != battery->present) { + battery->update_time = 0; + result = acpi_battery_get_info(battery); + if (result) { + battery->present = 0; + return result; + } + } + result = acpi_battery_get_state(battery); + if (result) + battery->present = 0; + return result; +} + +/* Smart Battery */ +static int acpi_battery_add(struct acpi_sbs *sbs, int id) +{ + struct acpi_battery *battery = &sbs->battery[id]; + struct power_supply_config psy_cfg = { .drv_data = battery, }; + int result; + + battery->id = id; + battery->sbs = sbs; + result = acpi_battery_read(battery); + if (result) + return result; + + sprintf(battery->name, ACPI_BATTERY_DIR_NAME, id); + battery->bat_desc.name = battery->name; + battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + if (!acpi_battery_mode(battery)) { + battery->bat_desc.properties = sbs_charge_battery_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(sbs_charge_battery_props); + } else { + battery->bat_desc.properties = sbs_energy_battery_props; + battery->bat_desc.num_properties = + ARRAY_SIZE(sbs_energy_battery_props); + } + battery->bat_desc.get_property = acpi_sbs_battery_get_property; + battery->bat = power_supply_register(&sbs->device->dev, + &battery->bat_desc, &psy_cfg); + if (IS_ERR(battery->bat)) { + result = PTR_ERR(battery->bat); + battery->bat = NULL; + goto end; + } + + result = device_create_file(&battery->bat->dev, &alarm_attr); + if (result) + goto end; + battery->have_sysfs_alarm = 1; + end: + printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n", + ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), + battery->name, battery->present ? "present" : "absent"); + return result; +} + +static void acpi_battery_remove(struct acpi_sbs *sbs, int id) +{ + struct acpi_battery *battery = &sbs->battery[id]; + + if (battery->bat) { + if (battery->have_sysfs_alarm) + device_remove_file(&battery->bat->dev, &alarm_attr); + power_supply_unregister(battery->bat); + } +} + +static int acpi_charger_add(struct acpi_sbs *sbs) +{ + int result; + struct power_supply_config psy_cfg = { .drv_data = sbs, }; + + result = acpi_ac_get_present(sbs); + if (result) + goto end; + + sbs->charger_exists = 1; + sbs->charger = power_supply_register(&sbs->device->dev, + &acpi_sbs_charger_desc, &psy_cfg); + if (IS_ERR(sbs->charger)) { + result = PTR_ERR(sbs->charger); + sbs->charger = NULL; + } + printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n", + ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), + ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line"); + end: + return result; +} + +static void acpi_charger_remove(struct acpi_sbs *sbs) +{ + if (sbs->charger) + power_supply_unregister(sbs->charger); +} + +static void acpi_sbs_callback(void *context) +{ + int id; + struct acpi_sbs *sbs = context; + struct acpi_battery *bat; + u8 saved_charger_state = sbs->charger_present; + u8 saved_battery_state; + + if (sbs->charger_exists) { + acpi_ac_get_present(sbs); + if (sbs->charger_present != saved_charger_state) + kobject_uevent(&sbs->charger->dev.kobj, KOBJ_CHANGE); + } + + if (sbs->manager_present) { + for (id = 0; id < MAX_SBS_BAT; ++id) { + if (!(sbs->batteries_supported & (1 << id))) + continue; + bat = &sbs->battery[id]; + saved_battery_state = bat->present; + acpi_battery_read(bat); + if (saved_battery_state == bat->present) + continue; + kobject_uevent(&bat->bat->dev.kobj, KOBJ_CHANGE); + } + } +} + +static int disable_sbs_manager(const struct dmi_system_id *d) +{ + sbs_manager_broken = true; + return 0; +} + +static struct dmi_system_id acpi_sbs_dmi_table[] = { + { + .callback = disable_sbs_manager, + .ident = "Apple", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc.") + }, + }, + { }, +}; + +static int acpi_sbs_add(struct acpi_device *device) +{ + struct acpi_sbs *sbs; + int result = 0; + int id; + + dmi_check_system(acpi_sbs_dmi_table); + + sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL); + if (!sbs) { + result = -ENOMEM; + goto end; + } + + mutex_init(&sbs->lock); + + sbs->hc = acpi_driver_data(device->parent); + sbs->device = device; + strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_SBS_CLASS); + device->driver_data = sbs; + + result = acpi_charger_add(sbs); + if (result && result != -ENODEV) + goto end; + + result = 0; + + if (!sbs_manager_broken) { + result = acpi_manager_get_info(sbs); + if (!result) { + sbs->manager_present = 1; + for (id = 0; id < MAX_SBS_BAT; ++id) + if ((sbs->batteries_supported & (1 << id))) + acpi_battery_add(sbs, id); + } + } + + if (!sbs->manager_present) + acpi_battery_add(sbs, 0); + + acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs); + end: + if (result) + acpi_sbs_remove(device); + return result; +} + +static int acpi_sbs_remove(struct acpi_device *device) +{ + struct acpi_sbs *sbs; + int id; + + if (!device) + return -EINVAL; + sbs = acpi_driver_data(device); + if (!sbs) + return -EINVAL; + mutex_lock(&sbs->lock); + acpi_smbus_unregister_callback(sbs->hc); + for (id = 0; id < MAX_SBS_BAT; ++id) + acpi_battery_remove(sbs, id); + acpi_charger_remove(sbs); + mutex_unlock(&sbs->lock); + mutex_destroy(&sbs->lock); + kfree(sbs); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int acpi_sbs_resume(struct device *dev) +{ + struct acpi_sbs *sbs; + if (!dev) + return -EINVAL; + sbs = to_acpi_device(dev)->driver_data; + acpi_sbs_callback(sbs); + return 0; +} +#else +#define acpi_sbs_resume NULL +#endif + +static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume); + +static struct acpi_driver acpi_sbs_driver = { + .name = "sbs", + .class = ACPI_SBS_CLASS, + .ids = sbs_device_ids, + .ops = { + .add = acpi_sbs_add, + .remove = acpi_sbs_remove, + }, + .drv.pm = &acpi_sbs_pm, +}; + +static int __init acpi_sbs_init(void) +{ + int result = 0; + + if (acpi_disabled) + return -ENODEV; + + result = acpi_bus_register_driver(&acpi_sbs_driver); + if (result < 0) + return -ENODEV; + + return 0; +} + +static void __exit acpi_sbs_exit(void) +{ + acpi_bus_unregister_driver(&acpi_sbs_driver); + return; +} + +module_init(acpi_sbs_init); +module_exit(acpi_sbs_exit); diff --git a/kernel/drivers/acpi/sbshc.c b/kernel/drivers/acpi/sbshc.c new file mode 100644 index 000000000..bf034f8b7 --- /dev/null +++ b/kernel/drivers/acpi/sbshc.c @@ -0,0 +1,338 @@ +/* + * SMBus driver for ACPI Embedded Controller (v0.1) + * + * Copyright (c) 2007 Alexey Starikovskiy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "sbshc.h" + +#define PREFIX "ACPI: " + +#define ACPI_SMB_HC_CLASS "smbus_host_ctl" +#define ACPI_SMB_HC_DEVICE_NAME "ACPI SMBus HC" + +struct acpi_smb_hc { + struct acpi_ec *ec; + struct mutex lock; + wait_queue_head_t wait; + u8 offset; + u8 query_bit; + smbus_alarm_callback callback; + void *context; +}; + +static int acpi_smbus_hc_add(struct acpi_device *device); +static int acpi_smbus_hc_remove(struct acpi_device *device); + +static const struct acpi_device_id sbs_device_ids[] = { + {"ACPI0001", 0}, + {"ACPI0005", 0}, + {"", 0}, +}; + +MODULE_DEVICE_TABLE(acpi, sbs_device_ids); + +static struct acpi_driver acpi_smb_hc_driver = { + .name = "smbus_hc", + .class = ACPI_SMB_HC_CLASS, + .ids = sbs_device_ids, + .ops = { + .add = acpi_smbus_hc_add, + .remove = acpi_smbus_hc_remove, + }, +}; + +union acpi_smb_status { + u8 raw; + struct { + u8 status:5; + u8 reserved:1; + u8 alarm:1; + u8 done:1; + } fields; +}; + +enum acpi_smb_status_codes { + SMBUS_OK = 0, + SMBUS_UNKNOWN_FAILURE = 0x07, + SMBUS_DEVICE_ADDRESS_NACK = 0x10, + SMBUS_DEVICE_ERROR = 0x11, + SMBUS_DEVICE_COMMAND_ACCESS_DENIED = 0x12, + SMBUS_UNKNOWN_ERROR = 0x13, + SMBUS_DEVICE_ACCESS_DENIED = 0x17, + SMBUS_TIMEOUT = 0x18, + SMBUS_HOST_UNSUPPORTED_PROTOCOL = 0x19, + SMBUS_BUSY = 0x1a, + SMBUS_PEC_ERROR = 0x1f, +}; + +enum acpi_smb_offset { + ACPI_SMB_PROTOCOL = 0, /* protocol, PEC */ + ACPI_SMB_STATUS = 1, /* status */ + ACPI_SMB_ADDRESS = 2, /* address */ + ACPI_SMB_COMMAND = 3, /* command */ + ACPI_SMB_DATA = 4, /* 32 data registers */ + ACPI_SMB_BLOCK_COUNT = 0x24, /* number of data bytes */ + ACPI_SMB_ALARM_ADDRESS = 0x25, /* alarm address */ + ACPI_SMB_ALARM_DATA = 0x26, /* 2 bytes alarm data */ +}; + +static bool macbook; + +static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data) +{ + return ec_read(hc->offset + address, data); +} + +static inline int smb_hc_write(struct acpi_smb_hc *hc, u8 address, u8 data) +{ + return ec_write(hc->offset + address, data); +} + +static inline int smb_check_done(struct acpi_smb_hc *hc) +{ + union acpi_smb_status status = {.raw = 0}; + smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw); + return status.fields.done && (status.fields.status == SMBUS_OK); +} + +static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout) +{ + if (wait_event_timeout(hc->wait, smb_check_done(hc), + msecs_to_jiffies(timeout))) + return 0; + /* + * After the timeout happens, OS will try to check the status of SMbus. + * If the status is what OS expected, it will be regarded as the bogus + * timeout. + */ + if (smb_check_done(hc)) + return 0; + else + return -ETIME; +} + +static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol, + u8 address, u8 command, u8 *data, u8 length) +{ + int ret = -EFAULT, i; + u8 temp, sz = 0; + + if (!hc) { + printk(KERN_ERR PREFIX "host controller is not configured\n"); + return ret; + } + + mutex_lock(&hc->lock); + if (macbook) + udelay(5); + if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp)) + goto end; + if (temp) { + ret = -EBUSY; + goto end; + } + smb_hc_write(hc, ACPI_SMB_COMMAND, command); + if (!(protocol & 0x01)) { + smb_hc_write(hc, ACPI_SMB_BLOCK_COUNT, length); + for (i = 0; i < length; ++i) + smb_hc_write(hc, ACPI_SMB_DATA + i, data[i]); + } + smb_hc_write(hc, ACPI_SMB_ADDRESS, address << 1); + smb_hc_write(hc, ACPI_SMB_PROTOCOL, protocol); + /* + * Wait for completion. Save the status code, data size, + * and data into the return package (if required by the protocol). + */ + ret = wait_transaction_complete(hc, 1000); + if (ret || !(protocol & 0x01)) + goto end; + switch (protocol) { + case SMBUS_RECEIVE_BYTE: + case SMBUS_READ_BYTE: + sz = 1; + break; + case SMBUS_READ_WORD: + sz = 2; + break; + case SMBUS_READ_BLOCK: + if (smb_hc_read(hc, ACPI_SMB_BLOCK_COUNT, &sz)) { + ret = -EFAULT; + goto end; + } + sz &= 0x1f; + break; + } + for (i = 0; i < sz; ++i) + smb_hc_read(hc, ACPI_SMB_DATA + i, &data[i]); + end: + mutex_unlock(&hc->lock); + return ret; +} + +int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address, + u8 command, u8 *data) +{ + return acpi_smbus_transaction(hc, protocol, address, command, data, 0); +} + +EXPORT_SYMBOL_GPL(acpi_smbus_read); + +int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 address, + u8 command, u8 *data, u8 length) +{ + return acpi_smbus_transaction(hc, protocol, address, command, data, length); +} + +EXPORT_SYMBOL_GPL(acpi_smbus_write); + +int acpi_smbus_register_callback(struct acpi_smb_hc *hc, + smbus_alarm_callback callback, void *context) +{ + mutex_lock(&hc->lock); + hc->callback = callback; + hc->context = context; + mutex_unlock(&hc->lock); + return 0; +} + +EXPORT_SYMBOL_GPL(acpi_smbus_register_callback); + +int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc) +{ + mutex_lock(&hc->lock); + hc->callback = NULL; + hc->context = NULL; + mutex_unlock(&hc->lock); + return 0; +} + +EXPORT_SYMBOL_GPL(acpi_smbus_unregister_callback); + +static inline void acpi_smbus_callback(void *context) +{ + struct acpi_smb_hc *hc = context; + if (hc->callback) + hc->callback(hc->context); +} + +static int smbus_alarm(void *context) +{ + struct acpi_smb_hc *hc = context; + union acpi_smb_status status; + u8 address; + if (smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw)) + return 0; + /* Check if it is only a completion notify */ + if (status.fields.done) + wake_up(&hc->wait); + if (!status.fields.alarm) + return 0; + mutex_lock(&hc->lock); + smb_hc_read(hc, ACPI_SMB_ALARM_ADDRESS, &address); + status.fields.alarm = 0; + smb_hc_write(hc, ACPI_SMB_STATUS, status.raw); + /* We are only interested in events coming from known devices */ + switch (address >> 1) { + case ACPI_SBS_CHARGER: + case ACPI_SBS_MANAGER: + case ACPI_SBS_BATTERY: + acpi_os_execute(OSL_NOTIFY_HANDLER, + acpi_smbus_callback, hc); + default:; + } + mutex_unlock(&hc->lock); + return 0; +} + +typedef int (*acpi_ec_query_func) (void *data); + +extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, + acpi_handle handle, acpi_ec_query_func func, + void *data); + +static int macbook_dmi_match(const struct dmi_system_id *d) +{ + pr_debug("Detected MacBook, enabling workaround\n"); + macbook = true; + return 0; +} + +static struct dmi_system_id acpi_smbus_dmi_table[] = { + { macbook_dmi_match, "Apple MacBook", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook") }, + }, + { }, +}; + +static int acpi_smbus_hc_add(struct acpi_device *device) +{ + int status; + unsigned long long val; + struct acpi_smb_hc *hc; + + dmi_check_system(acpi_smbus_dmi_table); + + if (!device) + return -EINVAL; + + status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "error obtaining _EC.\n"); + return -EIO; + } + + strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS); + + hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL); + if (!hc) + return -ENOMEM; + mutex_init(&hc->lock); + init_waitqueue_head(&hc->wait); + + hc->ec = acpi_driver_data(device->parent); + hc->offset = (val >> 8) & 0xff; + hc->query_bit = val & 0xff; + device->driver_data = hc; + + acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc); + printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n", + hc->ec, hc->offset, hc->query_bit); + + return 0; +} + +extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); + +static int acpi_smbus_hc_remove(struct acpi_device *device) +{ + struct acpi_smb_hc *hc; + + if (!device) + return -EINVAL; + + hc = acpi_driver_data(device); + acpi_ec_remove_query_handler(hc->ec, hc->query_bit); + kfree(hc); + device->driver_data = NULL; + return 0; +} + +module_acpi_driver(acpi_smb_hc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexey Starikovskiy"); +MODULE_DESCRIPTION("ACPI SMBus HC driver"); diff --git a/kernel/drivers/acpi/sbshc.h b/kernel/drivers/acpi/sbshc.h new file mode 100644 index 000000000..a57b0762d --- /dev/null +++ b/kernel/drivers/acpi/sbshc.h @@ -0,0 +1,33 @@ +struct acpi_smb_hc; +enum acpi_smb_protocol { + SMBUS_WRITE_QUICK = 2, + SMBUS_READ_QUICK = 3, + SMBUS_SEND_BYTE = 4, + SMBUS_RECEIVE_BYTE = 5, + SMBUS_WRITE_BYTE = 6, + SMBUS_READ_BYTE = 7, + SMBUS_WRITE_WORD = 8, + SMBUS_READ_WORD = 9, + SMBUS_WRITE_BLOCK = 0xa, + SMBUS_READ_BLOCK = 0xb, + SMBUS_PROCESS_CALL = 0xc, + SMBUS_BLOCK_PROCESS_CALL = 0xd, +}; + +static const u8 SMBUS_PEC = 0x80; + +enum acpi_sbs_device_addr { + ACPI_SBS_CHARGER = 0x9, + ACPI_SBS_MANAGER = 0xa, + ACPI_SBS_BATTERY = 0xb, +}; + +typedef void (*smbus_alarm_callback)(void *context); + +extern int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address, + u8 command, u8 * data); +extern int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 slave_address, + u8 command, u8 * data, u8 length); +extern int acpi_smbus_register_callback(struct acpi_smb_hc *hc, + smbus_alarm_callback callback, void *context); +extern int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc); diff --git a/kernel/drivers/acpi/scan.c b/kernel/drivers/acpi/scan.c new file mode 100644 index 000000000..03141aa4e --- /dev/null +++ b/kernel/drivers/acpi/scan.c @@ -0,0 +1,2688 @@ +/* + * scan.c - support for transforming the ACPI namespace into individual objects + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "internal.h" + +#define _COMPONENT ACPI_BUS_COMPONENT +ACPI_MODULE_NAME("scan"); +extern struct acpi_device *acpi_root; + +#define ACPI_BUS_CLASS "system_bus" +#define ACPI_BUS_HID "LNXSYBUS" +#define ACPI_BUS_DEVICE_NAME "System Bus" + +#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) + +#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page) + +/* + * If set, devices will be hot-removed even if they cannot be put offline + * gracefully (from the kernel's standpoint). + */ +bool acpi_force_hot_remove; + +static const char *dummy_hid = "device"; + +static LIST_HEAD(acpi_dep_list); +static DEFINE_MUTEX(acpi_dep_list_lock); +static LIST_HEAD(acpi_bus_id_list); +static DEFINE_MUTEX(acpi_scan_lock); +static LIST_HEAD(acpi_scan_handlers_list); +DEFINE_MUTEX(acpi_device_lock); +LIST_HEAD(acpi_wakeup_device_list); +static DEFINE_MUTEX(acpi_hp_context_lock); + +struct acpi_dep_data { + struct list_head node; + acpi_handle master; + acpi_handle slave; +}; + +struct acpi_device_bus_id{ + char bus_id[15]; + unsigned int instance_no; + struct list_head node; +}; + +void acpi_scan_lock_acquire(void) +{ + mutex_lock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire); + +void acpi_scan_lock_release(void) +{ + mutex_unlock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_release); + +void acpi_lock_hp_context(void) +{ + mutex_lock(&acpi_hp_context_lock); +} + +void acpi_unlock_hp_context(void) +{ + mutex_unlock(&acpi_hp_context_lock); +} + +void acpi_initialize_hp_context(struct acpi_device *adev, + struct acpi_hotplug_context *hp, + int (*notify)(struct acpi_device *, u32), + void (*uevent)(struct acpi_device *, u32)) +{ + acpi_lock_hp_context(); + hp->notify = notify; + hp->uevent = uevent; + acpi_set_hp_context(adev, hp); + acpi_unlock_hp_context(); +} +EXPORT_SYMBOL_GPL(acpi_initialize_hp_context); + +int acpi_scan_add_handler(struct acpi_scan_handler *handler) +{ + if (!handler) + return -EINVAL; + + list_add_tail(&handler->list_node, &acpi_scan_handlers_list); + return 0; +} + +int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, + const char *hotplug_profile_name) +{ + int error; + + error = acpi_scan_add_handler(handler); + if (error) + return error; + + acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name); + return 0; +} + +/** + * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent + * @acpi_dev: ACPI device object. + * @modalias: Buffer to print into. + * @size: Size of the buffer. + * + * Creates hid/cid(s) string needed for modalias and uevent + * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: + * char *modalias: "acpi:IBM0001:ACPI0001" + * Return: 0: no _HID and no _CID + * -EINVAL: output error + * -ENOMEM: output is truncated +*/ +static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, + int size) +{ + int len; + int count; + struct acpi_hardware_id *id; + + /* + * Since we skip PRP0001 from the modalias below, 0 should be returned + * if PRP0001 is the only ACPI/PNP ID in the device's list. + */ + count = 0; + list_for_each_entry(id, &acpi_dev->pnp.ids, list) + if (strcmp(id->id, "PRP0001")) + count++; + + if (!count) + return 0; + + len = snprintf(modalias, size, "acpi:"); + if (len <= 0) + return len; + + size -= len; + + list_for_each_entry(id, &acpi_dev->pnp.ids, list) { + if (!strcmp(id->id, "PRP0001")) + continue; + + count = snprintf(&modalias[len], size, "%s:", id->id); + if (count < 0) + return -EINVAL; + + if (count >= size) + return -ENOMEM; + + len += count; + size -= count; + } + modalias[len] = '\0'; + return len; +} + +/** + * create_of_modalias - Creates DT compatible string for modalias and uevent + * @acpi_dev: ACPI device object. + * @modalias: Buffer to print into. + * @size: Size of the buffer. + * + * Expose DT compatible modalias as of:NnameTCcompatible. This function should + * only be called for devices having PRP0001 in their list of ACPI/PNP IDs. + */ +static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, + int size) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + const union acpi_object *of_compatible, *obj; + int len, count; + int i, nval; + char *c; + + acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); + /* DT strings are all in lower case */ + for (c = buf.pointer; *c != '\0'; c++) + *c = tolower(*c); + + len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); + ACPI_FREE(buf.pointer); + + if (len <= 0) + return len; + + of_compatible = acpi_dev->data.of_compatible; + if (of_compatible->type == ACPI_TYPE_PACKAGE) { + nval = of_compatible->package.count; + obj = of_compatible->package.elements; + } else { /* Must be ACPI_TYPE_STRING. */ + nval = 1; + obj = of_compatible; + } + for (i = 0; i < nval; i++, obj++) { + count = snprintf(&modalias[len], size, "C%s", + obj->string.pointer); + if (count < 0) + return -EINVAL; + + if (count >= size) + return -ENOMEM; + + len += count; + size -= count; + } + modalias[len] = '\0'; + return len; +} + +/* + * acpi_companion_match() - Can we match via ACPI companion device + * @dev: Device in question + * + * Check if the given device has an ACPI companion and if that companion has + * a valid list of PNP IDs, and if the device is the first (primary) physical + * device associated with it. Return the companion pointer if that's the case + * or NULL otherwise. + * + * If multiple physical devices are attached to a single ACPI companion, we need + * to be careful. The usage scenario for this kind of relationship is that all + * of the physical devices in question use resources provided by the ACPI + * companion. A typical case is an MFD device where all the sub-devices share + * the parent's ACPI companion. In such cases we can only allow the primary + * (first) physical device to be matched with the help of the companion's PNP + * IDs. + * + * Additional physical devices sharing the ACPI companion can still use + * resources available from it but they will be matched normally using functions + * provided by their bus types (and analogously for their modalias). + */ +static struct acpi_device *acpi_companion_match(const struct device *dev) +{ + struct acpi_device *adev; + struct mutex *physical_node_lock; + + adev = ACPI_COMPANION(dev); + if (!adev) + return NULL; + + if (list_empty(&adev->pnp.ids)) + return NULL; + + physical_node_lock = &adev->physical_node_lock; + mutex_lock(physical_node_lock); + if (list_empty(&adev->physical_node_list)) { + adev = NULL; + } else { + const struct acpi_device_physical_node *node; + + node = list_first_entry(&adev->physical_node_list, + struct acpi_device_physical_node, node); + if (node->dev != dev) + adev = NULL; + } + mutex_unlock(physical_node_lock); + + return adev; +} + +static int __acpi_device_uevent_modalias(struct acpi_device *adev, + struct kobj_uevent_env *env) +{ + int len; + + if (!adev) + return -ENODEV; + + if (list_empty(&adev->pnp.ids)) + return 0; + + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + + len = create_pnp_modalias(adev, &env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen); + if (len < 0) + return len; + + env->buflen += len; + if (!adev->data.of_compatible) + return 0; + + if (len > 0 && add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + + len = create_of_modalias(adev, &env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen); + if (len < 0) + return len; + + env->buflen += len; + + return 0; +} + +/* + * Creates uevent modalias field for ACPI enumerated devices. + * Because the other buses does not support ACPI HIDs & CIDs. + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: + * "acpi:IBM0001:ACPI0001" + */ +int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) +{ + return __acpi_device_uevent_modalias(acpi_companion_match(dev), env); +} +EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); + +static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size) +{ + int len, count; + + if (!adev) + return -ENODEV; + + if (list_empty(&adev->pnp.ids)) + return 0; + + len = create_pnp_modalias(adev, buf, size - 1); + if (len < 0) { + return len; + } else if (len > 0) { + buf[len++] = '\n'; + size -= len; + } + if (!adev->data.of_compatible) + return len; + + count = create_of_modalias(adev, buf + len, size - 1); + if (count < 0) { + return count; + } else if (count > 0) { + len += count; + buf[len++] = '\n'; + } + + return len; +} + +/* + * Creates modalias sysfs attribute for ACPI enumerated devices. + * Because the other buses does not support ACPI HIDs & CIDs. + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: + * "acpi:IBM0001:ACPI0001" + */ +int acpi_device_modalias(struct device *dev, char *buf, int size) +{ + return __acpi_device_modalias(acpi_companion_match(dev), buf, size); +} +EXPORT_SYMBOL_GPL(acpi_device_modalias); + +static ssize_t +acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { + return __acpi_device_modalias(to_acpi_device(dev), buf, 1024); +} +static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); + +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) +{ + struct acpi_device_physical_node *pn; + bool offline = true; + + /* + * acpi_container_offline() calls this for all of the container's + * children under the container's physical_node_lock lock. + */ + mutex_lock_nested(&adev->physical_node_lock, SINGLE_DEPTH_NESTING); + + list_for_each_entry(pn, &adev->physical_node_list, node) + if (device_supports_offline(pn->dev) && !pn->dev->offline) { + if (uevent) + kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); + + offline = false; + break; + } + + mutex_unlock(&adev->physical_node_lock); + return offline; +} + +static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data, + void **ret_p) +{ + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; + bool second_pass = (bool)data; + acpi_status status = AE_OK; + + if (acpi_bus_get_device(handle, &device)) + return AE_OK; + + if (device->handler && !device->handler->hotplug.enabled) { + *ret_p = &device->dev; + return AE_SUPPORT; + } + + mutex_lock(&device->physical_node_lock); + + list_for_each_entry(pn, &device->physical_node_list, node) { + int ret; + + if (second_pass) { + /* Skip devices offlined by the first pass. */ + if (pn->put_online) + continue; + } else { + pn->put_online = false; + } + ret = device_offline(pn->dev); + if (acpi_force_hot_remove) + continue; + + if (ret >= 0) { + pn->put_online = !ret; + } else { + *ret_p = pn->dev; + if (second_pass) { + status = AE_ERROR; + break; + } + } + } + + mutex_unlock(&device->physical_node_lock); + + return status; +} + +static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data, + void **ret_p) +{ + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; + + if (acpi_bus_get_device(handle, &device)) + return AE_OK; + + mutex_lock(&device->physical_node_lock); + + list_for_each_entry(pn, &device->physical_node_list, node) + if (pn->put_online) { + device_online(pn->dev); + pn->put_online = false; + } + + mutex_unlock(&device->physical_node_lock); + + return AE_OK; +} + +static int acpi_scan_try_to_offline(struct acpi_device *device) +{ + acpi_handle handle = device->handle; + struct device *errdev = NULL; + acpi_status status; + + /* + * Carry out two passes here and ignore errors in the first pass, + * because if the devices in question are memory blocks and + * CONFIG_MEMCG is set, one of the blocks may hold data structures + * that the other blocks depend on, but it is not known in advance which + * block holds them. + * + * If the first pass is successful, the second one isn't needed, though. + */ + status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + NULL, acpi_bus_offline, (void *)false, + (void **)&errdev); + if (status == AE_SUPPORT) { + dev_warn(errdev, "Offline disabled.\n"); + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_online, NULL, NULL, NULL); + return -EPERM; + } + acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev); + if (errdev) { + errdev = NULL; + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + NULL, acpi_bus_offline, (void *)true, + (void **)&errdev); + if (!errdev || acpi_force_hot_remove) + acpi_bus_offline(handle, 0, (void *)true, + (void **)&errdev); + + if (errdev && !acpi_force_hot_remove) { + dev_warn(errdev, "Offline failed.\n"); + acpi_bus_online(handle, 0, NULL, NULL); + acpi_walk_namespace(ACPI_TYPE_ANY, handle, + ACPI_UINT32_MAX, acpi_bus_online, + NULL, NULL, NULL); + return -EBUSY; + } + } + return 0; +} + +static int acpi_scan_hot_remove(struct acpi_device *device) +{ + acpi_handle handle = device->handle; + unsigned long long sta; + acpi_status status; + + if (device->handler && device->handler->hotplug.demand_offline + && !acpi_force_hot_remove) { + if (!acpi_scan_is_offline(device, true)) + return -EBUSY; + } else { + int error = acpi_scan_try_to_offline(device); + if (error) + return error; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Hot-removing device %s...\n", dev_name(&device->dev))); + + acpi_bus_trim(device); + + acpi_evaluate_lck(handle, 0); + /* + * TBD: _EJD support. + */ + status = acpi_evaluate_ej0(handle); + if (status == AE_NOT_FOUND) + return -ENODEV; + else if (ACPI_FAILURE(status)) + return -EIO; + + /* + * Verify if eject was indeed successful. If not, log an error + * message. No need to call _OST since _EJ0 call was made OK. + */ + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, + "Status check after eject failed (0x%x)\n", status); + } else if (sta & ACPI_STA_DEVICE_ENABLED) { + acpi_handle_warn(handle, + "Eject incomplete - status 0x%llx\n", sta); + } + + return 0; +} + +static int acpi_scan_device_not_present(struct acpi_device *adev) +{ + if (!acpi_device_enumerated(adev)) { + dev_warn(&adev->dev, "Still not present\n"); + return -EALREADY; + } + acpi_bus_trim(adev); + return 0; +} + +static int acpi_scan_device_check(struct acpi_device *adev) +{ + int error; + + acpi_bus_get_status(adev); + if (adev->status.present || adev->status.functional) { + /* + * This function is only called for device objects for which + * matching scan handlers exist. The only situation in which + * the scan handler is not attached to this device object yet + * is when the device has just appeared (either it wasn't + * present at all before or it was removed and then added + * again). + */ + if (adev->handler) { + dev_warn(&adev->dev, "Already enumerated\n"); + return -EALREADY; + } + error = acpi_bus_scan(adev->handle); + if (error) { + dev_warn(&adev->dev, "Namespace scan failure\n"); + return error; + } + if (!adev->handler) { + dev_warn(&adev->dev, "Enumeration failure\n"); + error = -ENODEV; + } + } else { + error = acpi_scan_device_not_present(adev); + } + return error; +} + +static int acpi_scan_bus_check(struct acpi_device *adev) +{ + struct acpi_scan_handler *handler = adev->handler; + struct acpi_device *child; + int error; + + acpi_bus_get_status(adev); + if (!(adev->status.present || adev->status.functional)) { + acpi_scan_device_not_present(adev); + return 0; + } + if (handler && handler->hotplug.scan_dependent) + return handler->hotplug.scan_dependent(adev); + + error = acpi_bus_scan(adev->handle); + if (error) { + dev_warn(&adev->dev, "Namespace scan failure\n"); + return error; + } + list_for_each_entry(child, &adev->children, node) { + error = acpi_scan_bus_check(child); + if (error) + return error; + } + return 0; +} + +static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type) +{ + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + return acpi_scan_bus_check(adev); + case ACPI_NOTIFY_DEVICE_CHECK: + return acpi_scan_device_check(adev); + case ACPI_NOTIFY_EJECT_REQUEST: + case ACPI_OST_EC_OSPM_EJECT: + if (adev->handler && !adev->handler->hotplug.enabled) { + dev_info(&adev->dev, "Eject disabled\n"); + return -EPERM; + } + acpi_evaluate_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + return acpi_scan_hot_remove(adev); + } + return -EINVAL; +} + +void acpi_device_hotplug(struct acpi_device *adev, u32 src) +{ + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; + int error = -ENODEV; + + lock_device_hotplug(); + mutex_lock(&acpi_scan_lock); + + /* + * The device object's ACPI handle cannot become invalid as long as we + * are holding acpi_scan_lock, but it might have become invalid before + * that lock was acquired. + */ + if (adev->handle == INVALID_ACPI_HANDLE) + goto err_out; + + if (adev->flags.is_dock_station) { + error = dock_notify(adev, src); + } else if (adev->flags.hotplug_notify) { + error = acpi_generic_hotplug_event(adev, src); + if (error == -EPERM) { + ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; + goto err_out; + } + } else { + int (*notify)(struct acpi_device *, u32); + + acpi_lock_hp_context(); + notify = adev->hp ? adev->hp->notify : NULL; + acpi_unlock_hp_context(); + /* + * There may be additional notify handlers for device objects + * without the .event() callback, so ignore them here. + */ + if (notify) + error = notify(adev, src); + else + goto out; + } + if (!error) + ost_code = ACPI_OST_SC_SUCCESS; + + err_out: + acpi_evaluate_ost(adev->handle, src, ost_code, NULL); + + out: + acpi_bus_put_acpi_device(adev); + mutex_unlock(&acpi_scan_lock); + unlock_device_hotplug(); +} + +static ssize_t real_power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + int state; + int ret; + + ret = acpi_device_get_power(adev, &state); + if (ret) + return ret; + + return sprintf(buf, "%s\n", acpi_power_state_string(state)); +} + +static DEVICE_ATTR(real_power_state, 0444, real_power_state_show, NULL); + +static ssize_t power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *adev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state)); +} + +static DEVICE_ATTR(power_state, 0444, power_state_show, NULL); + +static ssize_t +acpi_eject_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_device = to_acpi_device(d); + acpi_object_type not_used; + acpi_status status; + + if (!count || buf[0] != '1') + return -EINVAL; + + if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) + && !acpi_device->driver) + return -ENODEV; + + status = acpi_get_type(acpi_device->handle, ¬_used); + if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) + return -ENODEV; + + get_device(&acpi_device->dev); + status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); + if (ACPI_SUCCESS(status)) + return count; + + put_device(&acpi_device->dev); + acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; +} + +static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); + +static ssize_t +acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev)); +} +static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); + +static ssize_t acpi_device_uid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id); +} +static DEVICE_ATTR(uid, 0444, acpi_device_uid_show, NULL); + +static ssize_t acpi_device_adr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + + return sprintf(buf, "0x%08x\n", + (unsigned int)(acpi_dev->pnp.bus_address)); +} +static DEVICE_ATTR(adr, 0444, acpi_device_adr_show, NULL); + +static ssize_t +acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; + int result; + + result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); + if (result) + goto end; + + result = sprintf(buf, "%s\n", (char*)path.pointer); + kfree(path.pointer); +end: + return result; +} +static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); + +/* sysfs file that shows description text from the ACPI _STR method */ +static ssize_t description_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + int result; + + if (acpi_dev->pnp.str_obj == NULL) + return 0; + + /* + * The _STR object contains a Unicode identifier for a device. + * We need to convert to utf-8 so it can be displayed. + */ + result = utf16s_to_utf8s( + (wchar_t *)acpi_dev->pnp.str_obj->buffer.pointer, + acpi_dev->pnp.str_obj->buffer.length, + UTF16_LITTLE_ENDIAN, buf, + PAGE_SIZE); + + buf[result++] = '\n'; + + return result; +} +static DEVICE_ATTR(description, 0444, description_show, NULL); + +static ssize_t +acpi_device_sun_show(struct device *dev, struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + acpi_status status; + unsigned long long sun; + + status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return sprintf(buf, "%llu\n", sun); +} +static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL); + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + acpi_status status; + unsigned long long sta; + + status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return sprintf(buf, "%llu\n", sta); +} +static DEVICE_ATTR_RO(status); + +static int acpi_device_setup_files(struct acpi_device *dev) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + int result = 0; + + /* + * Devices gotten from FADT don't have a "path" attribute + */ + if (dev->handle) { + result = device_create_file(&dev->dev, &dev_attr_path); + if (result) + goto end; + } + + if (!list_empty(&dev->pnp.ids)) { + result = device_create_file(&dev->dev, &dev_attr_hid); + if (result) + goto end; + + result = device_create_file(&dev->dev, &dev_attr_modalias); + if (result) + goto end; + } + + /* + * If device has _STR, 'description' file is created + */ + if (acpi_has_method(dev->handle, "_STR")) { + status = acpi_evaluate_object(dev->handle, "_STR", + NULL, &buffer); + if (ACPI_FAILURE(status)) + buffer.pointer = NULL; + dev->pnp.str_obj = buffer.pointer; + result = device_create_file(&dev->dev, &dev_attr_description); + if (result) + goto end; + } + + if (dev->pnp.type.bus_address) + result = device_create_file(&dev->dev, &dev_attr_adr); + if (dev->pnp.unique_id) + result = device_create_file(&dev->dev, &dev_attr_uid); + + if (acpi_has_method(dev->handle, "_SUN")) { + result = device_create_file(&dev->dev, &dev_attr_sun); + if (result) + goto end; + } + + if (acpi_has_method(dev->handle, "_STA")) { + result = device_create_file(&dev->dev, &dev_attr_status); + if (result) + goto end; + } + + /* + * If device has _EJ0, 'eject' file is created that is used to trigger + * hot-removal function from userland. + */ + if (acpi_has_method(dev->handle, "_EJ0")) { + result = device_create_file(&dev->dev, &dev_attr_eject); + if (result) + return result; + } + + if (dev->flags.power_manageable) { + result = device_create_file(&dev->dev, &dev_attr_power_state); + if (result) + return result; + + if (dev->power.flags.power_resources) + result = device_create_file(&dev->dev, + &dev_attr_real_power_state); + } + +end: + return result; +} + +static void acpi_device_remove_files(struct acpi_device *dev) +{ + if (dev->flags.power_manageable) { + device_remove_file(&dev->dev, &dev_attr_power_state); + if (dev->power.flags.power_resources) + device_remove_file(&dev->dev, + &dev_attr_real_power_state); + } + + /* + * If device has _STR, remove 'description' file + */ + if (acpi_has_method(dev->handle, "_STR")) { + kfree(dev->pnp.str_obj); + device_remove_file(&dev->dev, &dev_attr_description); + } + /* + * If device has _EJ0, remove 'eject' file. + */ + if (acpi_has_method(dev->handle, "_EJ0")) + device_remove_file(&dev->dev, &dev_attr_eject); + + if (acpi_has_method(dev->handle, "_SUN")) + device_remove_file(&dev->dev, &dev_attr_sun); + + if (dev->pnp.unique_id) + device_remove_file(&dev->dev, &dev_attr_uid); + if (dev->pnp.type.bus_address) + device_remove_file(&dev->dev, &dev_attr_adr); + device_remove_file(&dev->dev, &dev_attr_modalias); + device_remove_file(&dev->dev, &dev_attr_hid); + if (acpi_has_method(dev->handle, "_STA")) + device_remove_file(&dev->dev, &dev_attr_status); + if (dev->handle) + device_remove_file(&dev->dev, &dev_attr_path); +} +/* -------------------------------------------------------------------------- + ACPI Bus operations + -------------------------------------------------------------------------- */ + +/** + * acpi_of_match_device - Match device object using the "compatible" property. + * @adev: ACPI device object to match. + * @of_match_table: List of device IDs to match against. + * + * If @dev has an ACPI companion which has the special PRP0001 device ID in its + * list of identifiers and a _DSD object with the "compatible" property, use + * that property to match against the given list of identifiers. + */ +static bool acpi_of_match_device(struct acpi_device *adev, + const struct of_device_id *of_match_table) +{ + const union acpi_object *of_compatible, *obj; + int i, nval; + + if (!adev) + return false; + + of_compatible = adev->data.of_compatible; + if (!of_match_table || !of_compatible) + return false; + + if (of_compatible->type == ACPI_TYPE_PACKAGE) { + nval = of_compatible->package.count; + obj = of_compatible->package.elements; + } else { /* Must be ACPI_TYPE_STRING. */ + nval = 1; + obj = of_compatible; + } + /* Now we can look for the driver DT compatible strings */ + for (i = 0; i < nval; i++, obj++) { + const struct of_device_id *id; + + for (id = of_match_table; id->compatible[0]; id++) + if (!strcasecmp(obj->string.pointer, id->compatible)) + return true; + } + + return false; +} + +static const struct acpi_device_id *__acpi_match_device( + struct acpi_device *device, + const struct acpi_device_id *ids, + const struct of_device_id *of_ids) +{ + const struct acpi_device_id *id; + struct acpi_hardware_id *hwid; + + /* + * If the device is not present, it is unnecessary to load device + * driver for it. + */ + if (!device || !device->status.present) + return NULL; + + list_for_each_entry(hwid, &device->pnp.ids, list) { + /* First, check the ACPI/PNP IDs provided by the caller. */ + for (id = ids; id->id[0]; id++) + if (!strcmp((char *) id->id, hwid->id)) + return id; + + /* + * Next, check the special "PRP0001" ID and try to match the + * "compatible" property if found. + * + * The id returned by the below is not valid, but the only + * caller passing non-NULL of_ids here is only interested in + * whether or not the return value is NULL. + */ + if (!strcmp("PRP0001", hwid->id) + && acpi_of_match_device(device, of_ids)) + return id; + } + return NULL; +} + +/** + * acpi_match_device - Match a struct device against a given list of ACPI IDs + * @ids: Array of struct acpi_device_id object to match against. + * @dev: The device structure to match. + * + * Check if @dev has a valid ACPI handle and if there is a struct acpi_device + * object for that handle and use that object to match against a given list of + * device IDs. + * + * Return a pointer to the first matching ID on success or %NULL on failure. + */ +const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, + const struct device *dev) +{ + return __acpi_match_device(acpi_companion_match(dev), ids, NULL); +} +EXPORT_SYMBOL_GPL(acpi_match_device); + +int acpi_match_device_ids(struct acpi_device *device, + const struct acpi_device_id *ids) +{ + return __acpi_match_device(device, ids, NULL) ? 0 : -ENOENT; +} +EXPORT_SYMBOL(acpi_match_device_ids); + +bool acpi_driver_match_device(struct device *dev, + const struct device_driver *drv) +{ + if (!drv->acpi_match_table) + return acpi_of_match_device(ACPI_COMPANION(dev), + drv->of_match_table); + + return !!__acpi_match_device(acpi_companion_match(dev), + drv->acpi_match_table, drv->of_match_table); +} +EXPORT_SYMBOL_GPL(acpi_driver_match_device); + +static void acpi_free_power_resources_lists(struct acpi_device *device) +{ + int i; + + if (device->wakeup.flags.valid) + acpi_power_resources_list_free(&device->wakeup.resources); + + if (!device->power.flags.power_resources) + return; + + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { + struct acpi_device_power_state *ps = &device->power.states[i]; + acpi_power_resources_list_free(&ps->resources); + } +} + +static void acpi_device_release(struct device *dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + + acpi_free_properties(acpi_dev); + acpi_free_pnp_ids(&acpi_dev->pnp); + acpi_free_power_resources_lists(acpi_dev); + kfree(acpi_dev); +} + +static int acpi_bus_match(struct device *dev, struct device_driver *drv) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(drv); + + return acpi_dev->flags.match_driver + && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); +} + +static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + return __acpi_device_uevent_modalias(to_acpi_device(dev), env); +} + +static void acpi_device_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_device *device = data; + + device->driver->ops.notify(device, event); +} + +static void acpi_device_notify_fixed(void *data) +{ + struct acpi_device *device = data; + + /* Fixed hardware devices have no handles */ + acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device); +} + +static u32 acpi_device_fixed_event(void *data) +{ + acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data); + return ACPI_INTERRUPT_HANDLED; +} + +static int acpi_device_install_notify_handler(struct acpi_device *device) +{ + acpi_status status; + + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) + status = + acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_fixed_event, + device); + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) + status = + acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_fixed_event, + device); + else + status = acpi_install_notify_handler(device->handle, + ACPI_DEVICE_NOTIFY, + acpi_device_notify, + device); + + if (ACPI_FAILURE(status)) + return -EINVAL; + return 0; +} + +static void acpi_device_remove_notify_handler(struct acpi_device *device) +{ + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) + acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, + acpi_device_fixed_event); + else if (device->device_type == ACPI_BUS_TYPE_SLEEP_BUTTON) + acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, + acpi_device_fixed_event); + else + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_device_notify); +} + +static int acpi_device_probe(struct device *dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); + int ret; + + if (acpi_dev->handler && !acpi_is_pnp_device(acpi_dev)) + return -EINVAL; + + if (!acpi_drv->ops.add) + return -ENOSYS; + + ret = acpi_drv->ops.add(acpi_dev); + if (ret) + return ret; + + acpi_dev->driver = acpi_drv; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Driver [%s] successfully bound to device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + + if (acpi_drv->ops.notify) { + ret = acpi_device_install_notify_handler(acpi_dev); + if (ret) { + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); + + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; + return ret; + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found driver [%s] for device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + get_device(dev); + return 0; +} + +static int acpi_device_remove(struct device * dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = acpi_dev->driver; + + if (acpi_drv) { + if (acpi_drv->ops.notify) + acpi_device_remove_notify_handler(acpi_dev); + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); + } + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; + + put_device(dev); + return 0; +} + +struct bus_type acpi_bus_type = { + .name = "acpi", + .match = acpi_bus_match, + .probe = acpi_device_probe, + .remove = acpi_device_remove, + .uevent = acpi_device_uevent, +}; + +static void acpi_device_del(struct acpi_device *device) +{ + mutex_lock(&acpi_device_lock); + if (device->parent) + list_del(&device->node); + + list_del(&device->wakeup_list); + mutex_unlock(&acpi_device_lock); + + acpi_power_add_remove_device(device, false); + acpi_device_remove_files(device); + if (device->remove) + device->remove(device); + + device_del(&device->dev); +} + +static LIST_HEAD(acpi_device_del_list); +static DEFINE_MUTEX(acpi_device_del_lock); + +static void acpi_device_del_work_fn(struct work_struct *work_not_used) +{ + for (;;) { + struct acpi_device *adev; + + mutex_lock(&acpi_device_del_lock); + + if (list_empty(&acpi_device_del_list)) { + mutex_unlock(&acpi_device_del_lock); + break; + } + adev = list_first_entry(&acpi_device_del_list, + struct acpi_device, del_list); + list_del(&adev->del_list); + + mutex_unlock(&acpi_device_del_lock); + + acpi_device_del(adev); + /* + * Drop references to all power resources that might have been + * used by the device. + */ + acpi_power_transition(adev, ACPI_STATE_D3_COLD); + put_device(&adev->dev); + } +} + +/** + * acpi_scan_drop_device - Drop an ACPI device object. + * @handle: Handle of an ACPI namespace node, not used. + * @context: Address of the ACPI device object to drop. + * + * This is invoked by acpi_ns_delete_node() during the removal of the ACPI + * namespace node the device object pointed to by @context is attached to. + * + * The unregistration is carried out asynchronously to avoid running + * acpi_device_del() under the ACPICA's namespace mutex and the list is used to + * ensure the correct ordering (the device objects must be unregistered in the + * same order in which the corresponding namespace nodes are deleted). + */ +static void acpi_scan_drop_device(acpi_handle handle, void *context) +{ + static DECLARE_WORK(work, acpi_device_del_work_fn); + struct acpi_device *adev = context; + + mutex_lock(&acpi_device_del_lock); + + /* + * Use the ACPI hotplug workqueue which is ordered, so this work item + * won't run after any hotplug work items submitted subsequently. That + * prevents attempts to register device objects identical to those being + * deleted from happening concurrently (such attempts result from + * hotplug events handled via the ACPI hotplug workqueue). It also will + * run after all of the work items submitted previosuly, which helps + * those work items to ensure that they are not accessing stale device + * objects. + */ + if (list_empty(&acpi_device_del_list)) + acpi_queue_hotplug_work(&work); + + list_add_tail(&adev->del_list, &acpi_device_del_list); + /* Make acpi_ns_validate_handle() return NULL for this handle. */ + adev->handle = INVALID_ACPI_HANDLE; + + mutex_unlock(&acpi_device_del_lock); +} + +static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device, + void (*callback)(void *)) +{ + acpi_status status; + + if (!device) + return -EINVAL; + + status = acpi_get_data_full(handle, acpi_scan_drop_device, + (void **)device, callback); + if (ACPI_FAILURE(status) || !*device) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", + handle)); + return -ENODEV; + } + return 0; +} + +int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +{ + return acpi_get_device_data(handle, device, NULL); +} +EXPORT_SYMBOL(acpi_bus_get_device); + +static void get_acpi_device(void *dev) +{ + if (dev) + get_device(&((struct acpi_device *)dev)->dev); +} + +struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle) +{ + struct acpi_device *adev = NULL; + + acpi_get_device_data(handle, &adev, get_acpi_device); + return adev; +} + +void acpi_bus_put_acpi_device(struct acpi_device *adev) +{ + put_device(&adev->dev); +} + +int acpi_device_add(struct acpi_device *device, + void (*release)(struct device *)) +{ + int result; + struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; + int found = 0; + + if (device->handle) { + acpi_status status; + + status = acpi_attach_data(device->handle, acpi_scan_drop_device, + device); + if (ACPI_FAILURE(status)) { + acpi_handle_err(device->handle, + "Unable to attach device data\n"); + return -ENODEV; + } + } + + /* + * Linkage + * ------- + * Link this device to its parent and siblings. + */ + INIT_LIST_HEAD(&device->children); + INIT_LIST_HEAD(&device->node); + INIT_LIST_HEAD(&device->wakeup_list); + INIT_LIST_HEAD(&device->physical_node_list); + INIT_LIST_HEAD(&device->del_list); + mutex_init(&device->physical_node_lock); + + new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); + if (!new_bus_id) { + pr_err(PREFIX "Memory allocation error\n"); + result = -ENOMEM; + goto err_detach; + } + + mutex_lock(&acpi_device_lock); + /* + * Find suitable bus_id and instance number in acpi_bus_id_list + * If failed, create one and link it into acpi_bus_id_list + */ + list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) { + if (!strcmp(acpi_device_bus_id->bus_id, + acpi_device_hid(device))) { + acpi_device_bus_id->instance_no++; + found = 1; + kfree(new_bus_id); + break; + } + } + if (!found) { + acpi_device_bus_id = new_bus_id; + strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device)); + acpi_device_bus_id->instance_no = 0; + list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); + } + dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); + + if (device->parent) + list_add_tail(&device->node, &device->parent->children); + + if (device->wakeup.flags.valid) + list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list); + mutex_unlock(&acpi_device_lock); + + if (device->parent) + device->dev.parent = &device->parent->dev; + device->dev.bus = &acpi_bus_type; + device->dev.release = release; + result = device_add(&device->dev); + if (result) { + dev_err(&device->dev, "Error registering device\n"); + goto err; + } + + result = acpi_device_setup_files(device); + if (result) + printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", + dev_name(&device->dev)); + + return 0; + + err: + mutex_lock(&acpi_device_lock); + if (device->parent) + list_del(&device->node); + list_del(&device->wakeup_list); + mutex_unlock(&acpi_device_lock); + + err_detach: + acpi_detach_data(device->handle, acpi_scan_drop_device); + return result; +} + +struct acpi_device *acpi_get_next_child(struct device *dev, + struct acpi_device *child) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + struct list_head *head, *next; + + if (!adev) + return NULL; + + head = &adev->children; + if (list_empty(head)) + return NULL; + + if (!child) + return list_first_entry(head, struct acpi_device, node); + + next = child->node.next; + return next == head ? NULL : list_entry(next, struct acpi_device, node); +} + +/* -------------------------------------------------------------------------- + Driver Management + -------------------------------------------------------------------------- */ +/** + * acpi_bus_register_driver - register a driver with the ACPI bus + * @driver: driver being registered + * + * Registers a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and binds. Returns zero for + * success or a negative error status for failure. + */ +int acpi_bus_register_driver(struct acpi_driver *driver) +{ + int ret; + + if (acpi_disabled) + return -ENODEV; + driver->drv.name = driver->name; + driver->drv.bus = &acpi_bus_type; + driver->drv.owner = driver->owner; + + ret = driver_register(&driver->drv); + return ret; +} + +EXPORT_SYMBOL(acpi_bus_register_driver); + +/** + * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus + * @driver: driver to unregister + * + * Unregisters a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and unbinds. + */ +void acpi_bus_unregister_driver(struct acpi_driver *driver) +{ + driver_unregister(&driver->drv); +} + +EXPORT_SYMBOL(acpi_bus_unregister_driver); + +/* -------------------------------------------------------------------------- + Device Enumeration + -------------------------------------------------------------------------- */ +static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) +{ + struct acpi_device *device = NULL; + acpi_status status; + + /* + * Fixed hardware devices do not appear in the namespace and do not + * have handles, but we fabricate acpi_devices for them, so we have + * to deal with them specially. + */ + if (!handle) + return acpi_root; + + do { + status = acpi_get_parent(handle, &handle); + if (ACPI_FAILURE(status)) + return status == AE_NULL_ENTRY ? NULL : acpi_root; + } while (acpi_bus_get_device(handle, &device)); + return device; +} + +acpi_status +acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) +{ + acpi_status status; + acpi_handle tmp; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + + status = acpi_get_handle(handle, "_EJD", &tmp); + if (ACPI_FAILURE(status)) + return status; + + status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer); + if (ACPI_SUCCESS(status)) { + obj = buffer.pointer; + status = acpi_get_handle(ACPI_ROOT_OBJECT, obj->string.pointer, + ejd); + kfree(buffer.pointer); + } + return status; +} +EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); + +static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, + struct acpi_device_wakeup *wakeup) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package = NULL; + union acpi_object *element = NULL; + acpi_status status; + int err = -ENODATA; + + if (!wakeup) + return -EINVAL; + + INIT_LIST_HEAD(&wakeup->resources); + + /* _PRW */ + status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRW")); + return err; + } + + package = (union acpi_object *)buffer.pointer; + + if (!package || package->package.count < 2) + goto out; + + element = &(package->package.elements[0]); + if (!element) + goto out; + + if (element->type == ACPI_TYPE_PACKAGE) { + if ((element->package.count < 2) || + (element->package.elements[0].type != + ACPI_TYPE_LOCAL_REFERENCE) + || (element->package.elements[1].type != ACPI_TYPE_INTEGER)) + goto out; + + wakeup->gpe_device = + element->package.elements[0].reference.handle; + wakeup->gpe_number = + (u32) element->package.elements[1].integer.value; + } else if (element->type == ACPI_TYPE_INTEGER) { + wakeup->gpe_device = NULL; + wakeup->gpe_number = element->integer.value; + } else { + goto out; + } + + element = &(package->package.elements[1]); + if (element->type != ACPI_TYPE_INTEGER) + goto out; + + wakeup->sleep_state = element->integer.value; + + err = acpi_extract_power_resources(package, 2, &wakeup->resources); + if (err) + goto out; + + if (!list_empty(&wakeup->resources)) { + int sleep_state; + + err = acpi_power_wakeup_list_init(&wakeup->resources, + &sleep_state); + if (err) { + acpi_handle_warn(handle, "Retrieving current states " + "of wakeup power resources failed\n"); + acpi_power_resources_list_free(&wakeup->resources); + goto out; + } + if (sleep_state < wakeup->sleep_state) { + acpi_handle_warn(handle, "Overriding _PRW sleep state " + "(S%d) by S%d from power resources\n", + (int)wakeup->sleep_state, sleep_state); + wakeup->sleep_state = sleep_state; + } + } + + out: + kfree(buffer.pointer); + return err; +} + +static void acpi_wakeup_gpe_init(struct acpi_device *device) +{ + struct acpi_device_id button_device_ids[] = { + {"PNP0C0C", 0}, + {"PNP0C0D", 0}, + {"PNP0C0E", 0}, + {"", 0}, + }; + struct acpi_device_wakeup *wakeup = &device->wakeup; + acpi_status status; + acpi_event_status event_status; + + wakeup->flags.notifier_present = 0; + + /* Power button, Lid switch always enable wakeup */ + if (!acpi_match_device_ids(device, button_device_ids)) { + wakeup->flags.run_wake = 1; + if (!acpi_match_device_ids(device, &button_device_ids[1])) { + /* Do not use Lid/sleep button for S5 wakeup */ + if (wakeup->sleep_state == ACPI_STATE_S5) + wakeup->sleep_state = ACPI_STATE_S4; + } + acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number); + device_set_wakeup_capable(&device->dev, true); + return; + } + + acpi_setup_gpe_for_wake(device->handle, wakeup->gpe_device, + wakeup->gpe_number); + status = acpi_get_gpe_status(wakeup->gpe_device, wakeup->gpe_number, + &event_status); + if (ACPI_FAILURE(status)) + return; + + wakeup->flags.run_wake = !!(event_status & ACPI_EVENT_FLAG_HAS_HANDLER); +} + +static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) +{ + int err; + + /* Presence of _PRW indicates wake capable */ + if (!acpi_has_method(device->handle, "_PRW")) + return; + + err = acpi_bus_extract_wakeup_device_power_package(device->handle, + &device->wakeup); + if (err) { + dev_err(&device->dev, "_PRW evaluation error: %d\n", err); + return; + } + + device->wakeup.flags.valid = 1; + device->wakeup.prepare_count = 0; + acpi_wakeup_gpe_init(device); + /* Call _PSW/_DSW object to disable its ability to wake the sleeping + * system for the ACPI device with the _PRW object. + * The _PSW object is depreciated in ACPI 3.0 and is replaced by _DSW. + * So it is necessary to call _DSW object first. Only when it is not + * present will the _PSW object used. + */ + err = acpi_device_sleep_wake(device, 0, 0, 0); + if (err) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "error in _DSW or _PSW evaluation\n")); +} + +static void acpi_bus_init_power_state(struct acpi_device *device, int state) +{ + struct acpi_device_power_state *ps = &device->power.states[state]; + char pathname[5] = { '_', 'P', 'R', '0' + state, '\0' }; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + + INIT_LIST_HEAD(&ps->resources); + + /* Evaluate "_PRx" to get referenced power resources */ + status = acpi_evaluate_object(device->handle, pathname, NULL, &buffer); + if (ACPI_SUCCESS(status)) { + union acpi_object *package = buffer.pointer; + + if (buffer.length && package + && package->type == ACPI_TYPE_PACKAGE + && package->package.count) { + int err = acpi_extract_power_resources(package, 0, + &ps->resources); + if (!err) + device->power.flags.power_resources = 1; + } + ACPI_FREE(buffer.pointer); + } + + /* Evaluate "_PSx" to see if we can do explicit sets */ + pathname[2] = 'S'; + if (acpi_has_method(device->handle, pathname)) + ps->flags.explicit_set = 1; + + /* + * State is valid if there are means to put the device into it. + * D3hot is only valid if _PR3 present. + */ + if (!list_empty(&ps->resources) + || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) { + ps->flags.valid = 1; + ps->flags.os_accessible = 1; + } + + ps->power = -1; /* Unknown - driver assigned */ + ps->latency = -1; /* Unknown - driver assigned */ +} + +static void acpi_bus_get_power_flags(struct acpi_device *device) +{ + u32 i; + + /* Presence of _PS0|_PR0 indicates 'power manageable' */ + if (!acpi_has_method(device->handle, "_PS0") && + !acpi_has_method(device->handle, "_PR0")) + return; + + device->flags.power_manageable = 1; + + /* + * Power Management Flags + */ + if (acpi_has_method(device->handle, "_PSC")) + device->power.flags.explicit_get = 1; + + if (acpi_has_method(device->handle, "_IRC")) + device->power.flags.inrush_current = 1; + + if (acpi_has_method(device->handle, "_DSW")) + device->power.flags.dsw_present = 1; + + /* + * Enumerate supported power management states + */ + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) + acpi_bus_init_power_state(device, i); + + INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources); + + /* Set defaults for D0 and D3 states (always valid) */ + device->power.states[ACPI_STATE_D0].flags.valid = 1; + device->power.states[ACPI_STATE_D0].power = 100; + device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; + device->power.states[ACPI_STATE_D3_COLD].power = 0; + + /* Set D3cold's explicit_set flag if _PS3 exists. */ + if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set) + device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1; + + /* Presence of _PS3 or _PRx means we can put the device into D3 cold */ + if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set || + device->power.flags.power_resources) + device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; + + if (acpi_bus_init_power(device)) + device->flags.power_manageable = 0; +} + +static void acpi_bus_get_flags(struct acpi_device *device) +{ + /* Presence of _STA indicates 'dynamic_status' */ + if (acpi_has_method(device->handle, "_STA")) + device->flags.dynamic_status = 1; + + /* Presence of _RMV indicates 'removable' */ + if (acpi_has_method(device->handle, "_RMV")) + device->flags.removable = 1; + + /* Presence of _EJD|_EJ0 indicates 'ejectable' */ + if (acpi_has_method(device->handle, "_EJD") || + acpi_has_method(device->handle, "_EJ0")) + device->flags.ejectable = 1; +} + +static void acpi_device_get_busid(struct acpi_device *device) +{ + char bus_id[5] = { '?', 0 }; + struct acpi_buffer buffer = { sizeof(bus_id), bus_id }; + int i = 0; + + /* + * Bus ID + * ------ + * The device's Bus ID is simply the object name. + * TBD: Shouldn't this value be unique (within the ACPI namespace)? + */ + if (ACPI_IS_ROOT_DEVICE(device)) { + strcpy(device->pnp.bus_id, "ACPI"); + return; + } + + switch (device->device_type) { + case ACPI_BUS_TYPE_POWER_BUTTON: + strcpy(device->pnp.bus_id, "PWRF"); + break; + case ACPI_BUS_TYPE_SLEEP_BUTTON: + strcpy(device->pnp.bus_id, "SLPF"); + break; + default: + acpi_get_name(device->handle, ACPI_SINGLE_NAME, &buffer); + /* Clean up trailing underscores (if any) */ + for (i = 3; i > 1; i--) { + if (bus_id[i] == '_') + bus_id[i] = '\0'; + else + break; + } + strcpy(device->pnp.bus_id, bus_id); + break; + } +} + +/* + * acpi_ata_match - see if an acpi object is an ATA device + * + * If an acpi object has one of the ACPI ATA methods defined, + * then we can safely call it an ATA device. + */ +bool acpi_ata_match(acpi_handle handle) +{ + return acpi_has_method(handle, "_GTF") || + acpi_has_method(handle, "_GTM") || + acpi_has_method(handle, "_STM") || + acpi_has_method(handle, "_SDD"); +} + +/* + * acpi_bay_match - see if an acpi object is an ejectable driver bay + * + * If an acpi object is ejectable and has one of the ACPI ATA methods defined, + * then we can safely call it an ejectable drive bay + */ +bool acpi_bay_match(acpi_handle handle) +{ + acpi_handle phandle; + + if (!acpi_has_method(handle, "_EJ0")) + return false; + if (acpi_ata_match(handle)) + return true; + if (ACPI_FAILURE(acpi_get_parent(handle, &phandle))) + return false; + + return acpi_ata_match(phandle); +} + +bool acpi_device_is_battery(struct acpi_device *adev) +{ + struct acpi_hardware_id *hwid; + + list_for_each_entry(hwid, &adev->pnp.ids, list) + if (!strcmp("PNP0C0A", hwid->id)) + return true; + + return false; +} + +static bool is_ejectable_bay(struct acpi_device *adev) +{ + acpi_handle handle = adev->handle; + + if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(adev)) + return true; + + return acpi_bay_match(handle); +} + +/* + * acpi_dock_match - see if an acpi object has a _DCK method + */ +bool acpi_dock_match(acpi_handle handle) +{ + return acpi_has_method(handle, "_DCK"); +} + +const char *acpi_device_hid(struct acpi_device *device) +{ + struct acpi_hardware_id *hid; + + if (list_empty(&device->pnp.ids)) + return dummy_hid; + + hid = list_first_entry(&device->pnp.ids, struct acpi_hardware_id, list); + return hid->id; +} +EXPORT_SYMBOL(acpi_device_hid); + +static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id) +{ + struct acpi_hardware_id *id; + + id = kmalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return; + + id->id = kstrdup(dev_id, GFP_KERNEL); + if (!id->id) { + kfree(id); + return; + } + + list_add_tail(&id->list, &pnp->ids); + pnp->type.hardware_id = 1; +} + +/* + * Old IBM workstations have a DSDT bug wherein the SMBus object + * lacks the SMBUS01 HID and the methods do not have the necessary "_" + * prefix. Work around this. + */ +static bool acpi_ibm_smbus_match(acpi_handle handle) +{ + char node_name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer path = { sizeof(node_name), node_name }; + + if (!dmi_name_in_vendors("IBM")) + return false; + + /* Look for SMBS object */ + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &path)) || + strcmp("SMBS", path.pointer)) + return false; + + /* Does it have the necessary (but misnamed) methods? */ + if (acpi_has_method(handle, "SBI") && + acpi_has_method(handle, "SBR") && + acpi_has_method(handle, "SBW")) + return true; + + return false; +} + +static bool acpi_object_is_system_bus(acpi_handle handle) +{ + acpi_handle tmp; + + if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_SB", &tmp)) && + tmp == handle) + return true; + if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_TZ", &tmp)) && + tmp == handle) + return true; + + return false; +} + +static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, + int device_type) +{ + acpi_status status; + struct acpi_device_info *info; + struct acpi_pnp_device_id_list *cid_list; + int i; + + switch (device_type) { + case ACPI_BUS_TYPE_DEVICE: + if (handle == ACPI_ROOT_OBJECT) { + acpi_add_id(pnp, ACPI_SYSTEM_HID); + break; + } + + status = acpi_get_object_info(handle, &info); + if (ACPI_FAILURE(status)) { + pr_err(PREFIX "%s: Error reading device info\n", + __func__); + return; + } + + if (info->valid & ACPI_VALID_HID) { + acpi_add_id(pnp, info->hardware_id.string); + pnp->type.platform_id = 1; + } + if (info->valid & ACPI_VALID_CID) { + cid_list = &info->compatible_id_list; + for (i = 0; i < cid_list->count; i++) + acpi_add_id(pnp, cid_list->ids[i].string); + } + if (info->valid & ACPI_VALID_ADR) { + pnp->bus_address = info->address; + pnp->type.bus_address = 1; + } + if (info->valid & ACPI_VALID_UID) + pnp->unique_id = kstrdup(info->unique_id.string, + GFP_KERNEL); + + kfree(info); + + /* + * Some devices don't reliably have _HIDs & _CIDs, so add + * synthetic HIDs to make sure drivers can find them. + */ + if (acpi_is_video_device(handle)) + acpi_add_id(pnp, ACPI_VIDEO_HID); + else if (acpi_bay_match(handle)) + acpi_add_id(pnp, ACPI_BAY_HID); + else if (acpi_dock_match(handle)) + acpi_add_id(pnp, ACPI_DOCK_HID); + else if (acpi_ibm_smbus_match(handle)) + acpi_add_id(pnp, ACPI_SMBUS_IBM_HID); + else if (list_empty(&pnp->ids) && + acpi_object_is_system_bus(handle)) { + /* \_SB, \_TZ, LNXSYBUS */ + acpi_add_id(pnp, ACPI_BUS_HID); + strcpy(pnp->device_name, ACPI_BUS_DEVICE_NAME); + strcpy(pnp->device_class, ACPI_BUS_CLASS); + } + + break; + case ACPI_BUS_TYPE_POWER: + acpi_add_id(pnp, ACPI_POWER_HID); + break; + case ACPI_BUS_TYPE_PROCESSOR: + acpi_add_id(pnp, ACPI_PROCESSOR_OBJECT_HID); + break; + case ACPI_BUS_TYPE_THERMAL: + acpi_add_id(pnp, ACPI_THERMAL_HID); + break; + case ACPI_BUS_TYPE_POWER_BUTTON: + acpi_add_id(pnp, ACPI_BUTTON_HID_POWERF); + break; + case ACPI_BUS_TYPE_SLEEP_BUTTON: + acpi_add_id(pnp, ACPI_BUTTON_HID_SLEEPF); + break; + } +} + +void acpi_free_pnp_ids(struct acpi_device_pnp *pnp) +{ + struct acpi_hardware_id *id, *tmp; + + list_for_each_entry_safe(id, tmp, &pnp->ids, list) { + kfree(id->id); + kfree(id); + } + kfree(pnp->unique_id); +} + +void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, + int type, unsigned long long sta) +{ + INIT_LIST_HEAD(&device->pnp.ids); + device->device_type = type; + device->handle = handle; + device->parent = acpi_bus_get_parent(handle); + device->fwnode.type = FWNODE_ACPI; + acpi_set_device_status(device, sta); + acpi_device_get_busid(device); + acpi_set_pnp_ids(handle, &device->pnp, type); + acpi_init_properties(device); + acpi_bus_get_flags(device); + device->flags.match_driver = false; + device->flags.initialized = true; + device->flags.visited = false; + device_initialize(&device->dev); + dev_set_uevent_suppress(&device->dev, true); +} + +void acpi_device_add_finalize(struct acpi_device *device) +{ + dev_set_uevent_suppress(&device->dev, false); + kobject_uevent(&device->dev.kobj, KOBJ_ADD); +} + +static int acpi_add_single_object(struct acpi_device **child, + acpi_handle handle, int type, + unsigned long long sta) +{ + int result; + struct acpi_device *device; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL); + if (!device) { + printk(KERN_ERR PREFIX "Memory allocation error\n"); + return -ENOMEM; + } + + acpi_init_device_object(device, handle, type, sta); + acpi_bus_get_power_flags(device); + acpi_bus_get_wakeup_device_flags(device); + + result = acpi_device_add(device, acpi_device_release); + if (result) { + acpi_device_release(&device->dev); + return result; + } + + acpi_power_add_remove_device(device, true); + acpi_device_add_finalize(device); + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Added %s [%s] parent %s\n", + dev_name(&device->dev), (char *) buffer.pointer, + device->parent ? dev_name(&device->parent->dev) : "(null)")); + kfree(buffer.pointer); + *child = device; + return 0; +} + +static int acpi_bus_type_and_status(acpi_handle handle, int *type, + unsigned long long *sta) +{ + acpi_status status; + acpi_object_type acpi_type; + + status = acpi_get_type(handle, &acpi_type); + if (ACPI_FAILURE(status)) + return -ENODEV; + + switch (acpi_type) { + case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */ + case ACPI_TYPE_DEVICE: + *type = ACPI_BUS_TYPE_DEVICE; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + break; + case ACPI_TYPE_PROCESSOR: + *type = ACPI_BUS_TYPE_PROCESSOR; + status = acpi_bus_get_status_handle(handle, sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + break; + case ACPI_TYPE_THERMAL: + *type = ACPI_BUS_TYPE_THERMAL; + *sta = ACPI_STA_DEFAULT; + break; + case ACPI_TYPE_POWER: + *type = ACPI_BUS_TYPE_POWER; + *sta = ACPI_STA_DEFAULT; + break; + default: + return -ENODEV; + } + + return 0; +} + +bool acpi_device_is_present(struct acpi_device *adev) +{ + if (adev->status.present || adev->status.functional) + return true; + + adev->flags.initialized = false; + return false; +} + +static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, + char *idstr, + const struct acpi_device_id **matchid) +{ + const struct acpi_device_id *devid; + + if (handler->match) + return handler->match(idstr, matchid); + + for (devid = handler->ids; devid->id[0]; devid++) + if (!strcmp((char *)devid->id, idstr)) { + if (matchid) + *matchid = devid; + + return true; + } + + return false; +} + +static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, + const struct acpi_device_id **matchid) +{ + struct acpi_scan_handler *handler; + + list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) + if (acpi_scan_handler_matching(handler, idstr, matchid)) + return handler; + + return NULL; +} + +void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) +{ + if (!!hotplug->enabled == !!val) + return; + + mutex_lock(&acpi_scan_lock); + + hotplug->enabled = val; + + mutex_unlock(&acpi_scan_lock); +} + +static void acpi_scan_init_hotplug(struct acpi_device *adev) +{ + struct acpi_hardware_id *hwid; + + if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) { + acpi_dock_add(adev); + return; + } + list_for_each_entry(hwid, &adev->pnp.ids, list) { + struct acpi_scan_handler *handler; + + handler = acpi_scan_match_handler(hwid->id, NULL); + if (handler) { + adev->flags.hotplug_notify = true; + break; + } + } +} + +static void acpi_device_dep_initialize(struct acpi_device *adev) +{ + struct acpi_dep_data *dep; + struct acpi_handle_list dep_devices; + acpi_status status; + int i; + + if (!acpi_has_method(adev->handle, "_DEP")) + return; + + status = acpi_evaluate_reference(adev->handle, "_DEP", NULL, + &dep_devices); + if (ACPI_FAILURE(status)) { + dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n"); + return; + } + + for (i = 0; i < dep_devices.count; i++) { + struct acpi_device_info *info; + int skip; + + status = acpi_get_object_info(dep_devices.handles[i], &info); + if (ACPI_FAILURE(status)) { + dev_dbg(&adev->dev, "Error reading _DEP device info\n"); + continue; + } + + /* + * Skip the dependency of Windows System Power + * Management Controller + */ + skip = info->valid & ACPI_VALID_HID && + !strcmp(info->hardware_id.string, "INT3396"); + + kfree(info); + + if (skip) + continue; + + dep = kzalloc(sizeof(struct acpi_dep_data), GFP_KERNEL); + if (!dep) + return; + + dep->master = dep_devices.handles[i]; + dep->slave = adev->handle; + adev->dep_unmet++; + + mutex_lock(&acpi_dep_list_lock); + list_add_tail(&dep->node , &acpi_dep_list); + mutex_unlock(&acpi_dep_list_lock); + } +} + +static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, + void *not_used, void **return_value) +{ + struct acpi_device *device = NULL; + int type; + unsigned long long sta; + int result; + + acpi_bus_get_device(handle, &device); + if (device) + goto out; + + result = acpi_bus_type_and_status(handle, &type, &sta); + if (result) + return AE_OK; + + if (type == ACPI_BUS_TYPE_POWER) { + acpi_add_power_resource(handle); + return AE_OK; + } + + acpi_add_single_object(&device, handle, type, sta); + if (!device) + return AE_CTRL_DEPTH; + + acpi_scan_init_hotplug(device); + acpi_device_dep_initialize(device); + + out: + if (!*return_value) + *return_value = device; + + return AE_OK; +} + +static int acpi_check_spi_i2c_slave(struct acpi_resource *ares, void *data) +{ + bool *is_spi_i2c_slave_p = data; + + if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return 1; + + /* + * devices that are connected to UART still need to be enumerated to + * platform bus + */ + if (ares->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART) + *is_spi_i2c_slave_p = true; + + /* no need to do more checking */ + return -1; +} + +static void acpi_default_enumeration(struct acpi_device *device) +{ + struct list_head resource_list; + bool is_spi_i2c_slave = false; + + /* + * Do not enemerate SPI/I2C slaves as they will be enuerated by their + * respective parents. + */ + INIT_LIST_HEAD(&resource_list); + acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave, + &is_spi_i2c_slave); + acpi_dev_free_resource_list(&resource_list); + if (!is_spi_i2c_slave) + acpi_create_platform_device(device); +} + +static const struct acpi_device_id generic_device_ids[] = { + {"PRP0001", }, + {"", }, +}; + +static int acpi_generic_device_attach(struct acpi_device *adev, + const struct acpi_device_id *not_used) +{ + /* + * Since PRP0001 is the only ID handled here, the test below can be + * unconditional. + */ + if (adev->data.of_compatible) + acpi_default_enumeration(adev); + + return 1; +} + +static struct acpi_scan_handler generic_device_handler = { + .ids = generic_device_ids, + .attach = acpi_generic_device_attach, +}; + +static int acpi_scan_attach_handler(struct acpi_device *device) +{ + struct acpi_hardware_id *hwid; + int ret = 0; + + list_for_each_entry(hwid, &device->pnp.ids, list) { + const struct acpi_device_id *devid; + struct acpi_scan_handler *handler; + + handler = acpi_scan_match_handler(hwid->id, &devid); + if (handler) { + if (!handler->attach) { + device->pnp.type.platform_id = 0; + continue; + } + device->handler = handler; + ret = handler->attach(device, devid); + if (ret > 0) + break; + + device->handler = NULL; + if (ret < 0) + break; + } + } + + return ret; +} + +static void acpi_bus_attach(struct acpi_device *device) +{ + struct acpi_device *child; + acpi_handle ejd; + int ret; + + if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) + register_dock_dependent_device(device, ejd); + + acpi_bus_get_status(device); + /* Skip devices that are not present. */ + if (!acpi_device_is_present(device)) { + device->flags.visited = false; + device->flags.power_manageable = 0; + return; + } + if (device->handler) + goto ok; + + if (!device->flags.initialized) { + device->flags.power_manageable = + device->power.states[ACPI_STATE_D0].flags.valid; + if (acpi_bus_init_power(device)) + device->flags.power_manageable = 0; + + device->flags.initialized = true; + } + device->flags.visited = false; + ret = acpi_scan_attach_handler(device); + if (ret < 0) + return; + + device->flags.match_driver = true; + if (!ret) { + ret = device_attach(&device->dev); + if (ret < 0) + return; + + if (!ret && device->pnp.type.platform_id) + acpi_default_enumeration(device); + } + device->flags.visited = true; + + ok: + list_for_each_entry(child, &device->children, node) + acpi_bus_attach(child); + + if (device->handler && device->handler->hotplug.notify_online) + device->handler->hotplug.notify_online(device); +} + +void acpi_walk_dep_device_list(acpi_handle handle) +{ + struct acpi_dep_data *dep, *tmp; + struct acpi_device *adev; + + mutex_lock(&acpi_dep_list_lock); + list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) { + if (dep->master == handle) { + acpi_bus_get_device(dep->slave, &adev); + if (!adev) + continue; + + adev->dep_unmet--; + if (!adev->dep_unmet) + acpi_bus_attach(adev); + list_del(&dep->node); + kfree(dep); + } + } + mutex_unlock(&acpi_dep_list_lock); +} +EXPORT_SYMBOL_GPL(acpi_walk_dep_device_list); + +/** + * acpi_bus_scan - Add ACPI device node objects in a given namespace scope. + * @handle: Root of the namespace scope to scan. + * + * Scan a given ACPI tree (probably recently hot-plugged) and create and add + * found devices. + * + * If no devices were found, -ENODEV is returned, but it does not mean that + * there has been a real error. There just have been no suitable ACPI objects + * in the table trunk from which the kernel could create a device and add an + * appropriate driver. + * + * Must be called under acpi_scan_lock. + */ +int acpi_bus_scan(acpi_handle handle) +{ + void *device = NULL; + + if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_check_add, NULL, NULL, &device); + + if (device) { + acpi_bus_attach(device); + return 0; + } + return -ENODEV; +} +EXPORT_SYMBOL(acpi_bus_scan); + +/** + * acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects. + * @adev: Root of the ACPI namespace scope to walk. + * + * Must be called under acpi_scan_lock. + */ +void acpi_bus_trim(struct acpi_device *adev) +{ + struct acpi_scan_handler *handler = adev->handler; + struct acpi_device *child; + + list_for_each_entry_reverse(child, &adev->children, node) + acpi_bus_trim(child); + + adev->flags.match_driver = false; + if (handler) { + if (handler->detach) + handler->detach(adev); + + adev->handler = NULL; + } else { + device_release_driver(&adev->dev); + } + /* + * Most likely, the device is going away, so put it into D3cold before + * that. + */ + acpi_device_set_power(adev, ACPI_STATE_D3_COLD); + adev->flags.initialized = false; + adev->flags.visited = false; +} +EXPORT_SYMBOL_GPL(acpi_bus_trim); + +static int acpi_bus_scan_fixed(void) +{ + int result = 0; + + /* + * Enumerate all fixed-feature devices. + */ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { + struct acpi_device *device = NULL; + + result = acpi_add_single_object(&device, NULL, + ACPI_BUS_TYPE_POWER_BUTTON, + ACPI_STA_DEFAULT); + if (result) + return result; + + device->flags.match_driver = true; + result = device_attach(&device->dev); + if (result < 0) + return result; + + device_init_wakeup(&device->dev, true); + } + + if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) { + struct acpi_device *device = NULL; + + result = acpi_add_single_object(&device, NULL, + ACPI_BUS_TYPE_SLEEP_BUTTON, + ACPI_STA_DEFAULT); + if (result) + return result; + + device->flags.match_driver = true; + result = device_attach(&device->dev); + } + + return result < 0 ? result : 0; +} + +int __init acpi_scan_init(void) +{ + int result; + + result = bus_register(&acpi_bus_type); + if (result) { + /* We don't want to quit even if we failed to add suspend/resume */ + printk(KERN_ERR PREFIX "Could not register bus type\n"); + } + + acpi_pci_root_init(); + acpi_pci_link_init(); + acpi_processor_init(); + acpi_lpss_init(); + acpi_apd_init(); + acpi_cmos_rtc_init(); + acpi_container_init(); + acpi_memory_hotplug_init(); + acpi_pnp_init(); + acpi_int340x_thermal_init(); + + acpi_scan_add_handler(&generic_device_handler); + + mutex_lock(&acpi_scan_lock); + /* + * Enumerate devices in the ACPI namespace. + */ + result = acpi_bus_scan(ACPI_ROOT_OBJECT); + if (result) + goto out; + + result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root); + if (result) + goto out; + + /* Fixed feature devices do not exist on HW-reduced platform */ + if (!acpi_gbl_reduced_hardware) { + result = acpi_bus_scan_fixed(); + if (result) { + acpi_detach_data(acpi_root->handle, + acpi_scan_drop_device); + acpi_device_del(acpi_root); + put_device(&acpi_root->dev); + goto out; + } + } + + acpi_update_all_gpes(); + + out: + mutex_unlock(&acpi_scan_lock); + return result; +} diff --git a/kernel/drivers/acpi/sleep.c b/kernel/drivers/acpi/sleep.c new file mode 100644 index 000000000..2f0d4db40 --- /dev/null +++ b/kernel/drivers/acpi/sleep.c @@ -0,0 +1,859 @@ +/* + * sleep.c - ACPI sleep support. + * + * Copyright (c) 2005 Alexey Starikovskiy + * Copyright (c) 2004 David Shaohua Li + * Copyright (c) 2000-2003 Patrick Mochel + * Copyright (c) 2003 Open Source Development Lab + * + * This file is released under the GPLv2. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" +#include "sleep.h" + +static u8 sleep_states[ACPI_S_STATE_COUNT]; + +static void acpi_sleep_tts_switch(u32 acpi_state) +{ + acpi_status status; + + status = acpi_execute_simple_method(NULL, "\\_TTS", acpi_state); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + /* + * OS can't evaluate the _TTS object correctly. Some warning + * message will be printed. But it won't break anything. + */ + printk(KERN_NOTICE "Failure in evaluating _TTS object\n"); + } +} + +static int tts_notify_reboot(struct notifier_block *this, + unsigned long code, void *x) +{ + acpi_sleep_tts_switch(ACPI_STATE_S5); + return NOTIFY_DONE; +} + +static struct notifier_block tts_notifier = { + .notifier_call = tts_notify_reboot, + .next = NULL, + .priority = 0, +}; + +static int acpi_sleep_prepare(u32 acpi_state) +{ +#ifdef CONFIG_ACPI_SLEEP + /* do we have a wakeup address for S2 and S3? */ + if (acpi_state == ACPI_STATE_S3) { + if (!acpi_wakeup_address) + return -EFAULT; + acpi_set_firmware_waking_vector(acpi_wakeup_address); + + } + ACPI_FLUSH_CPU_CACHE(); +#endif + printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n", + acpi_state); + acpi_enable_wakeup_devices(acpi_state); + acpi_enter_sleep_state_prep(acpi_state); + return 0; +} + +static bool acpi_sleep_state_supported(u8 sleep_state) +{ + acpi_status status; + u8 type_a, type_b; + + status = acpi_get_sleep_type_data(sleep_state, &type_a, &type_b); + return ACPI_SUCCESS(status) && (!acpi_gbl_reduced_hardware + || (acpi_gbl_FADT.sleep_control.address + && acpi_gbl_FADT.sleep_status.address)); +} + +#ifdef CONFIG_ACPI_SLEEP +static u32 acpi_target_sleep_state = ACPI_STATE_S0; + +u32 acpi_target_system_state(void) +{ + return acpi_target_sleep_state; +} +EXPORT_SYMBOL_GPL(acpi_target_system_state); + +static bool pwr_btn_event_pending; + +/* + * The ACPI specification wants us to save NVS memory regions during hibernation + * and to restore them during the subsequent resume. Windows does that also for + * suspend to RAM. However, it is known that this mechanism does not work on + * all machines, so we allow the user to disable it with the help of the + * 'acpi_sleep=nonvs' kernel command line option. + */ +static bool nvs_nosave; + +void __init acpi_nvs_nosave(void) +{ + nvs_nosave = true; +} + +/* + * The ACPI specification wants us to save NVS memory regions during hibernation + * but says nothing about saving NVS during S3. Not all versions of Windows + * save NVS on S3 suspend either, and it is clear that not all systems need + * NVS to be saved at S3 time. To improve suspend/resume time, allow the + * user to disable saving NVS on S3 if their system does not require it, but + * continue to save/restore NVS for S4 as specified. + */ +static bool nvs_nosave_s3; + +void __init acpi_nvs_nosave_s3(void) +{ + nvs_nosave_s3 = true; +} + +/* + * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the + * user to request that behavior by using the 'acpi_old_suspend_ordering' + * kernel command line option that causes the following variable to be set. + */ +static bool old_suspend_ordering; + +void __init acpi_old_suspend_ordering(void) +{ + old_suspend_ordering = true; +} + +static int __init init_old_suspend_ordering(const struct dmi_system_id *d) +{ + acpi_old_suspend_ordering(); + return 0; +} + +static int __init init_nvs_nosave(const struct dmi_system_id *d) +{ + acpi_nvs_nosave(); + return 0; +} + +static struct dmi_system_id acpisleep_dmi_table[] __initdata = { + { + .callback = init_old_suspend_ordering, + .ident = "Abit KN9 (nForce4 variant)", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "http://www.abit.com.tw/"), + DMI_MATCH(DMI_BOARD_NAME, "KN9 Series(NF-CK804)"), + }, + }, + { + .callback = init_old_suspend_ordering, + .ident = "HP xw4600 Workstation", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP xw4600 Workstation"), + }, + }, + { + .callback = init_old_suspend_ordering, + .ident = "Asus Pundit P1-AH2 (M2N8L motherboard)", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "M2N8L"), + }, + }, + { + .callback = init_old_suspend_ordering, + .ident = "Panasonic CF51-2L", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, + "Matsushita Electric Industrial Co.,Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "CF51-2L"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-FW41E_H", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW41E_H"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-FW21E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21E"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-FW21M", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21M"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VPCEB17FX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB17FX"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-SR11M", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR11M"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Everex StepNote Series", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Everex Systems, Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Everex StepNote Series"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VPCEB1Z1E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1Z1E"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-NW130D", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NW130D"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VPCCW29FX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPCCW29FX"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Averatec AV1020-ED2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AVERATEC"), + DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"), + }, + }, + { + .callback = init_old_suspend_ordering, + .ident = "Asus A8N-SLI DELUXE", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"), + }, + }, + { + .callback = init_old_suspend_ordering, + .ident = "Asus A8N-SLI Premium", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-SR26GN_P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR26GN_P"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VPCEB1S1E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB1S1E"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-FW520F", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW520F"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Asus K54C", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "K54C"), + }, + }, + { + .callback = init_nvs_nosave, + .ident = "Asus K54HR", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "K54HR"), + }, + }, + {}, +}; + +static void __init acpi_sleep_dmi_check(void) +{ + int year; + + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year >= 2012) + acpi_nvs_nosave_s3(); + + dmi_check_system(acpisleep_dmi_table); +} + +/** + * acpi_pm_freeze - Disable the GPEs and suspend EC transactions. + */ +static int acpi_pm_freeze(void) +{ + acpi_disable_all_gpes(); + acpi_os_wait_events_complete(); + acpi_ec_block_transactions(); + return 0; +} + +/** + * acpi_pre_suspend - Enable wakeup devices, "freeze" EC and save NVS. + */ +static int acpi_pm_pre_suspend(void) +{ + acpi_pm_freeze(); + return suspend_nvs_save(); +} + +/** + * __acpi_pm_prepare - Prepare the platform to enter the target state. + * + * If necessary, set the firmware waking vector and do arch-specific + * nastiness to get the wakeup code to the waking vector. + */ +static int __acpi_pm_prepare(void) +{ + int error = acpi_sleep_prepare(acpi_target_sleep_state); + if (error) + acpi_target_sleep_state = ACPI_STATE_S0; + + return error; +} + +/** + * acpi_pm_prepare - Prepare the platform to enter the target sleep + * state and disable the GPEs. + */ +static int acpi_pm_prepare(void) +{ + int error = __acpi_pm_prepare(); + if (!error) + error = acpi_pm_pre_suspend(); + + return error; +} + +static int find_powerf_dev(struct device *dev, void *data) +{ + struct acpi_device *device = to_acpi_device(dev); + const char *hid = acpi_device_hid(device); + + return !strcmp(hid, ACPI_BUTTON_HID_POWERF); +} + +/** + * acpi_pm_finish - Instruct the platform to leave a sleep state. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ +static void acpi_pm_finish(void) +{ + struct device *pwr_btn_dev; + u32 acpi_state = acpi_target_sleep_state; + + acpi_ec_unblock_transactions(); + suspend_nvs_free(); + + if (acpi_state == ACPI_STATE_S0) + return; + + printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n", + acpi_state); + acpi_disable_wakeup_devices(acpi_state); + acpi_leave_sleep_state(acpi_state); + + /* reset firmware waking vector */ + acpi_set_firmware_waking_vector((acpi_physical_address) 0); + + acpi_target_sleep_state = ACPI_STATE_S0; + + acpi_resume_power_resources(); + + /* If we were woken with the fixed power button, provide a small + * hint to userspace in the form of a wakeup event on the fixed power + * button device (if it can be found). + * + * We delay the event generation til now, as the PM layer requires + * timekeeping to be running before we generate events. */ + if (!pwr_btn_event_pending) + return; + + pwr_btn_event_pending = false; + pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL, + find_powerf_dev); + if (pwr_btn_dev) { + pm_wakeup_event(pwr_btn_dev, 0); + put_device(pwr_btn_dev); + } +} + +/** + * acpi_pm_start - Start system PM transition. + */ +static void acpi_pm_start(u32 acpi_state) +{ + acpi_target_sleep_state = acpi_state; + acpi_sleep_tts_switch(acpi_target_sleep_state); + acpi_scan_lock_acquire(); +} + +/** + * acpi_pm_end - Finish up system PM transition. + */ +static void acpi_pm_end(void) +{ + acpi_scan_lock_release(); + /* + * This is necessary in case acpi_pm_finish() is not called during a + * failing transition to a sleep state. + */ + acpi_target_sleep_state = ACPI_STATE_S0; + acpi_sleep_tts_switch(acpi_target_sleep_state); +} +#else /* !CONFIG_ACPI_SLEEP */ +#define acpi_target_sleep_state ACPI_STATE_S0 +static inline void acpi_sleep_dmi_check(void) {} +#endif /* CONFIG_ACPI_SLEEP */ + +#ifdef CONFIG_SUSPEND +static u32 acpi_suspend_states[] = { + [PM_SUSPEND_ON] = ACPI_STATE_S0, + [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, + [PM_SUSPEND_MEM] = ACPI_STATE_S3, + [PM_SUSPEND_MAX] = ACPI_STATE_S5 +}; + +/** + * acpi_suspend_begin - Set the target system sleep state to the state + * associated with given @pm_state, if supported. + */ +static int acpi_suspend_begin(suspend_state_t pm_state) +{ + u32 acpi_state = acpi_suspend_states[pm_state]; + int error; + + error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc(); + if (error) + return error; + + if (!sleep_states[acpi_state]) { + pr_err("ACPI does not support sleep state S%u\n", acpi_state); + return -ENOSYS; + } + + acpi_pm_start(acpi_state); + return 0; +} + +/** + * acpi_suspend_enter - Actually enter a sleep state. + * @pm_state: ignored + * + * Flush caches and go to sleep. For STR we have to call arch-specific + * assembly, which in turn call acpi_enter_sleep_state(). + * It's unfortunate, but it works. Please fix if you're feeling frisky. + */ +static int acpi_suspend_enter(suspend_state_t pm_state) +{ + acpi_status status = AE_OK; + u32 acpi_state = acpi_target_sleep_state; + int error; + + ACPI_FLUSH_CPU_CACHE(); + + trace_suspend_resume(TPS("acpi_suspend"), acpi_state, true); + switch (acpi_state) { + case ACPI_STATE_S1: + barrier(); + status = acpi_enter_sleep_state(acpi_state); + break; + + case ACPI_STATE_S3: + if (!acpi_suspend_lowlevel) + return -ENOSYS; + error = acpi_suspend_lowlevel(); + if (error) + return error; + pr_info(PREFIX "Low-level resume complete\n"); + break; + } + trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false); + + /* This violates the spec but is required for bug compatibility. */ + acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); + + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(acpi_state); + + /* ACPI 3.0 specs (P62) says that it's the responsibility + * of the OSPM to clear the status bit [ implying that the + * POWER_BUTTON event should not reach userspace ] + * + * However, we do generate a small hint for userspace in the form of + * a wakeup event. We flag this condition for now and generate the + * event later, as we're currently too early in resume to be able to + * generate wakeup events. + */ + if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) { + acpi_event_status pwr_btn_status = ACPI_EVENT_FLAG_DISABLED; + + acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status); + + if (pwr_btn_status & ACPI_EVENT_FLAG_SET) { + acpi_clear_event(ACPI_EVENT_POWER_BUTTON); + /* Flag for later */ + pwr_btn_event_pending = true; + } + } + + /* + * Disable and clear GPE status before interrupt is enabled. Some GPEs + * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. + * acpi_leave_sleep_state will reenable specific GPEs later + */ + acpi_disable_all_gpes(); + /* Allow EC transactions to happen. */ + acpi_ec_unblock_transactions_early(); + + suspend_nvs_restore(); + + return ACPI_SUCCESS(status) ? 0 : -EFAULT; +} + +static int acpi_suspend_state_valid(suspend_state_t pm_state) +{ + u32 acpi_state; + + switch (pm_state) { + case PM_SUSPEND_ON: + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + acpi_state = acpi_suspend_states[pm_state]; + + return sleep_states[acpi_state]; + default: + return 0; + } +} + +static const struct platform_suspend_ops acpi_suspend_ops = { + .valid = acpi_suspend_state_valid, + .begin = acpi_suspend_begin, + .prepare_late = acpi_pm_prepare, + .enter = acpi_suspend_enter, + .wake = acpi_pm_finish, + .end = acpi_pm_end, +}; + +/** + * acpi_suspend_begin_old - Set the target system sleep state to the + * state associated with given @pm_state, if supported, and + * execute the _PTS control method. This function is used if the + * pre-ACPI 2.0 suspend ordering has been requested. + */ +static int acpi_suspend_begin_old(suspend_state_t pm_state) +{ + int error = acpi_suspend_begin(pm_state); + if (!error) + error = __acpi_pm_prepare(); + + return error; +} + +/* + * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has + * been requested. + */ +static const struct platform_suspend_ops acpi_suspend_ops_old = { + .valid = acpi_suspend_state_valid, + .begin = acpi_suspend_begin_old, + .prepare_late = acpi_pm_pre_suspend, + .enter = acpi_suspend_enter, + .wake = acpi_pm_finish, + .end = acpi_pm_end, + .recover = acpi_pm_finish, +}; + +static int acpi_freeze_begin(void) +{ + acpi_scan_lock_acquire(); + return 0; +} + +static int acpi_freeze_prepare(void) +{ + acpi_enable_wakeup_devices(ACPI_STATE_S0); + acpi_enable_all_wakeup_gpes(); + acpi_os_wait_events_complete(); + enable_irq_wake(acpi_gbl_FADT.sci_interrupt); + return 0; +} + +static void acpi_freeze_restore(void) +{ + acpi_disable_wakeup_devices(ACPI_STATE_S0); + disable_irq_wake(acpi_gbl_FADT.sci_interrupt); + acpi_enable_all_runtime_gpes(); +} + +static void acpi_freeze_end(void) +{ + acpi_scan_lock_release(); +} + +static const struct platform_freeze_ops acpi_freeze_ops = { + .begin = acpi_freeze_begin, + .prepare = acpi_freeze_prepare, + .restore = acpi_freeze_restore, + .end = acpi_freeze_end, +}; + +static void acpi_sleep_suspend_setup(void) +{ + int i; + + for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) + if (acpi_sleep_state_supported(i)) + sleep_states[i] = 1; + + suspend_set_ops(old_suspend_ordering ? + &acpi_suspend_ops_old : &acpi_suspend_ops); + freeze_set_ops(&acpi_freeze_ops); +} + +#else /* !CONFIG_SUSPEND */ +static inline void acpi_sleep_suspend_setup(void) {} +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION +static unsigned long s4_hardware_signature; +static struct acpi_table_facs *facs; +static bool nosigcheck; + +void __init acpi_no_s4_hw_signature(void) +{ + nosigcheck = true; +} + +static int acpi_hibernation_begin(void) +{ + int error; + + error = nvs_nosave ? 0 : suspend_nvs_alloc(); + if (!error) + acpi_pm_start(ACPI_STATE_S4); + + return error; +} + +static int acpi_hibernation_enter(void) +{ + acpi_status status = AE_OK; + + ACPI_FLUSH_CPU_CACHE(); + + /* This shouldn't return. If it returns, we have a problem */ + status = acpi_enter_sleep_state(ACPI_STATE_S4); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); + + return ACPI_SUCCESS(status) ? 0 : -EFAULT; +} + +static void acpi_hibernation_leave(void) +{ + /* + * If ACPI is not enabled by the BIOS and the boot kernel, we need to + * enable it here. + */ + acpi_enable(); + /* Reprogram control registers */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); + /* Check the hardware signature */ + if (facs && s4_hardware_signature != facs->hardware_signature) + pr_crit("ACPI: Hardware changed while hibernated, success doubtful!\n"); + /* Restore the NVS memory area */ + suspend_nvs_restore(); + /* Allow EC transactions to happen. */ + acpi_ec_unblock_transactions_early(); +} + +static void acpi_pm_thaw(void) +{ + acpi_ec_unblock_transactions(); + acpi_enable_all_runtime_gpes(); +} + +static const struct platform_hibernation_ops acpi_hibernation_ops = { + .begin = acpi_hibernation_begin, + .end = acpi_pm_end, + .pre_snapshot = acpi_pm_prepare, + .finish = acpi_pm_finish, + .prepare = acpi_pm_prepare, + .enter = acpi_hibernation_enter, + .leave = acpi_hibernation_leave, + .pre_restore = acpi_pm_freeze, + .restore_cleanup = acpi_pm_thaw, +}; + +/** + * acpi_hibernation_begin_old - Set the target system sleep state to + * ACPI_STATE_S4 and execute the _PTS control method. This + * function is used if the pre-ACPI 2.0 suspend ordering has been + * requested. + */ +static int acpi_hibernation_begin_old(void) +{ + int error; + /* + * The _TTS object should always be evaluated before the _PTS object. + * When the old_suspended_ordering is true, the _PTS object is + * evaluated in the acpi_sleep_prepare. + */ + acpi_sleep_tts_switch(ACPI_STATE_S4); + + error = acpi_sleep_prepare(ACPI_STATE_S4); + + if (!error) { + if (!nvs_nosave) + error = suspend_nvs_alloc(); + if (!error) { + acpi_target_sleep_state = ACPI_STATE_S4; + acpi_scan_lock_acquire(); + } + } + return error; +} + +/* + * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has + * been requested. + */ +static const struct platform_hibernation_ops acpi_hibernation_ops_old = { + .begin = acpi_hibernation_begin_old, + .end = acpi_pm_end, + .pre_snapshot = acpi_pm_pre_suspend, + .prepare = acpi_pm_freeze, + .finish = acpi_pm_finish, + .enter = acpi_hibernation_enter, + .leave = acpi_hibernation_leave, + .pre_restore = acpi_pm_freeze, + .restore_cleanup = acpi_pm_thaw, + .recover = acpi_pm_finish, +}; + +static void acpi_sleep_hibernate_setup(void) +{ + if (!acpi_sleep_state_supported(ACPI_STATE_S4)) + return; + + hibernation_set_ops(old_suspend_ordering ? + &acpi_hibernation_ops_old : &acpi_hibernation_ops); + sleep_states[ACPI_STATE_S4] = 1; + if (nosigcheck) + return; + + acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs); + if (facs) + s4_hardware_signature = facs->hardware_signature; +} +#else /* !CONFIG_HIBERNATION */ +static inline void acpi_sleep_hibernate_setup(void) {} +#endif /* !CONFIG_HIBERNATION */ + +static void acpi_power_off_prepare(void) +{ + /* Prepare to power off the system */ + acpi_sleep_prepare(ACPI_STATE_S5); + acpi_disable_all_gpes(); + acpi_os_wait_events_complete(); +} + +static void acpi_power_off(void) +{ + /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */ + printk(KERN_DEBUG "%s called\n", __func__); + local_irq_disable(); + acpi_enter_sleep_state(ACPI_STATE_S5); +} + +int __init acpi_sleep_init(void) +{ + char supported[ACPI_S_STATE_COUNT * 3 + 1]; + char *pos = supported; + int i; + + acpi_sleep_dmi_check(); + + sleep_states[ACPI_STATE_S0] = 1; + + acpi_sleep_suspend_setup(); + acpi_sleep_hibernate_setup(); + + if (acpi_sleep_state_supported(ACPI_STATE_S5)) { + sleep_states[ACPI_STATE_S5] = 1; + pm_power_off_prepare = acpi_power_off_prepare; + pm_power_off = acpi_power_off; + } + + supported[0] = 0; + for (i = 0; i < ACPI_S_STATE_COUNT; i++) { + if (sleep_states[i]) + pos += sprintf(pos, " S%d", i); + } + pr_info(PREFIX "(supports%s)\n", supported); + + /* + * Register the tts_notifier to reboot notifier list so that the _TTS + * object can also be evaluated when the system enters S5. + */ + register_reboot_notifier(&tts_notifier); + return 0; +} diff --git a/kernel/drivers/acpi/sleep.h b/kernel/drivers/acpi/sleep.h new file mode 100644 index 000000000..c797ffa56 --- /dev/null +++ b/kernel/drivers/acpi/sleep.h @@ -0,0 +1,8 @@ + +extern void acpi_enable_wakeup_devices(u8 sleep_state); +extern void acpi_disable_wakeup_devices(u8 sleep_state); + +extern struct list_head acpi_wakeup_device_list; +extern struct mutex acpi_device_lock; + +extern void acpi_resume_power_resources(void); diff --git a/kernel/drivers/acpi/sysfs.c b/kernel/drivers/acpi/sysfs.c new file mode 100644 index 000000000..0876d77b3 --- /dev/null +++ b/kernel/drivers/acpi/sysfs.c @@ -0,0 +1,824 @@ +/* + * sysfs.c - ACPI sysfs interface to userspace. + */ + +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME("sysfs"); + +#ifdef CONFIG_ACPI_DEBUG +/* + * ACPI debug sysfs I/F, including: + * /sys/modules/acpi/parameters/debug_layer + * /sys/modules/acpi/parameters/debug_level + * /sys/modules/acpi/parameters/trace_method_name + * /sys/modules/acpi/parameters/trace_state + * /sys/modules/acpi/parameters/trace_debug_layer + * /sys/modules/acpi/parameters/trace_debug_level + */ + +struct acpi_dlayer { + const char *name; + unsigned long value; +}; +struct acpi_dlevel { + const char *name; + unsigned long value; +}; +#define ACPI_DEBUG_INIT(v) { .name = #v, .value = v } + +static const struct acpi_dlayer acpi_debug_layers[] = { + ACPI_DEBUG_INIT(ACPI_UTILITIES), + ACPI_DEBUG_INIT(ACPI_HARDWARE), + ACPI_DEBUG_INIT(ACPI_EVENTS), + ACPI_DEBUG_INIT(ACPI_TABLES), + ACPI_DEBUG_INIT(ACPI_NAMESPACE), + ACPI_DEBUG_INIT(ACPI_PARSER), + ACPI_DEBUG_INIT(ACPI_DISPATCHER), + ACPI_DEBUG_INIT(ACPI_EXECUTER), + ACPI_DEBUG_INIT(ACPI_RESOURCES), + ACPI_DEBUG_INIT(ACPI_CA_DEBUGGER), + ACPI_DEBUG_INIT(ACPI_OS_SERVICES), + ACPI_DEBUG_INIT(ACPI_CA_DISASSEMBLER), + ACPI_DEBUG_INIT(ACPI_COMPILER), + ACPI_DEBUG_INIT(ACPI_TOOLS), + + ACPI_DEBUG_INIT(ACPI_BUS_COMPONENT), + ACPI_DEBUG_INIT(ACPI_AC_COMPONENT), + ACPI_DEBUG_INIT(ACPI_BATTERY_COMPONENT), + ACPI_DEBUG_INIT(ACPI_BUTTON_COMPONENT), + ACPI_DEBUG_INIT(ACPI_SBS_COMPONENT), + ACPI_DEBUG_INIT(ACPI_FAN_COMPONENT), + ACPI_DEBUG_INIT(ACPI_PCI_COMPONENT), + ACPI_DEBUG_INIT(ACPI_POWER_COMPONENT), + ACPI_DEBUG_INIT(ACPI_CONTAINER_COMPONENT), + ACPI_DEBUG_INIT(ACPI_SYSTEM_COMPONENT), + ACPI_DEBUG_INIT(ACPI_THERMAL_COMPONENT), + ACPI_DEBUG_INIT(ACPI_MEMORY_DEVICE_COMPONENT), + ACPI_DEBUG_INIT(ACPI_VIDEO_COMPONENT), + ACPI_DEBUG_INIT(ACPI_PROCESSOR_COMPONENT), +}; + +static const struct acpi_dlevel acpi_debug_levels[] = { + ACPI_DEBUG_INIT(ACPI_LV_INIT), + ACPI_DEBUG_INIT(ACPI_LV_DEBUG_OBJECT), + ACPI_DEBUG_INIT(ACPI_LV_INFO), + + ACPI_DEBUG_INIT(ACPI_LV_INIT_NAMES), + ACPI_DEBUG_INIT(ACPI_LV_PARSE), + ACPI_DEBUG_INIT(ACPI_LV_LOAD), + ACPI_DEBUG_INIT(ACPI_LV_DISPATCH), + ACPI_DEBUG_INIT(ACPI_LV_EXEC), + ACPI_DEBUG_INIT(ACPI_LV_NAMES), + ACPI_DEBUG_INIT(ACPI_LV_OPREGION), + ACPI_DEBUG_INIT(ACPI_LV_BFIELD), + ACPI_DEBUG_INIT(ACPI_LV_TABLES), + ACPI_DEBUG_INIT(ACPI_LV_VALUES), + ACPI_DEBUG_INIT(ACPI_LV_OBJECTS), + ACPI_DEBUG_INIT(ACPI_LV_RESOURCES), + ACPI_DEBUG_INIT(ACPI_LV_USER_REQUESTS), + ACPI_DEBUG_INIT(ACPI_LV_PACKAGE), + + ACPI_DEBUG_INIT(ACPI_LV_ALLOCATIONS), + ACPI_DEBUG_INIT(ACPI_LV_FUNCTIONS), + ACPI_DEBUG_INIT(ACPI_LV_OPTIMIZATIONS), + + ACPI_DEBUG_INIT(ACPI_LV_MUTEX), + ACPI_DEBUG_INIT(ACPI_LV_THREADS), + ACPI_DEBUG_INIT(ACPI_LV_IO), + ACPI_DEBUG_INIT(ACPI_LV_INTERRUPTS), + + ACPI_DEBUG_INIT(ACPI_LV_AML_DISASSEMBLE), + ACPI_DEBUG_INIT(ACPI_LV_VERBOSE_INFO), + ACPI_DEBUG_INIT(ACPI_LV_FULL_TABLES), + ACPI_DEBUG_INIT(ACPI_LV_EVENTS), +}; + +static int param_get_debug_layer(char *buffer, const struct kernel_param *kp) +{ + int result = 0; + int i; + + result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); + + for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) { + result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n", + acpi_debug_layers[i].name, + acpi_debug_layers[i].value, + (acpi_dbg_layer & acpi_debug_layers[i].value) + ? '*' : ' '); + } + result += + sprintf(buffer + result, "%-25s\t0x%08X [%c]\n", "ACPI_ALL_DRIVERS", + ACPI_ALL_DRIVERS, + (acpi_dbg_layer & ACPI_ALL_DRIVERS) == + ACPI_ALL_DRIVERS ? '*' : (acpi_dbg_layer & ACPI_ALL_DRIVERS) + == 0 ? ' ' : '-'); + result += + sprintf(buffer + result, + "--\ndebug_layer = 0x%08X ( * = enabled)\n", + acpi_dbg_layer); + + return result; +} + +static int param_get_debug_level(char *buffer, const struct kernel_param *kp) +{ + int result = 0; + int i; + + result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); + + for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) { + result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n", + acpi_debug_levels[i].name, + acpi_debug_levels[i].value, + (acpi_dbg_level & acpi_debug_levels[i].value) + ? '*' : ' '); + } + result += + sprintf(buffer + result, "--\ndebug_level = 0x%08X (* = enabled)\n", + acpi_dbg_level); + + return result; +} + +static const struct kernel_param_ops param_ops_debug_layer = { + .set = param_set_uint, + .get = param_get_debug_layer, +}; + +static const struct kernel_param_ops param_ops_debug_level = { + .set = param_set_uint, + .get = param_get_debug_level, +}; + +module_param_cb(debug_layer, ¶m_ops_debug_layer, &acpi_dbg_layer, 0644); +module_param_cb(debug_level, ¶m_ops_debug_level, &acpi_dbg_level, 0644); + +static char trace_method_name[6]; +module_param_string(trace_method_name, trace_method_name, 6, 0644); +static unsigned int trace_debug_layer; +module_param(trace_debug_layer, uint, 0644); +static unsigned int trace_debug_level; +module_param(trace_debug_level, uint, 0644); + +static int param_set_trace_state(const char *val, struct kernel_param *kp) +{ + int result = 0; + + if (!strncmp(val, "enable", sizeof("enable") - 1)) { + result = acpi_debug_trace(trace_method_name, trace_debug_level, + trace_debug_layer, 0); + if (result) + result = -EBUSY; + goto exit; + } + + if (!strncmp(val, "disable", sizeof("disable") - 1)) { + int name = 0; + result = acpi_debug_trace((char *)&name, trace_debug_level, + trace_debug_layer, 0); + if (result) + result = -EBUSY; + goto exit; + } + + if (!strncmp(val, "1", 1)) { + result = acpi_debug_trace(trace_method_name, trace_debug_level, + trace_debug_layer, 1); + if (result) + result = -EBUSY; + goto exit; + } + + result = -EINVAL; +exit: + return result; +} + +static int param_get_trace_state(char *buffer, struct kernel_param *kp) +{ + if (!acpi_gbl_trace_method_name) + return sprintf(buffer, "disable"); + else { + if (acpi_gbl_trace_flags & 1) + return sprintf(buffer, "1"); + else + return sprintf(buffer, "enable"); + } + return 0; +} + +module_param_call(trace_state, param_set_trace_state, param_get_trace_state, + NULL, 0644); +#endif /* CONFIG_ACPI_DEBUG */ + + +/* /sys/modules/acpi/parameters/aml_debug_output */ + +module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object, + byte, 0644); +MODULE_PARM_DESC(aml_debug_output, + "To enable/disable the ACPI Debug Object output."); + +/* /sys/module/acpi/parameters/acpica_version */ +static int param_get_acpica_version(char *buffer, struct kernel_param *kp) +{ + int result; + + result = sprintf(buffer, "%x", ACPI_CA_VERSION); + + return result; +} + +module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); + +/* + * ACPI table sysfs I/F: + * /sys/firmware/acpi/tables/ + * /sys/firmware/acpi/tables/dynamic/ + */ + +static LIST_HEAD(acpi_table_attr_list); +static struct kobject *tables_kobj; +static struct kobject *dynamic_tables_kobj; +static struct kobject *hotplug_kobj; + +struct acpi_table_attr { + struct bin_attribute attr; + char name[8]; + int instance; + struct list_head node; +}; + +static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t offset, size_t count) +{ + struct acpi_table_attr *table_attr = + container_of(bin_attr, struct acpi_table_attr, attr); + struct acpi_table_header *table_header = NULL; + acpi_status status; + char name[ACPI_NAME_SIZE]; + + if (strncmp(table_attr->name, "NULL", 4)) + memcpy(name, table_attr->name, ACPI_NAME_SIZE); + else + memcpy(name, "\0\0\0\0", 4); + + status = acpi_get_table(name, table_attr->instance, &table_header); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return memory_read_from_buffer(buf, count, &offset, + table_header, table_header->length); +} + +static void acpi_table_attr_init(struct acpi_table_attr *table_attr, + struct acpi_table_header *table_header) +{ + struct acpi_table_header *header = NULL; + struct acpi_table_attr *attr = NULL; + + sysfs_attr_init(&table_attr->attr.attr); + if (table_header->signature[0] != '\0') + memcpy(table_attr->name, table_header->signature, + ACPI_NAME_SIZE); + else + memcpy(table_attr->name, "NULL", 4); + + list_for_each_entry(attr, &acpi_table_attr_list, node) { + if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE)) + if (table_attr->instance < attr->instance) + table_attr->instance = attr->instance; + } + table_attr->instance++; + + if (table_attr->instance > 1 || (table_attr->instance == 1 && + !acpi_get_table + (table_header->signature, 2, &header))) + sprintf(table_attr->name + ACPI_NAME_SIZE, "%d", + table_attr->instance); + + table_attr->attr.size = table_header->length; + table_attr->attr.read = acpi_table_show; + table_attr->attr.attr.name = table_attr->name; + table_attr->attr.attr.mode = 0400; + + return; +} + +static acpi_status +acpi_sysfs_table_handler(u32 event, void *table, void *context) +{ + struct acpi_table_attr *table_attr; + + switch (event) { + case ACPI_TABLE_EVENT_LOAD: + table_attr = + kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); + if (!table_attr) + return AE_NO_MEMORY; + + acpi_table_attr_init(table_attr, table); + if (sysfs_create_bin_file(dynamic_tables_kobj, + &table_attr->attr)) { + kfree(table_attr); + return AE_ERROR; + } else + list_add_tail(&table_attr->node, &acpi_table_attr_list); + break; + case ACPI_TABLE_EVENT_UNLOAD: + /* + * we do not need to do anything right now + * because the table is not deleted from the + * global table list when unloading it. + */ + break; + default: + return AE_BAD_PARAMETER; + } + return AE_OK; +} + +static int acpi_tables_sysfs_init(void) +{ + struct acpi_table_attr *table_attr; + struct acpi_table_header *table_header = NULL; + int table_index; + acpi_status status; + int ret; + + tables_kobj = kobject_create_and_add("tables", acpi_kobj); + if (!tables_kobj) + goto err; + + dynamic_tables_kobj = kobject_create_and_add("dynamic", tables_kobj); + if (!dynamic_tables_kobj) + goto err_dynamic_tables; + + for (table_index = 0;; table_index++) { + status = acpi_get_table_by_index(table_index, &table_header); + + if (status == AE_BAD_PARAMETER) + break; + + if (ACPI_FAILURE(status)) + continue; + + table_attr = NULL; + table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL); + if (!table_attr) + return -ENOMEM; + + acpi_table_attr_init(table_attr, table_header); + ret = sysfs_create_bin_file(tables_kobj, &table_attr->attr); + if (ret) { + kfree(table_attr); + return ret; + } + list_add_tail(&table_attr->node, &acpi_table_attr_list); + } + + kobject_uevent(tables_kobj, KOBJ_ADD); + kobject_uevent(dynamic_tables_kobj, KOBJ_ADD); + status = acpi_install_table_handler(acpi_sysfs_table_handler, NULL); + + return ACPI_FAILURE(status) ? -EINVAL : 0; +err_dynamic_tables: + kobject_put(tables_kobj); +err: + return -ENOMEM; +} + +/* + * Detailed ACPI IRQ counters: + * /sys/firmware/acpi/interrupts/ + */ + +u32 acpi_irq_handled; +u32 acpi_irq_not_handled; + +#define COUNT_GPE 0 +#define COUNT_SCI 1 /* acpi_irq_handled */ +#define COUNT_SCI_NOT 2 /* acpi_irq_not_handled */ +#define COUNT_ERROR 3 /* other */ +#define NUM_COUNTERS_EXTRA 4 + +struct event_counter { + u32 count; + u32 flags; +}; + +static struct event_counter *all_counters; +static u32 num_gpes; +static u32 num_counters; +static struct attribute **all_attrs; +static u32 acpi_gpe_count; + +static struct attribute_group interrupt_stats_attr_group = { + .name = "interrupts", +}; + +static struct kobj_attribute *counter_attrs; + +static void delete_gpe_attr_array(void) +{ + struct event_counter *tmp = all_counters; + + all_counters = NULL; + kfree(tmp); + + if (counter_attrs) { + int i; + + for (i = 0; i < num_gpes; i++) + kfree(counter_attrs[i].attr.name); + + kfree(counter_attrs); + } + kfree(all_attrs); + + return; +} + +static void gpe_count(u32 gpe_number) +{ + acpi_gpe_count++; + + if (!all_counters) + return; + + if (gpe_number < num_gpes) + all_counters[gpe_number].count++; + else + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + + COUNT_ERROR].count++; + + return; +} + +static void fixed_event_count(u32 event_number) +{ + if (!all_counters) + return; + + if (event_number < ACPI_NUM_FIXED_EVENTS) + all_counters[num_gpes + event_number].count++; + else + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + + COUNT_ERROR].count++; + + return; +} + +static void acpi_global_event_handler(u32 event_type, acpi_handle device, + u32 event_number, void *context) +{ + if (event_type == ACPI_EVENT_TYPE_GPE) + gpe_count(event_number); + + if (event_type == ACPI_EVENT_TYPE_FIXED) + fixed_event_count(event_number); +} + +static int get_status(u32 index, acpi_event_status *status, + acpi_handle *handle) +{ + int result = 0; + + if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) + goto end; + + if (index < num_gpes) { + result = acpi_get_gpe_device(index, handle); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, + "Invalid GPE 0x%x", index)); + goto end; + } + result = acpi_get_gpe_status(*handle, index, status); + } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) + result = acpi_get_event_status(index - num_gpes, status); + +end: + return result; +} + +static ssize_t counter_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int index = attr - counter_attrs; + int size; + acpi_handle handle; + acpi_event_status status; + int result = 0; + + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count = + acpi_irq_handled; + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT].count = + acpi_irq_not_handled; + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = + acpi_gpe_count; + size = sprintf(buf, "%8u", all_counters[index].count); + + /* "gpe_all" or "sci" */ + if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) + goto end; + + result = get_status(index, &status, &handle); + if (result) + goto end; + + if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) + size += sprintf(buf + size, " invalid"); + else if (status & ACPI_EVENT_FLAG_ENABLED) + size += sprintf(buf + size, " enabled"); + else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED) + size += sprintf(buf + size, " wake_enabled"); + else + size += sprintf(buf + size, " disabled"); + +end: + size += sprintf(buf + size, "\n"); + return result ? result : size; +} + +/* + * counter_set() sets the specified counter. + * setting the total "sci" file to any value clears all counters. + * enable/disable/clear a gpe/fixed event in user space. + */ +static ssize_t counter_set(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t size) +{ + int index = attr - counter_attrs; + acpi_event_status status; + acpi_handle handle; + int result = 0; + unsigned long tmp; + + if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { + int i; + for (i = 0; i < num_counters; ++i) + all_counters[i].count = 0; + acpi_gpe_count = 0; + acpi_irq_handled = 0; + acpi_irq_not_handled = 0; + goto end; + } + + /* show the event status for both GPEs and Fixed Events */ + result = get_status(index, &status, &handle); + if (result) + goto end; + + if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) { + printk(KERN_WARNING PREFIX + "Can not change Invalid GPE/Fixed Event status\n"); + return -EINVAL; + } + + if (index < num_gpes) { + if (!strcmp(buf, "disable\n") && + (status & ACPI_EVENT_FLAG_ENABLED)) + result = acpi_disable_gpe(handle, index); + else if (!strcmp(buf, "enable\n") && + !(status & ACPI_EVENT_FLAG_ENABLED)) + result = acpi_enable_gpe(handle, index); + else if (!strcmp(buf, "clear\n") && + (status & ACPI_EVENT_FLAG_SET)) + result = acpi_clear_gpe(handle, index); + else if (!kstrtoul(buf, 0, &tmp)) + all_counters[index].count = tmp; + else + result = -EINVAL; + } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { + int event = index - num_gpes; + if (!strcmp(buf, "disable\n") && + (status & ACPI_EVENT_FLAG_ENABLED)) + result = acpi_disable_event(event, ACPI_NOT_ISR); + else if (!strcmp(buf, "enable\n") && + !(status & ACPI_EVENT_FLAG_ENABLED)) + result = acpi_enable_event(event, ACPI_NOT_ISR); + else if (!strcmp(buf, "clear\n") && + (status & ACPI_EVENT_FLAG_SET)) + result = acpi_clear_event(event); + else if (!kstrtoul(buf, 0, &tmp)) + all_counters[index].count = tmp; + else + result = -EINVAL; + } else + all_counters[index].count = strtoul(buf, NULL, 0); + + if (ACPI_FAILURE(result)) + result = -EINVAL; +end: + return result ? result : size; +} + +void acpi_irq_stats_init(void) +{ + acpi_status status; + int i; + + if (all_counters) + return; + + num_gpes = acpi_current_gpe_count; + num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; + + all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), + GFP_KERNEL); + if (all_attrs == NULL) + return; + + all_counters = kzalloc(sizeof(struct event_counter) * (num_counters), + GFP_KERNEL); + if (all_counters == NULL) + goto fail; + + status = acpi_install_global_event_handler(acpi_global_event_handler, NULL); + if (ACPI_FAILURE(status)) + goto fail; + + counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), + GFP_KERNEL); + if (counter_attrs == NULL) + goto fail; + + for (i = 0; i < num_counters; ++i) { + char buffer[12]; + char *name; + + if (i < num_gpes) + sprintf(buffer, "gpe%02X", i); + else if (i == num_gpes + ACPI_EVENT_PMTIMER) + sprintf(buffer, "ff_pmtimer"); + else if (i == num_gpes + ACPI_EVENT_GLOBAL) + sprintf(buffer, "ff_gbl_lock"); + else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) + sprintf(buffer, "ff_pwr_btn"); + else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) + sprintf(buffer, "ff_slp_btn"); + else if (i == num_gpes + ACPI_EVENT_RTC) + sprintf(buffer, "ff_rt_clk"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) + sprintf(buffer, "gpe_all"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) + sprintf(buffer, "sci"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT) + sprintf(buffer, "sci_not"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) + sprintf(buffer, "error"); + else + sprintf(buffer, "bug%02X", i); + + name = kstrdup(buffer, GFP_KERNEL); + if (name == NULL) + goto fail; + + sysfs_attr_init(&counter_attrs[i].attr); + counter_attrs[i].attr.name = name; + counter_attrs[i].attr.mode = 0644; + counter_attrs[i].show = counter_show; + counter_attrs[i].store = counter_set; + + all_attrs[i] = &counter_attrs[i].attr; + } + + interrupt_stats_attr_group.attrs = all_attrs; + if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group)) + return; + +fail: + delete_gpe_attr_array(); + return; +} + +static void __exit interrupt_stats_exit(void) +{ + sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); + + delete_gpe_attr_array(); + + return; +} + +static ssize_t +acpi_show_profile(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", acpi_gbl_FADT.preferred_profile); +} + +static const struct device_attribute pm_profile_attr = + __ATTR(pm_profile, S_IRUGO, acpi_show_profile, NULL); + +static ssize_t hotplug_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + + return sprintf(buf, "%d\n", hotplug->enabled); +} + +static ssize_t hotplug_enabled_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); + unsigned int val; + + if (kstrtouint(buf, 10, &val) || val > 1) + return -EINVAL; + + acpi_scan_hotplug_enabled(hotplug, val); + return size; +} + +static struct kobj_attribute hotplug_enabled_attr = + __ATTR(enabled, S_IRUGO | S_IWUSR, hotplug_enabled_show, + hotplug_enabled_store); + +static struct attribute *hotplug_profile_attrs[] = { + &hotplug_enabled_attr.attr, + NULL +}; + +static struct kobj_type acpi_hotplug_profile_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = hotplug_profile_attrs, +}; + +void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, + const char *name) +{ + int error; + + if (!hotplug_kobj) + goto err_out; + + error = kobject_init_and_add(&hotplug->kobj, + &acpi_hotplug_profile_ktype, hotplug_kobj, "%s", name); + if (error) + goto err_out; + + kobject_uevent(&hotplug->kobj, KOBJ_ADD); + return; + + err_out: + pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); +} + +static ssize_t force_remove_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", !!acpi_force_hot_remove); +} + +static ssize_t force_remove_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + lock_device_hotplug(); + acpi_force_hot_remove = val; + unlock_device_hotplug(); + return size; +} + +static const struct kobj_attribute force_remove_attr = + __ATTR(force_remove, S_IRUGO | S_IWUSR, force_remove_show, + force_remove_store); + +int __init acpi_sysfs_init(void) +{ + int result; + + result = acpi_tables_sysfs_init(); + if (result) + return result; + + hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); + result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr); + if (result) + return result; + + result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); + return result; +} diff --git a/kernel/drivers/acpi/tables.c b/kernel/drivers/acpi/tables.c new file mode 100644 index 000000000..2e19189da --- /dev/null +++ b/kernel/drivers/acpi/tables.c @@ -0,0 +1,432 @@ +/* + * acpi_tables.c - ACPI Boot-Time Table Parsing + * + * Copyright (C) 2001 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +/* Uncomment next line to get verbose printout */ +/* #define DEBUG */ +#define pr_fmt(fmt) "ACPI: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ACPI_MAX_TABLES 128 + +static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" }; +static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" }; + +static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata; + +static int acpi_apic_instance __initdata; + +/* + * Disable table checksum verification for the early stage due to the size + * limitation of the current x86 early mapping implementation. + */ +static bool acpi_verify_table_checksum __initdata = false; + +void acpi_table_print_madt_entry(struct acpi_subtable_header *header) +{ + if (!header) + return; + + switch (header->type) { + + case ACPI_MADT_TYPE_LOCAL_APIC: + { + struct acpi_madt_local_apic *p = + (struct acpi_madt_local_apic *)header; + pr_debug("LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n", + p->processor_id, p->id, + (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + } + break; + + case ACPI_MADT_TYPE_LOCAL_X2APIC: + { + struct acpi_madt_local_x2apic *p = + (struct acpi_madt_local_x2apic *)header; + pr_debug("X2APIC (apic_id[0x%02x] uid[0x%02x] %s)\n", + p->local_apic_id, p->uid, + (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + } + break; + + case ACPI_MADT_TYPE_IO_APIC: + { + struct acpi_madt_io_apic *p = + (struct acpi_madt_io_apic *)header; + pr_debug("IOAPIC (id[0x%02x] address[0x%08x] gsi_base[%d])\n", + p->id, p->address, p->global_irq_base); + } + break; + + case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: + { + struct acpi_madt_interrupt_override *p = + (struct acpi_madt_interrupt_override *)header; + pr_info("INT_SRC_OVR (bus %d bus_irq %d global_irq %d %s %s)\n", + p->bus, p->source_irq, p->global_irq, + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2]); + if (p->inti_flags & + ~(ACPI_MADT_POLARITY_MASK | ACPI_MADT_TRIGGER_MASK)) + pr_info("INT_SRC_OVR unexpected reserved flags: 0x%x\n", + p->inti_flags & + ~(ACPI_MADT_POLARITY_MASK | ACPI_MADT_TRIGGER_MASK)); + } + break; + + case ACPI_MADT_TYPE_NMI_SOURCE: + { + struct acpi_madt_nmi_source *p = + (struct acpi_madt_nmi_source *)header; + pr_info("NMI_SRC (%s %s global_irq %d)\n", + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], + p->global_irq); + } + break; + + case ACPI_MADT_TYPE_LOCAL_APIC_NMI: + { + struct acpi_madt_local_apic_nmi *p = + (struct acpi_madt_local_apic_nmi *)header; + pr_info("LAPIC_NMI (acpi_id[0x%02x] %s %s lint[0x%x])\n", + p->processor_id, + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK ], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], + p->lint); + } + break; + + case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI: + { + u16 polarity, trigger; + struct acpi_madt_local_x2apic_nmi *p = + (struct acpi_madt_local_x2apic_nmi *)header; + + polarity = p->inti_flags & ACPI_MADT_POLARITY_MASK; + trigger = (p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2; + + pr_info("X2APIC_NMI (uid[0x%02x] %s %s lint[0x%x])\n", + p->uid, + mps_inti_flags_polarity[polarity], + mps_inti_flags_trigger[trigger], + p->lint); + } + break; + + case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE: + { + struct acpi_madt_local_apic_override *p = + (struct acpi_madt_local_apic_override *)header; + pr_info("LAPIC_ADDR_OVR (address[%p])\n", + (void *)(unsigned long)p->address); + } + break; + + case ACPI_MADT_TYPE_IO_SAPIC: + { + struct acpi_madt_io_sapic *p = + (struct acpi_madt_io_sapic *)header; + pr_debug("IOSAPIC (id[0x%x] address[%p] gsi_base[%d])\n", + p->id, (void *)(unsigned long)p->address, + p->global_irq_base); + } + break; + + case ACPI_MADT_TYPE_LOCAL_SAPIC: + { + struct acpi_madt_local_sapic *p = + (struct acpi_madt_local_sapic *)header; + pr_debug("LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n", + p->processor_id, p->id, p->eid, + (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + } + break; + + case ACPI_MADT_TYPE_INTERRUPT_SOURCE: + { + struct acpi_madt_interrupt_source *p = + (struct acpi_madt_interrupt_source *)header; + pr_info("PLAT_INT_SRC (%s %s type[0x%x] id[0x%04x] eid[0x%x] iosapic_vector[0x%x] global_irq[0x%x]\n", + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], + p->type, p->id, p->eid, p->io_sapic_vector, + p->global_irq); + } + break; + + case ACPI_MADT_TYPE_GENERIC_INTERRUPT: + { + struct acpi_madt_generic_interrupt *p = + (struct acpi_madt_generic_interrupt *)header; + pr_debug("GICC (acpi_id[0x%04x] address[%llx] MPIDR[0x%llx] %s)\n", + p->uid, p->base_address, + p->arm_mpidr, + (p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + + } + break; + + case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR: + { + struct acpi_madt_generic_distributor *p = + (struct acpi_madt_generic_distributor *)header; + pr_debug("GIC Distributor (gic_id[0x%04x] address[%llx] gsi_base[%d])\n", + p->gic_id, p->base_address, + p->global_irq_base); + } + break; + + default: + pr_warn("Found unsupported MADT entry (type = 0x%x)\n", + header->type); + break; + } +} + +int __init +acpi_parse_entries(char *id, unsigned long table_size, + acpi_tbl_entry_handler handler, + struct acpi_table_header *table_header, + int entry_id, unsigned int max_entries) +{ + struct acpi_subtable_header *entry; + int count = 0; + unsigned long table_end; + + if (acpi_disabled) + return -ENODEV; + + if (!id || !handler) + return -EINVAL; + + if (!table_size) + return -EINVAL; + + if (!table_header) { + pr_warn("%4.4s not present\n", id); + return -ENODEV; + } + + table_end = (unsigned long)table_header + table_header->length; + + /* Parse all entries looking for a match. */ + + entry = (struct acpi_subtable_header *) + ((unsigned long)table_header + table_size); + + while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < + table_end) { + if (entry->type == entry_id + && (!max_entries || count < max_entries)) { + if (handler(entry, table_end)) + return -EINVAL; + + count++; + } + + /* + * If entry->length is 0, break from this loop to avoid + * infinite loop. + */ + if (entry->length == 0) { + pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id); + return -EINVAL; + } + + entry = (struct acpi_subtable_header *) + ((unsigned long)entry + entry->length); + } + + if (max_entries && count > max_entries) { + pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", + id, entry_id, count - max_entries, count); + } + + return count; +} + +int __init +acpi_table_parse_entries(char *id, + unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries) +{ + struct acpi_table_header *table_header = NULL; + acpi_size tbl_size; + int count; + u32 instance = 0; + + if (acpi_disabled) + return -ENODEV; + + if (!id || !handler) + return -EINVAL; + + if (!strncmp(id, ACPI_SIG_MADT, 4)) + instance = acpi_apic_instance; + + acpi_get_table_with_size(id, instance, &table_header, &tbl_size); + if (!table_header) { + pr_warn("%4.4s not present\n", id); + return -ENODEV; + } + + count = acpi_parse_entries(id, table_size, handler, table_header, + entry_id, max_entries); + + early_acpi_os_unmap_memory((char *)table_header, tbl_size); + return count; +} + +int __init +acpi_table_parse_madt(enum acpi_madt_type id, + acpi_tbl_entry_handler handler, unsigned int max_entries) +{ + return acpi_table_parse_entries(ACPI_SIG_MADT, + sizeof(struct acpi_table_madt), id, + handler, max_entries); +} + +/** + * acpi_table_parse - find table with @id, run @handler on it + * @id: table id to find + * @handler: handler to run + * + * Scan the ACPI System Descriptor Table (STD) for a table matching @id, + * run @handler on it. + * + * Return 0 if table found, -errno if not. + */ +int __init acpi_table_parse(char *id, acpi_tbl_table_handler handler) +{ + struct acpi_table_header *table = NULL; + acpi_size tbl_size; + + if (acpi_disabled) + return -ENODEV; + + if (!id || !handler) + return -EINVAL; + + if (strncmp(id, ACPI_SIG_MADT, 4) == 0) + acpi_get_table_with_size(id, acpi_apic_instance, &table, &tbl_size); + else + acpi_get_table_with_size(id, 0, &table, &tbl_size); + + if (table) { + handler(table); + early_acpi_os_unmap_memory(table, tbl_size); + return 0; + } else + return -ENODEV; +} + +/* + * The BIOS is supposed to supply a single APIC/MADT, + * but some report two. Provide a knob to use either. + * (don't you wish instance 0 and 1 were not the same?) + */ +static void __init check_multiple_madt(void) +{ + struct acpi_table_header *table = NULL; + acpi_size tbl_size; + + acpi_get_table_with_size(ACPI_SIG_MADT, 2, &table, &tbl_size); + if (table) { + pr_warn("BIOS bug: multiple APIC/MADT found, using %d\n", + acpi_apic_instance); + pr_warn("If \"acpi_apic_instance=%d\" works better, " + "notify linux-acpi@vger.kernel.org\n", + acpi_apic_instance ? 0 : 2); + early_acpi_os_unmap_memory(table, tbl_size); + + } else + acpi_apic_instance = 0; + + return; +} + +/* + * acpi_table_init() + * + * find RSDP, find and checksum SDT/XSDT. + * checksum all tables, print SDT/XSDT + * + * result: sdt_entry[] is initialized + */ + +int __init acpi_table_init(void) +{ + acpi_status status; + + if (acpi_verify_table_checksum) { + pr_info("Early table checksum verification enabled\n"); + acpi_gbl_verify_table_checksum = TRUE; + } else { + pr_info("Early table checksum verification disabled\n"); + acpi_gbl_verify_table_checksum = FALSE; + } + + status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0); + if (ACPI_FAILURE(status)) + return -EINVAL; + + check_multiple_madt(); + return 0; +} + +static int __init acpi_parse_apic_instance(char *str) +{ + if (!str) + return -EINVAL; + + if (kstrtoint(str, 0, &acpi_apic_instance)) + return -EINVAL; + + pr_notice("Shall use APIC/MADT table %d\n", acpi_apic_instance); + + return 0; +} + +early_param("acpi_apic_instance", acpi_parse_apic_instance); + +static int __init acpi_force_table_verification_setup(char *s) +{ + acpi_verify_table_checksum = true; + + return 0; +} + +early_param("acpi_force_table_verification", acpi_force_table_verification_setup); diff --git a/kernel/drivers/acpi/thermal.c b/kernel/drivers/acpi/thermal.c new file mode 100644 index 000000000..d24fa1964 --- /dev/null +++ b/kernel/drivers/acpi/thermal.c @@ -0,0 +1,1285 @@ +/* + * acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 41 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This driver fully implements the ACPI thermal policy as described in the + * ACPI 2.0 Specification. + * + * TBD: 1. Implement passive cooling hysteresis. + * 2. Enhance passive cooling (CPU) states/limit interface to support + * concepts of 'multiple limiters', upper/lower limits, etc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +#define ACPI_THERMAL_CLASS "thermal_zone" +#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" +#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80 +#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81 +#define ACPI_THERMAL_NOTIFY_DEVICES 0x82 +#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 +#define ACPI_THERMAL_NOTIFY_HOT 0xF1 +#define ACPI_THERMAL_MODE_ACTIVE 0x00 + +#define ACPI_THERMAL_MAX_ACTIVE 10 +#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 + +#define _COMPONENT ACPI_THERMAL_COMPONENT +ACPI_MODULE_NAME("thermal"); + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI Thermal Zone Driver"); +MODULE_LICENSE("GPL"); + +static int act; +module_param(act, int, 0644); +MODULE_PARM_DESC(act, "Disable or override all lowest active trip points."); + +static int crt; +module_param(crt, int, 0644); +MODULE_PARM_DESC(crt, "Disable or lower all critical trip points."); + +static int tzp; +module_param(tzp, int, 0444); +MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds."); + +static int nocrt; +module_param(nocrt, int, 0); +MODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points."); + +static int off; +module_param(off, int, 0); +MODULE_PARM_DESC(off, "Set to disable ACPI thermal support."); + +static int psv; +module_param(psv, int, 0644); +MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); + +static struct workqueue_struct *acpi_thermal_pm_queue; + +static int acpi_thermal_add(struct acpi_device *device); +static int acpi_thermal_remove(struct acpi_device *device); +static void acpi_thermal_notify(struct acpi_device *device, u32 event); + +static const struct acpi_device_id thermal_device_ids[] = { + {ACPI_THERMAL_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, thermal_device_ids); + +#ifdef CONFIG_PM_SLEEP +static int acpi_thermal_suspend(struct device *dev); +static int acpi_thermal_resume(struct device *dev); +#else +#define acpi_thermal_suspend NULL +#define acpi_thermal_resume NULL +#endif +static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume); + +static struct acpi_driver acpi_thermal_driver = { + .name = "thermal", + .class = ACPI_THERMAL_CLASS, + .ids = thermal_device_ids, + .ops = { + .add = acpi_thermal_add, + .remove = acpi_thermal_remove, + .notify = acpi_thermal_notify, + }, + .drv.pm = &acpi_thermal_pm, +}; + +struct acpi_thermal_state { + u8 critical:1; + u8 hot:1; + u8 passive:1; + u8 active:1; + u8 reserved:4; + int active_index; +}; + +struct acpi_thermal_state_flags { + u8 valid:1; + u8 enabled:1; + u8 reserved:6; +}; + +struct acpi_thermal_critical { + struct acpi_thermal_state_flags flags; + unsigned long temperature; +}; + +struct acpi_thermal_hot { + struct acpi_thermal_state_flags flags; + unsigned long temperature; +}; + +struct acpi_thermal_passive { + struct acpi_thermal_state_flags flags; + unsigned long temperature; + unsigned long tc1; + unsigned long tc2; + unsigned long tsp; + struct acpi_handle_list devices; +}; + +struct acpi_thermal_active { + struct acpi_thermal_state_flags flags; + unsigned long temperature; + struct acpi_handle_list devices; +}; + +struct acpi_thermal_trips { + struct acpi_thermal_critical critical; + struct acpi_thermal_hot hot; + struct acpi_thermal_passive passive; + struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE]; +}; + +struct acpi_thermal_flags { + u8 cooling_mode:1; /* _SCP */ + u8 devices:1; /* _TZD */ + u8 reserved:6; +}; + +struct acpi_thermal { + struct acpi_device * device; + acpi_bus_id name; + unsigned long temperature; + unsigned long last_temperature; + unsigned long polling_frequency; + volatile u8 zombie; + struct acpi_thermal_flags flags; + struct acpi_thermal_state state; + struct acpi_thermal_trips trips; + struct acpi_handle_list devices; + struct thermal_zone_device *thermal_zone; + int tz_enabled; + int kelvin_offset; + struct work_struct thermal_check_work; +}; + +/* -------------------------------------------------------------------------- + Thermal Zone Management + -------------------------------------------------------------------------- */ + +static int acpi_thermal_get_temperature(struct acpi_thermal *tz) +{ + acpi_status status = AE_OK; + unsigned long long tmp; + + if (!tz) + return -EINVAL; + + tz->last_temperature = tz->temperature; + + status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp); + if (ACPI_FAILURE(status)) + return -ENODEV; + + tz->temperature = tmp; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n", + tz->temperature)); + + return 0; +} + +static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz) +{ + acpi_status status = AE_OK; + unsigned long long tmp; + + if (!tz) + return -EINVAL; + + status = acpi_evaluate_integer(tz->device->handle, "_TZP", NULL, &tmp); + if (ACPI_FAILURE(status)) + return -ENODEV; + + tz->polling_frequency = tmp; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n", + tz->polling_frequency)); + + return 0; +} + +static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) +{ + if (!tz) + return -EINVAL; + + if (!acpi_has_method(tz->device->handle, "_SCP")) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n")); + return -ENODEV; + } else if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle, + "_SCP", mode))) { + return -ENODEV; + } + + return 0; +} + +#define ACPI_TRIPS_CRITICAL 0x01 +#define ACPI_TRIPS_HOT 0x02 +#define ACPI_TRIPS_PASSIVE 0x04 +#define ACPI_TRIPS_ACTIVE 0x08 +#define ACPI_TRIPS_DEVICES 0x10 + +#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE) +#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES + +#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \ + ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \ + ACPI_TRIPS_DEVICES) + +/* + * This exception is thrown out in two cases: + * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid + * when re-evaluating the AML code. + * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change. + * We need to re-bind the cooling devices of a thermal zone when this occurs. + */ +#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \ +do { \ + if (flags != ACPI_TRIPS_INIT) \ + ACPI_EXCEPTION((AE_INFO, AE_ERROR, \ + "ACPI thermal trip point %s changed\n" \ + "Please send acpidump to linux-acpi@vger.kernel.org", str)); \ +} while (0) + +static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) +{ + acpi_status status = AE_OK; + unsigned long long tmp; + struct acpi_handle_list devices; + int valid = 0; + int i; + + /* Critical Shutdown */ + if (flag & ACPI_TRIPS_CRITICAL) { + status = acpi_evaluate_integer(tz->device->handle, + "_CRT", NULL, &tmp); + tz->trips.critical.temperature = tmp; + /* + * Treat freezing temperatures as invalid as well; some + * BIOSes return really low values and cause reboots at startup. + * Below zero (Celsius) values clearly aren't right for sure.. + * ... so lets discard those as invalid. + */ + if (ACPI_FAILURE(status)) { + tz->trips.critical.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No critical threshold\n")); + } else if (tmp <= 2732) { + pr_warn(FW_BUG "Invalid critical threshold (%llu)\n", + tmp); + tz->trips.critical.flags.valid = 0; + } else { + tz->trips.critical.flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found critical threshold [%lu]\n", + tz->trips.critical.temperature)); + } + if (tz->trips.critical.flags.valid == 1) { + if (crt == -1) { + tz->trips.critical.flags.valid = 0; + } else if (crt > 0) { + unsigned long crt_k = CELSIUS_TO_KELVIN(crt); + /* + * Allow override critical threshold + */ + if (crt_k > tz->trips.critical.temperature) + pr_warn(PREFIX "Critical threshold %d C\n", + crt); + tz->trips.critical.temperature = crt_k; + } + } + } + + /* Critical Sleep (optional) */ + if (flag & ACPI_TRIPS_HOT) { + status = acpi_evaluate_integer(tz->device->handle, + "_HOT", NULL, &tmp); + if (ACPI_FAILURE(status)) { + tz->trips.hot.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No hot threshold\n")); + } else { + tz->trips.hot.temperature = tmp; + tz->trips.hot.flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found hot threshold [%lu]\n", + tz->trips.hot.temperature)); + } + } + + /* Passive (optional) */ + if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) || + (flag == ACPI_TRIPS_INIT)) { + valid = tz->trips.passive.flags.valid; + if (psv == -1) { + status = AE_SUPPORT; + } else if (psv > 0) { + tmp = CELSIUS_TO_KELVIN(psv); + status = AE_OK; + } else { + status = acpi_evaluate_integer(tz->device->handle, + "_PSV", NULL, &tmp); + } + + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + else { + tz->trips.passive.temperature = tmp; + tz->trips.passive.flags.valid = 1; + if (flag == ACPI_TRIPS_INIT) { + status = acpi_evaluate_integer( + tz->device->handle, "_TC1", + NULL, &tmp); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + else + tz->trips.passive.tc1 = tmp; + status = acpi_evaluate_integer( + tz->device->handle, "_TC2", + NULL, &tmp); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + else + tz->trips.passive.tc2 = tmp; + status = acpi_evaluate_integer( + tz->device->handle, "_TSP", + NULL, &tmp); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + else + tz->trips.passive.tsp = tmp; + } + } + } + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + status = acpi_evaluate_reference(tz->device->handle, "_PSL", + NULL, &devices); + if (ACPI_FAILURE(status)) { + pr_warn(PREFIX "Invalid passive threshold\n"); + tz->trips.passive.flags.valid = 0; + } + else + tz->trips.passive.flags.valid = 1; + + if (memcmp(&tz->trips.passive.devices, &devices, + sizeof(struct acpi_handle_list))) { + memcpy(&tz->trips.passive.devices, &devices, + sizeof(struct acpi_handle_list)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } + } + if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) { + if (valid != tz->trips.passive.flags.valid) + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); + } + + /* Active (optional) */ + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; + valid = tz->trips.active[i].flags.valid; + + if (act == -1) + break; /* disable all active trip points */ + + if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) && + tz->trips.active[i].flags.valid)) { + status = acpi_evaluate_integer(tz->device->handle, + name, NULL, &tmp); + if (ACPI_FAILURE(status)) { + tz->trips.active[i].flags.valid = 0; + if (i == 0) + break; + if (act <= 0) + break; + if (i == 1) + tz->trips.active[0].temperature = + CELSIUS_TO_KELVIN(act); + else + /* + * Don't allow override higher than + * the next higher trip point + */ + tz->trips.active[i - 1].temperature = + (tz->trips.active[i - 2].temperature < + CELSIUS_TO_KELVIN(act) ? + tz->trips.active[i - 2].temperature : + CELSIUS_TO_KELVIN(act)); + break; + } else { + tz->trips.active[i].temperature = tmp; + tz->trips.active[i].flags.valid = 1; + } + } + + name[2] = 'L'; + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + status = acpi_evaluate_reference(tz->device->handle, + name, NULL, &devices); + if (ACPI_FAILURE(status)) { + pr_warn(PREFIX "Invalid active%d threshold\n", + i); + tz->trips.active[i].flags.valid = 0; + } + else + tz->trips.active[i].flags.valid = 1; + + if (memcmp(&tz->trips.active[i].devices, &devices, + sizeof(struct acpi_handle_list))) { + memcpy(&tz->trips.active[i].devices, &devices, + sizeof(struct acpi_handle_list)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } + } + if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES)) + if (valid != tz->trips.active[i].flags.valid) + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); + + if (!tz->trips.active[i].flags.valid) + break; + } + + if ((flag & ACPI_TRIPS_DEVICES) + && acpi_has_method(tz->device->handle, "_TZD")) { + memset(&devices, 0, sizeof(devices)); + status = acpi_evaluate_reference(tz->device->handle, "_TZD", + NULL, &devices); + if (ACPI_SUCCESS(status) + && memcmp(&tz->devices, &devices, sizeof(devices))) { + tz->devices = devices; + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } + } + + return 0; +} + +static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) +{ + int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT); + + if (ret) + return ret; + + valid = tz->trips.critical.flags.valid | + tz->trips.hot.flags.valid | + tz->trips.passive.flags.valid; + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) + valid |= tz->trips.active[i].flags.valid; + + if (!valid) { + pr_warn(FW_BUG "No valid trip found\n"); + return -ENODEV; + } + return 0; +} + +static void acpi_thermal_check(void *data) +{ + struct acpi_thermal *tz = data; + + if (!tz->tz_enabled) + return; + + thermal_zone_device_update(tz->thermal_zone); +} + +/* sys I/F for generic thermal sysfs support */ + +static int thermal_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + struct acpi_thermal *tz = thermal->devdata; + int result; + + if (!tz) + return -EINVAL; + + result = acpi_thermal_get_temperature(tz); + if (result) + return result; + + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature, + tz->kelvin_offset); + return 0; +} + +static int thermal_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + struct acpi_thermal *tz = thermal->devdata; + + if (!tz) + return -EINVAL; + + *mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED : + THERMAL_DEVICE_DISABLED; + + return 0; +} + +static int thermal_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + struct acpi_thermal *tz = thermal->devdata; + int enable; + + if (!tz) + return -EINVAL; + + /* + * enable/disable thermal management from ACPI thermal driver + */ + if (mode == THERMAL_DEVICE_ENABLED) + enable = 1; + else if (mode == THERMAL_DEVICE_DISABLED) { + enable = 0; + pr_warn("thermal zone will be disabled\n"); + } else + return -EINVAL; + + if (enable != tz->tz_enabled) { + tz->tz_enabled = enable; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "%s kernel ACPI thermal control\n", + tz->tz_enabled ? "Enable" : "Disable")); + acpi_thermal_check(tz); + } + return 0; +} + +static int thermal_get_trip_type(struct thermal_zone_device *thermal, + int trip, enum thermal_trip_type *type) +{ + struct acpi_thermal *tz = thermal->devdata; + int i; + + if (!tz || trip < 0) + return -EINVAL; + + if (tz->trips.critical.flags.valid) { + if (!trip) { + *type = THERMAL_TRIP_CRITICAL; + return 0; + } + trip--; + } + + if (tz->trips.hot.flags.valid) { + if (!trip) { + *type = THERMAL_TRIP_HOT; + return 0; + } + trip--; + } + + if (tz->trips.passive.flags.valid) { + if (!trip) { + *type = THERMAL_TRIP_PASSIVE; + return 0; + } + trip--; + } + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && + tz->trips.active[i].flags.valid; i++) { + if (!trip) { + *type = THERMAL_TRIP_ACTIVE; + return 0; + } + trip--; + } + + return -EINVAL; +} + +static int thermal_get_trip_temp(struct thermal_zone_device *thermal, + int trip, unsigned long *temp) +{ + struct acpi_thermal *tz = thermal->devdata; + int i; + + if (!tz || trip < 0) + return -EINVAL; + + if (tz->trips.critical.flags.valid) { + if (!trip) { + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + tz->trips.critical.temperature, + tz->kelvin_offset); + return 0; + } + trip--; + } + + if (tz->trips.hot.flags.valid) { + if (!trip) { + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + tz->trips.hot.temperature, + tz->kelvin_offset); + return 0; + } + trip--; + } + + if (tz->trips.passive.flags.valid) { + if (!trip) { + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + tz->trips.passive.temperature, + tz->kelvin_offset); + return 0; + } + trip--; + } + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && + tz->trips.active[i].flags.valid; i++) { + if (!trip) { + *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + tz->trips.active[i].temperature, + tz->kelvin_offset); + return 0; + } + trip--; + } + + return -EINVAL; +} + +static int thermal_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temperature) { + struct acpi_thermal *tz = thermal->devdata; + + if (tz->trips.critical.flags.valid) { + *temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + tz->trips.critical.temperature, + tz->kelvin_offset); + return 0; + } else + return -EINVAL; +} + +static int thermal_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + struct acpi_thermal *tz = thermal->devdata; + enum thermal_trip_type type; + int i; + + if (thermal_get_trip_type(thermal, trip, &type)) + return -EINVAL; + + if (type == THERMAL_TRIP_ACTIVE) { + unsigned long trip_temp; + unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( + tz->temperature, tz->kelvin_offset); + if (thermal_get_trip_temp(thermal, trip, &trip_temp)) + return -EINVAL; + + if (temp > trip_temp) { + *trend = THERMAL_TREND_RAISING; + return 0; + } else { + /* Fall back on default trend */ + return -EINVAL; + } + } + + /* + * tz->temperature has already been updated by generic thermal layer, + * before this callback being invoked + */ + i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + + (tz->trips.passive.tc2 + * (tz->temperature - tz->trips.passive.temperature)); + + if (i > 0) + *trend = THERMAL_TREND_RAISING; + else if (i < 0) + *trend = THERMAL_TREND_DROPPING; + else + *trend = THERMAL_TREND_STABLE; + return 0; +} + + +static int thermal_notify(struct thermal_zone_device *thermal, int trip, + enum thermal_trip_type trip_type) +{ + u8 type = 0; + struct acpi_thermal *tz = thermal->devdata; + + if (trip_type == THERMAL_TRIP_CRITICAL) + type = ACPI_THERMAL_NOTIFY_CRITICAL; + else if (trip_type == THERMAL_TRIP_HOT) + type = ACPI_THERMAL_NOTIFY_HOT; + else + return 0; + + acpi_bus_generate_netlink_event(tz->device->pnp.device_class, + dev_name(&tz->device->dev), type, 1); + + if (trip_type == THERMAL_TRIP_CRITICAL && nocrt) + return 1; + + return 0; +} + +static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev, + bool bind) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_thermal *tz = thermal->devdata; + struct acpi_device *dev; + acpi_status status; + acpi_handle handle; + int i; + int j; + int trip = -1; + int result = 0; + + if (tz->trips.critical.flags.valid) + trip++; + + if (tz->trips.hot.flags.valid) + trip++; + + if (tz->trips.passive.flags.valid) { + trip++; + for (i = 0; i < tz->trips.passive.devices.count; + i++) { + handle = tz->trips.passive.devices.handles[i]; + status = acpi_bus_get_device(handle, &dev); + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = + thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = + thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; + } + } + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + if (!tz->trips.active[i].flags.valid) + break; + trip++; + for (j = 0; + j < tz->trips.active[i].devices.count; + j++) { + handle = tz->trips.active[i].devices.handles[j]; + status = acpi_bus_get_device(handle, &dev); + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; + } + } + + for (i = 0; i < tz->devices.count; i++) { + handle = tz->devices.handles[i]; + status = acpi_bus_get_device(handle, &dev); + if (ACPI_SUCCESS(status) && (dev == device)) { + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, THERMAL_TRIPS_NONE, + cdev, THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, THERMAL_TRIPS_NONE, + cdev); + if (result) + goto failed; + } + } + +failed: + return result; +} + +static int +acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + return acpi_thermal_cooling_device_cb(thermal, cdev, true); +} + +static int +acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + return acpi_thermal_cooling_device_cb(thermal, cdev, false); +} + +static struct thermal_zone_device_ops acpi_thermal_zone_ops = { + .bind = acpi_thermal_bind_cooling_device, + .unbind = acpi_thermal_unbind_cooling_device, + .get_temp = thermal_get_temp, + .get_mode = thermal_get_mode, + .set_mode = thermal_set_mode, + .get_trip_type = thermal_get_trip_type, + .get_trip_temp = thermal_get_trip_temp, + .get_crit_temp = thermal_get_crit_temp, + .get_trend = thermal_get_trend, + .notify = thermal_notify, +}; + +static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) +{ + int trips = 0; + int result; + acpi_status status; + int i; + + if (tz->trips.critical.flags.valid) + trips++; + + if (tz->trips.hot.flags.valid) + trips++; + + if (tz->trips.passive.flags.valid) + trips++; + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && + tz->trips.active[i].flags.valid; i++, trips++); + + if (tz->trips.passive.flags.valid) + tz->thermal_zone = + thermal_zone_device_register("acpitz", trips, 0, tz, + &acpi_thermal_zone_ops, NULL, + tz->trips.passive.tsp*100, + tz->polling_frequency*100); + else + tz->thermal_zone = + thermal_zone_device_register("acpitz", trips, 0, tz, + &acpi_thermal_zone_ops, NULL, + 0, tz->polling_frequency*100); + if (IS_ERR(tz->thermal_zone)) + return -ENODEV; + + result = sysfs_create_link(&tz->device->dev.kobj, + &tz->thermal_zone->device.kobj, "thermal_zone"); + if (result) + return result; + + result = sysfs_create_link(&tz->thermal_zone->device.kobj, + &tz->device->dev.kobj, "device"); + if (result) + return result; + + status = acpi_bus_attach_private_data(tz->device->handle, + tz->thermal_zone); + if (ACPI_FAILURE(status)) + return -ENODEV; + + tz->tz_enabled = 1; + + dev_info(&tz->device->dev, "registered as thermal_zone%d\n", + tz->thermal_zone->id); + return 0; +} + +static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) +{ + sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); + sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); + thermal_zone_device_unregister(tz->thermal_zone); + tz->thermal_zone = NULL; + acpi_bus_detach_private_data(tz->device->handle); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static void acpi_thermal_notify(struct acpi_device *device, u32 event) +{ + struct acpi_thermal *tz = acpi_driver_data(device); + + + if (!tz) + return; + + switch (event) { + case ACPI_THERMAL_NOTIFY_TEMPERATURE: + acpi_thermal_check(tz); + break; + case ACPI_THERMAL_NOTIFY_THRESHOLDS: + acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); + acpi_thermal_check(tz); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, 0); + break; + case ACPI_THERMAL_NOTIFY_DEVICES: + acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); + acpi_thermal_check(tz); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, 0); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } +} + +/* + * On some platforms, the AML code has dependency about + * the evaluating order of _TMP and _CRT/_HOT/_PSV/_ACx. + * 1. On HP Pavilion G4-1016tx, _TMP must be invoked after + * /_CRT/_HOT/_PSV/_ACx, or else system will be power off. + * 2. On HP Compaq 6715b/6715s, the return value of _PSV is 0 + * if _TMP has never been evaluated. + * + * As this dependency is totally transparent to OS, evaluate + * all of them once, in the order of _CRT/_HOT/_PSV/_ACx, + * _TMP, before they are actually used. + */ +static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz) +{ + acpi_handle handle = tz->device->handle; + unsigned long long value; + int i; + + acpi_evaluate_integer(handle, "_CRT", NULL, &value); + acpi_evaluate_integer(handle, "_HOT", NULL, &value); + acpi_evaluate_integer(handle, "_PSV", NULL, &value); + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; + acpi_status status; + + status = acpi_evaluate_integer(handle, name, NULL, &value); + if (status == AE_NOT_FOUND) + break; + } + acpi_evaluate_integer(handle, "_TMP", NULL, &value); +} + +static int acpi_thermal_get_info(struct acpi_thermal *tz) +{ + int result = 0; + + + if (!tz) + return -EINVAL; + + acpi_thermal_aml_dependency_fix(tz); + + /* Get trip points [_CRT, _PSV, etc.] (required) */ + result = acpi_thermal_get_trip_points(tz); + if (result) + return result; + + /* Get temperature [_TMP] (required) */ + result = acpi_thermal_get_temperature(tz); + if (result) + return result; + + /* Set the cooling mode [_SCP] to active cooling (default) */ + result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE); + if (!result) + tz->flags.cooling_mode = 1; + + /* Get default polling frequency [_TZP] (optional) */ + if (tzp) + tz->polling_frequency = tzp; + else + acpi_thermal_get_polling_frequency(tz); + + return 0; +} + +/* + * The exact offset between Kelvin and degree Celsius is 273.15. However ACPI + * handles temperature values with a single decimal place. As a consequence, + * some implementations use an offset of 273.1 and others use an offset of + * 273.2. Try to find out which one is being used, to present the most + * accurate and visually appealing number. + * + * The heuristic below should work for all ACPI thermal zones which have a + * critical trip point with a value being a multiple of 0.5 degree Celsius. + */ +static void acpi_thermal_guess_offset(struct acpi_thermal *tz) +{ + if (tz->trips.critical.flags.valid && + (tz->trips.critical.temperature % 5) == 1) + tz->kelvin_offset = 2731; + else + tz->kelvin_offset = 2732; +} + +static void acpi_thermal_check_fn(struct work_struct *work) +{ + struct acpi_thermal *tz = container_of(work, struct acpi_thermal, + thermal_check_work); + acpi_thermal_check(tz); +} + +static int acpi_thermal_add(struct acpi_device *device) +{ + int result = 0; + struct acpi_thermal *tz = NULL; + + + if (!device) + return -EINVAL; + + tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL); + if (!tz) + return -ENOMEM; + + tz->device = device; + strcpy(tz->name, device->pnp.bus_id); + strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); + device->driver_data = tz; + + result = acpi_thermal_get_info(tz); + if (result) + goto free_memory; + + acpi_thermal_guess_offset(tz); + + result = acpi_thermal_register_thermal_zone(tz); + if (result) + goto free_memory; + + INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn); + + pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device), + acpi_device_bid(device), KELVIN_TO_CELSIUS(tz->temperature)); + goto end; + +free_memory: + kfree(tz); +end: + return result; +} + +static int acpi_thermal_remove(struct acpi_device *device) +{ + struct acpi_thermal *tz = NULL; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + flush_workqueue(acpi_thermal_pm_queue); + tz = acpi_driver_data(device); + + acpi_thermal_unregister_thermal_zone(tz); + kfree(tz); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int acpi_thermal_suspend(struct device *dev) +{ + /* Make sure the previously queued thermal check work has been done */ + flush_workqueue(acpi_thermal_pm_queue); + return 0; +} + +static int acpi_thermal_resume(struct device *dev) +{ + struct acpi_thermal *tz; + int i, j, power_state, result; + + if (!dev) + return -EINVAL; + + tz = acpi_driver_data(to_acpi_device(dev)); + if (!tz) + return -EINVAL; + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + if (!(&tz->trips.active[i])) + break; + if (!tz->trips.active[i].flags.valid) + break; + tz->trips.active[i].flags.enabled = 1; + for (j = 0; j < tz->trips.active[i].devices.count; j++) { + result = acpi_bus_update_power( + tz->trips.active[i].devices.handles[j], + &power_state); + if (result || (power_state != ACPI_STATE_D0)) { + tz->trips.active[i].flags.enabled = 0; + break; + } + } + tz->state.active |= tz->trips.active[i].flags.enabled; + } + + queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work); + + return AE_OK; +} +#endif + +static int thermal_act(const struct dmi_system_id *d) { + + if (act == 0) { + pr_notice(PREFIX "%s detected: " + "disabling all active thermal trip points\n", d->ident); + act = -1; + } + return 0; +} +static int thermal_nocrt(const struct dmi_system_id *d) { + + pr_notice(PREFIX "%s detected: " + "disabling all critical thermal trip point actions.\n", d->ident); + nocrt = 1; + return 0; +} +static int thermal_tzp(const struct dmi_system_id *d) { + + if (tzp == 0) { + pr_notice(PREFIX "%s detected: " + "enabling thermal zone polling\n", d->ident); + tzp = 300; /* 300 dS = 30 Seconds */ + } + return 0; +} +static int thermal_psv(const struct dmi_system_id *d) { + + if (psv == 0) { + pr_notice(PREFIX "%s detected: " + "disabling all passive thermal trip points\n", d->ident); + psv = -1; + } + return 0; +} + +static struct dmi_system_id thermal_dmi_table[] __initdata = { + /* + * Award BIOS on this AOpen makes thermal control almost worthless. + * http://bugzilla.kernel.org/show_bug.cgi?id=8842 + */ + { + .callback = thermal_act, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_psv, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_tzp, + .ident = "AOpen i915GMm-HFS", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), + DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), + }, + }, + { + .callback = thermal_nocrt, + .ident = "Gigabyte GA-7ZX", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "7ZX"), + }, + }, + {} +}; + +static int __init acpi_thermal_init(void) +{ + int result = 0; + + dmi_check_system(thermal_dmi_table); + + if (off) { + pr_notice(PREFIX "thermal control disabled\n"); + return -ENODEV; + } + + acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm"); + if (!acpi_thermal_pm_queue) + return -ENODEV; + + result = acpi_bus_register_driver(&acpi_thermal_driver); + if (result < 0) { + destroy_workqueue(acpi_thermal_pm_queue); + return -ENODEV; + } + + return 0; +} + +static void __exit acpi_thermal_exit(void) +{ + acpi_bus_unregister_driver(&acpi_thermal_driver); + destroy_workqueue(acpi_thermal_pm_queue); + + return; +} + +module_init(acpi_thermal_init); +module_exit(acpi_thermal_exit); diff --git a/kernel/drivers/acpi/utils.c b/kernel/drivers/acpi/utils.c new file mode 100644 index 000000000..cd49a3982 --- /dev/null +++ b/kernel/drivers/acpi/utils.c @@ -0,0 +1,714 @@ +/* + * acpi_utils.c - ACPI Utility Functions ($Revision: 10 $) + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define _COMPONENT ACPI_BUS_COMPONENT +ACPI_MODULE_NAME("utils"); + +/* -------------------------------------------------------------------------- + Object Evaluation Helpers + -------------------------------------------------------------------------- */ +static void +acpi_util_eval_error(acpi_handle h, acpi_string p, acpi_status s) +{ +#ifdef ACPI_DEBUG_OUTPUT + char prefix[80] = {'\0'}; + struct acpi_buffer buffer = {sizeof(prefix), prefix}; + acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate [%s.%s]: %s\n", + (char *) prefix, p, acpi_format_exception(s))); +#else + return; +#endif +} + +acpi_status +acpi_extract_package(union acpi_object *package, + struct acpi_buffer *format, struct acpi_buffer *buffer) +{ + u32 size_required = 0; + u32 tail_offset = 0; + char *format_string = NULL; + u32 format_count = 0; + u32 i = 0; + u8 *head = NULL; + u8 *tail = NULL; + + + if (!package || (package->type != ACPI_TYPE_PACKAGE) + || (package->package.count < 1)) { + printk(KERN_WARNING PREFIX "Invalid package argument\n"); + return AE_BAD_PARAMETER; + } + + if (!format || !format->pointer || (format->length < 1)) { + printk(KERN_WARNING PREFIX "Invalid format argument\n"); + return AE_BAD_PARAMETER; + } + + if (!buffer) { + printk(KERN_WARNING PREFIX "Invalid buffer argument\n"); + return AE_BAD_PARAMETER; + } + + format_count = (format->length / sizeof(char)) - 1; + if (format_count > package->package.count) { + printk(KERN_WARNING PREFIX "Format specifies more objects [%d]" + " than exist in package [%d].\n", + format_count, package->package.count); + return AE_BAD_DATA; + } + + format_string = format->pointer; + + /* + * Calculate size_required. + */ + for (i = 0; i < format_count; i++) { + + union acpi_object *element = &(package->package.elements[i]); + + switch (element->type) { + + case ACPI_TYPE_INTEGER: + switch (format_string[i]) { + case 'N': + size_required += sizeof(u64); + tail_offset += sizeof(u64); + break; + case 'S': + size_required += + sizeof(char *) + sizeof(u64) + + sizeof(char); + tail_offset += sizeof(char *); + break; + default: + printk(KERN_WARNING PREFIX "Invalid package element" + " [%d]: got number, expecting" + " [%c]\n", + i, format_string[i]); + return AE_BAD_DATA; + break; + } + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + switch (format_string[i]) { + case 'S': + size_required += + sizeof(char *) + + (element->string.length * sizeof(char)) + + sizeof(char); + tail_offset += sizeof(char *); + break; + case 'B': + size_required += + sizeof(u8 *) + element->buffer.length; + tail_offset += sizeof(u8 *); + break; + default: + printk(KERN_WARNING PREFIX "Invalid package element" + " [%d] got string/buffer," + " expecting [%c]\n", + i, format_string[i]); + return AE_BAD_DATA; + break; + } + break; + case ACPI_TYPE_LOCAL_REFERENCE: + switch (format_string[i]) { + case 'R': + size_required += sizeof(void *); + tail_offset += sizeof(void *); + break; + default: + printk(KERN_WARNING PREFIX "Invalid package element" + " [%d] got reference," + " expecting [%c]\n", + i, format_string[i]); + return AE_BAD_DATA; + break; + } + break; + + case ACPI_TYPE_PACKAGE: + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found unsupported element at index=%d\n", + i)); + /* TBD: handle nested packages... */ + return AE_SUPPORT; + break; + } + } + + /* + * Validate output buffer. + */ + if (buffer->length == ACPI_ALLOCATE_BUFFER) { + buffer->pointer = ACPI_ALLOCATE_ZEROED(size_required); + if (!buffer->pointer) + return AE_NO_MEMORY; + buffer->length = size_required; + } else { + if (buffer->length < size_required) { + buffer->length = size_required; + return AE_BUFFER_OVERFLOW; + } else if (buffer->length != size_required || + !buffer->pointer) { + return AE_BAD_PARAMETER; + } + } + + head = buffer->pointer; + tail = buffer->pointer + tail_offset; + + /* + * Extract package data. + */ + for (i = 0; i < format_count; i++) { + + u8 **pointer = NULL; + union acpi_object *element = &(package->package.elements[i]); + + if (!element) { + return AE_BAD_DATA; + } + + switch (element->type) { + + case ACPI_TYPE_INTEGER: + switch (format_string[i]) { + case 'N': + *((u64 *) head) = + element->integer.value; + head += sizeof(u64); + break; + case 'S': + pointer = (u8 **) head; + *pointer = tail; + *((u64 *) tail) = + element->integer.value; + head += sizeof(u64 *); + tail += sizeof(u64); + /* NULL terminate string */ + *tail = (char)0; + tail += sizeof(char); + break; + default: + /* Should never get here */ + break; + } + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + switch (format_string[i]) { + case 'S': + pointer = (u8 **) head; + *pointer = tail; + memcpy(tail, element->string.pointer, + element->string.length); + head += sizeof(char *); + tail += element->string.length * sizeof(char); + /* NULL terminate string */ + *tail = (char)0; + tail += sizeof(char); + break; + case 'B': + pointer = (u8 **) head; + *pointer = tail; + memcpy(tail, element->buffer.pointer, + element->buffer.length); + head += sizeof(u8 *); + tail += element->buffer.length; + break; + default: + /* Should never get here */ + break; + } + break; + case ACPI_TYPE_LOCAL_REFERENCE: + switch (format_string[i]) { + case 'R': + *(void **)head = + (void *)element->reference.handle; + head += sizeof(void *); + break; + default: + /* Should never get here */ + break; + } + break; + case ACPI_TYPE_PACKAGE: + /* TBD: handle nested packages... */ + default: + /* Should never get here */ + break; + } + } + + return AE_OK; +} + +EXPORT_SYMBOL(acpi_extract_package); + +acpi_status +acpi_evaluate_integer(acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *arguments, unsigned long long *data) +{ + acpi_status status = AE_OK; + union acpi_object element; + struct acpi_buffer buffer = { 0, NULL }; + + if (!data) + return AE_BAD_PARAMETER; + + buffer.length = sizeof(union acpi_object); + buffer.pointer = &element; + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) { + acpi_util_eval_error(handle, pathname, status); + return status; + } + + if (element.type != ACPI_TYPE_INTEGER) { + acpi_util_eval_error(handle, pathname, AE_BAD_DATA); + return AE_BAD_DATA; + } + + *data = element.integer.value; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%llu]\n", *data)); + + return AE_OK; +} + +EXPORT_SYMBOL(acpi_evaluate_integer); + +acpi_status +acpi_evaluate_reference(acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *arguments, + struct acpi_handle_list *list) +{ + acpi_status status = AE_OK; + union acpi_object *package = NULL; + union acpi_object *element = NULL; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + u32 i = 0; + + + if (!list) { + return AE_BAD_PARAMETER; + } + + /* Evaluate object. */ + + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) + goto end; + + package = buffer.pointer; + + if ((buffer.length == 0) || !package) { + status = AE_BAD_DATA; + acpi_util_eval_error(handle, pathname, status); + goto end; + } + if (package->type != ACPI_TYPE_PACKAGE) { + status = AE_BAD_DATA; + acpi_util_eval_error(handle, pathname, status); + goto end; + } + if (!package->package.count) { + status = AE_BAD_DATA; + acpi_util_eval_error(handle, pathname, status); + goto end; + } + + if (package->package.count > ACPI_MAX_HANDLES) { + return AE_NO_MEMORY; + } + list->count = package->package.count; + + /* Extract package data. */ + + for (i = 0; i < list->count; i++) { + + element = &(package->package.elements[i]); + + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { + status = AE_BAD_DATA; + acpi_util_eval_error(handle, pathname, status); + break; + } + + if (!element->reference.handle) { + status = AE_NULL_ENTRY; + acpi_util_eval_error(handle, pathname, status); + break; + } + /* Get the acpi_handle. */ + + list->handles[i] = element->reference.handle; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found reference [%p]\n", + list->handles[i])); + } + + end: + if (ACPI_FAILURE(status)) { + list->count = 0; + //kfree(list->handles); + } + + kfree(buffer.pointer); + + return status; +} + +EXPORT_SYMBOL(acpi_evaluate_reference); + +acpi_status +acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *output; + + status = acpi_evaluate_object(handle, "_PLD", NULL, &buffer); + + if (ACPI_FAILURE(status)) + return status; + + output = buffer.pointer; + + if (!output || output->type != ACPI_TYPE_PACKAGE + || !output->package.count + || output->package.elements[0].type != ACPI_TYPE_BUFFER + || output->package.elements[0].buffer.length < ACPI_PLD_REV1_BUFFER_SIZE) { + status = AE_TYPE; + goto out; + } + + status = acpi_decode_pld_buffer( + output->package.elements[0].buffer.pointer, + output->package.elements[0].buffer.length, + pld); + +out: + kfree(buffer.pointer); + return status; +} +EXPORT_SYMBOL(acpi_get_physical_device_location); + +/** + * acpi_evaluate_ost: Evaluate _OST for hotplug operations + * @handle: ACPI device handle + * @source_event: source event code + * @status_code: status code + * @status_buf: optional detailed information (NULL if none) + * + * Evaluate _OST for hotplug operations. All ACPI hotplug handlers + * must call this function when evaluating _OST for hotplug operations. + * When the platform does not support _OST, this function has no effect. + */ +acpi_status +acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code, + struct acpi_buffer *status_buf) +{ + union acpi_object params[3] = { + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_BUFFER,} + }; + struct acpi_object_list arg_list = {3, params}; + + params[0].integer.value = source_event; + params[1].integer.value = status_code; + if (status_buf != NULL) { + params[2].buffer.pointer = status_buf->pointer; + params[2].buffer.length = status_buf->length; + } else { + params[2].buffer.pointer = NULL; + params[2].buffer.length = 0; + } + + return acpi_evaluate_object(handle, "_OST", &arg_list, NULL); +} +EXPORT_SYMBOL(acpi_evaluate_ost); + +/** + * acpi_handle_path: Return the object path of handle + * + * Caller must free the returned buffer + */ +static char *acpi_handle_path(acpi_handle handle) +{ + struct acpi_buffer buffer = { + .length = ACPI_ALLOCATE_BUFFER, + .pointer = NULL + }; + + if (in_interrupt() || + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK) + return NULL; + return buffer.pointer; +} + +/** + * acpi_handle_printk: Print message with ACPI prefix and object path + * + * This function is called through acpi_handle_ macros and prints + * a message with ACPI prefix and object path. This function acquires + * the global namespace mutex to obtain an object path. In interrupt + * context, it shows the object path as . + */ +void +acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + const char *path; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + path = acpi_handle_path(handle); + printk("%sACPI: %s: %pV", level, path ? path : "" , &vaf); + + va_end(args); + kfree(path); +} +EXPORT_SYMBOL(acpi_handle_printk); + +#if defined(CONFIG_DYNAMIC_DEBUG) +/** + * __acpi_handle_debug: pr_debug with ACPI prefix and object path + * + * This function is called through acpi_handle_debug macro and debug + * prints a message with ACPI prefix and object path. This function + * acquires the global namespace mutex to obtain an object path. In + * interrupt context, it shows the object path as . + */ +void +__acpi_handle_debug(struct _ddebug *descriptor, acpi_handle handle, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + const char *path; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + path = acpi_handle_path(handle); + __dynamic_pr_debug(descriptor, "ACPI: %s: %pV", path ? path : "", &vaf); + + va_end(args); + kfree(path); +} +EXPORT_SYMBOL(__acpi_handle_debug); +#endif + +/** + * acpi_has_method: Check whether @handle has a method named @name + * @handle: ACPI device handle + * @name: name of object or method + * + * Check whether @handle has a method named @name. + */ +bool acpi_has_method(acpi_handle handle, char *name) +{ + acpi_handle tmp; + + return ACPI_SUCCESS(acpi_get_handle(handle, name, &tmp)); +} +EXPORT_SYMBOL(acpi_has_method); + +acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, + u64 arg) +{ + union acpi_object obj = { .type = ACPI_TYPE_INTEGER }; + struct acpi_object_list arg_list = { .count = 1, .pointer = &obj, }; + + obj.integer.value = arg; + + return acpi_evaluate_object(handle, method, &arg_list, NULL); +} +EXPORT_SYMBOL(acpi_execute_simple_method); + +/** + * acpi_evaluate_ej0: Evaluate _EJ0 method for hotplug operations + * @handle: ACPI device handle + * + * Evaluate device's _EJ0 method for hotplug operations. + */ +acpi_status acpi_evaluate_ej0(acpi_handle handle) +{ + acpi_status status; + + status = acpi_execute_simple_method(handle, "_EJ0", 1); + if (status == AE_NOT_FOUND) + acpi_handle_warn(handle, "No _EJ0 support for device\n"); + else if (ACPI_FAILURE(status)) + acpi_handle_warn(handle, "Eject failed (0x%x)\n", status); + + return status; +} + +/** + * acpi_evaluate_lck: Evaluate _LCK method to lock/unlock device + * @handle: ACPI device handle + * @lock: lock device if non-zero, otherwise unlock device + * + * Evaluate device's _LCK method if present to lock/unlock device + */ +acpi_status acpi_evaluate_lck(acpi_handle handle, int lock) +{ + acpi_status status; + + status = acpi_execute_simple_method(handle, "_LCK", !!lock); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + if (lock) + acpi_handle_warn(handle, + "Locking device failed (0x%x)\n", status); + else + acpi_handle_warn(handle, + "Unlocking device failed (0x%x)\n", status); + } + + return status; +} + +/** + * acpi_evaluate_dsm - evaluate device's _DSM method + * @handle: ACPI device handle + * @uuid: UUID of requested functions, should be 16 bytes + * @rev: revision number of requested function + * @func: requested function number + * @argv4: the function specific parameter + * + * Evaluate device's _DSM method with specified UUID, revision id and + * function number. Caller needs to free the returned object. + * + * Though ACPI defines the fourth parameter for _DSM should be a package, + * some old BIOSes do expect a buffer or an integer etc. + */ +union acpi_object * +acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, int rev, int func, + union acpi_object *argv4) +{ + acpi_status ret; + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object params[4]; + struct acpi_object_list input = { + .count = 4, + .pointer = params, + }; + + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = 16; + params[0].buffer.pointer = (char *)uuid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = rev; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = func; + if (argv4) { + params[3] = *argv4; + } else { + params[3].type = ACPI_TYPE_PACKAGE; + params[3].package.count = 0; + params[3].package.elements = NULL; + } + + ret = acpi_evaluate_object(handle, "_DSM", &input, &buf); + if (ACPI_SUCCESS(ret)) + return (union acpi_object *)buf.pointer; + + if (ret != AE_NOT_FOUND) + acpi_handle_warn(handle, + "failed to evaluate _DSM (0x%x)\n", ret); + + return NULL; +} +EXPORT_SYMBOL(acpi_evaluate_dsm); + +/** + * acpi_check_dsm - check if _DSM method supports requested functions. + * @handle: ACPI device handle + * @uuid: UUID of requested functions, should be 16 bytes at least + * @rev: revision number of requested functions + * @funcs: bitmap of requested functions + * + * Evaluate device's _DSM method to check whether it supports requested + * functions. Currently only support 64 functions at maximum, should be + * enough for now. + */ +bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs) +{ + int i; + u64 mask = 0; + union acpi_object *obj; + + if (funcs == 0) + return false; + + obj = acpi_evaluate_dsm(handle, uuid, rev, 0, NULL); + if (!obj) + return false; + + /* For compatibility, old BIOSes may return an integer */ + if (obj->type == ACPI_TYPE_INTEGER) + mask = obj->integer.value; + else if (obj->type == ACPI_TYPE_BUFFER) + for (i = 0; i < obj->buffer.length && i < 8; i++) + mask |= (((u8)obj->buffer.pointer[i]) << (i * 8)); + ACPI_FREE(obj); + + /* + * Bit 0 indicates whether there's support for any functions other than + * function 0 for the specified UUID and revision. + */ + if ((mask & 0x1) && (mask & funcs) == funcs) + return true; + + return false; +} +EXPORT_SYMBOL(acpi_check_dsm); diff --git a/kernel/drivers/acpi/video.c b/kernel/drivers/acpi/video.c new file mode 100644 index 000000000..cc79d3fed --- /dev/null +++ b/kernel/drivers/acpi/video.c @@ -0,0 +1,2231 @@ +/* + * video.c - ACPI Video Driver + * + * Copyright (C) 2004 Luming Yu + * Copyright (C) 2004 Bruno Ducrot + * Copyright (C) 2006 Thomas Tuttle + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +#define ACPI_VIDEO_BUS_NAME "Video Bus" +#define ACPI_VIDEO_DEVICE_NAME "Video Device" +#define ACPI_VIDEO_NOTIFY_SWITCH 0x80 +#define ACPI_VIDEO_NOTIFY_PROBE 0x81 +#define ACPI_VIDEO_NOTIFY_CYCLE 0x82 +#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83 +#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84 + +#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85 +#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 +#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 +#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88 +#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89 + +#define MAX_NAME_LEN 20 + +#define _COMPONENT ACPI_VIDEO_COMPONENT +ACPI_MODULE_NAME("video"); + +MODULE_AUTHOR("Bruno Ducrot"); +MODULE_DESCRIPTION("ACPI Video Driver"); +MODULE_LICENSE("GPL"); + +static bool brightness_switch_enabled = 1; +module_param(brightness_switch_enabled, bool, 0644); + +/* + * By default, we don't allow duplicate ACPI video bus devices + * under the same VGA controller + */ +static bool allow_duplicates; +module_param(allow_duplicates, bool, 0644); + +/* + * For Windows 8 systems: used to decide if video module + * should skip registering backlight interface of its own. + */ +enum { + NATIVE_BACKLIGHT_NOT_SET = -1, + NATIVE_BACKLIGHT_OFF, + NATIVE_BACKLIGHT_ON, +}; + +static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET; +module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); +static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET; + +static int register_count; +static struct mutex video_list_lock; +static struct list_head video_bus_head; +static int acpi_video_bus_add(struct acpi_device *device); +static int acpi_video_bus_remove(struct acpi_device *device); +static void acpi_video_bus_notify(struct acpi_device *device, u32 event); + +static const struct acpi_device_id video_device_ids[] = { + {ACPI_VIDEO_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, video_device_ids); + +static struct acpi_driver acpi_video_bus = { + .name = "video", + .class = ACPI_VIDEO_CLASS, + .ids = video_device_ids, + .ops = { + .add = acpi_video_bus_add, + .remove = acpi_video_bus_remove, + .notify = acpi_video_bus_notify, + }, +}; + +struct acpi_video_bus_flags { + u8 multihead:1; /* can switch video heads */ + u8 rom:1; /* can retrieve a video rom */ + u8 post:1; /* can configure the head to */ + u8 reserved:5; +}; + +struct acpi_video_bus_cap { + u8 _DOS:1; /* Enable/Disable output switching */ + u8 _DOD:1; /* Enumerate all devices attached to display adapter */ + u8 _ROM:1; /* Get ROM Data */ + u8 _GPD:1; /* Get POST Device */ + u8 _SPD:1; /* Set POST Device */ + u8 _VPO:1; /* Video POST Options */ + u8 reserved:2; +}; + +struct acpi_video_device_attrib { + u32 display_index:4; /* A zero-based instance of the Display */ + u32 display_port_attachment:4; /* This field differentiates the display type */ + u32 display_type:4; /* Describe the specific type in use */ + u32 vendor_specific:4; /* Chipset Vendor Specific */ + u32 bios_can_detect:1; /* BIOS can detect the device */ + u32 depend_on_vga:1; /* Non-VGA output device whose power is related to + the VGA device. */ + u32 pipe_id:3; /* For VGA multiple-head devices. */ + u32 reserved:10; /* Must be 0 */ + u32 device_id_scheme:1; /* Device ID Scheme */ +}; + +struct acpi_video_enumerated_device { + union { + u32 int_val; + struct acpi_video_device_attrib attrib; + } value; + struct acpi_video_device *bind_info; +}; + +struct acpi_video_bus { + struct acpi_device *device; + bool backlight_registered; + bool backlight_notifier_registered; + u8 dos_setting; + struct acpi_video_enumerated_device *attached_array; + u8 attached_count; + u8 child_count; + struct acpi_video_bus_cap cap; + struct acpi_video_bus_flags flags; + struct list_head video_device_list; + struct mutex device_list_lock; /* protects video_device_list */ + struct list_head entry; + struct input_dev *input; + char phys[32]; /* for input device */ + struct notifier_block pm_nb; + struct notifier_block backlight_nb; +}; + +struct acpi_video_device_flags { + u8 crt:1; + u8 lcd:1; + u8 tvout:1; + u8 dvi:1; + u8 bios:1; + u8 unknown:1; + u8 notify:1; + u8 reserved:1; +}; + +struct acpi_video_device_cap { + u8 _ADR:1; /* Return the unique ID */ + u8 _BCL:1; /* Query list of brightness control levels supported */ + u8 _BCM:1; /* Set the brightness level */ + u8 _BQC:1; /* Get current brightness level */ + u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */ + u8 _DDC:1; /* Return the EDID for this device */ +}; + +struct acpi_video_brightness_flags { + u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */ + u8 _BCL_reversed:1; /* _BCL package is in a reversed order */ + u8 _BQC_use_index:1; /* _BQC returns an index value */ +}; + +struct acpi_video_device_brightness { + int curr; + int count; + int *levels; + struct acpi_video_brightness_flags flags; +}; + +struct acpi_video_device { + unsigned long device_id; + struct acpi_video_device_flags flags; + struct acpi_video_device_cap cap; + struct list_head entry; + struct delayed_work switch_brightness_work; + int switch_brightness_event; + struct acpi_video_bus *video; + struct acpi_device *dev; + struct acpi_video_device_brightness *brightness; + struct backlight_device *backlight; + struct thermal_cooling_device *cooling_dev; +}; + +static const char device_decode[][30] = { + "motherboard VGA device", + "PCI VGA device", + "AGP VGA device", + "UNKNOWN", +}; + +static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data); +static void acpi_video_device_rebind(struct acpi_video_bus *video); +static void acpi_video_device_bind(struct acpi_video_bus *video, + struct acpi_video_device *device); +static int acpi_video_device_enumerate(struct acpi_video_bus *video); +static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, + int level); +static int acpi_video_device_lcd_get_level_current( + struct acpi_video_device *device, + unsigned long long *level, bool raw); +static int acpi_video_get_next_level(struct acpi_video_device *device, + u32 level_current, u32 event); +static void acpi_video_switch_brightness(struct work_struct *work); + +static bool acpi_video_use_native_backlight(void) +{ + if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET) + return use_native_backlight_param; + else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET) + return use_native_backlight_dmi; + return acpi_osi_is_win8(); +} + +bool acpi_video_verify_backlight_support(void) +{ + if (acpi_video_use_native_backlight() && + backlight_device_registered(BACKLIGHT_RAW)) + return false; + return acpi_video_backlight_support(); +} +EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support); + +/* backlight device sysfs support */ +static int acpi_video_get_brightness(struct backlight_device *bd) +{ + unsigned long long cur_level; + int i; + struct acpi_video_device *vd = bl_get_data(bd); + + if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false)) + return -EINVAL; + for (i = 2; i < vd->brightness->count; i++) { + if (vd->brightness->levels[i] == cur_level) + /* + * The first two entries are special - see page 575 + * of the ACPI spec 3.0 + */ + return i - 2; + } + return 0; +} + +static int acpi_video_set_brightness(struct backlight_device *bd) +{ + int request_level = bd->props.brightness + 2; + struct acpi_video_device *vd = bl_get_data(bd); + + cancel_delayed_work(&vd->switch_brightness_work); + return acpi_video_device_lcd_set_level(vd, + vd->brightness->levels[request_level]); +} + +static const struct backlight_ops acpi_backlight_ops = { + .get_brightness = acpi_video_get_brightness, + .update_status = acpi_video_set_brightness, +}; + +/* thermal cooling device callbacks */ +static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned + long *state) +{ + struct acpi_device *device = cooling_dev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + + *state = video->brightness->count - 3; + return 0; +} + +static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned + long *state) +{ + struct acpi_device *device = cooling_dev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + unsigned long long level; + int offset; + + if (acpi_video_device_lcd_get_level_current(video, &level, false)) + return -EINVAL; + for (offset = 2; offset < video->brightness->count; offset++) + if (level == video->brightness->levels[offset]) { + *state = video->brightness->count - offset - 1; + return 0; + } + + return -EINVAL; +} + +static int +video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) +{ + struct acpi_device *device = cooling_dev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + int level; + + if (state >= video->brightness->count - 2) + return -EINVAL; + + state = video->brightness->count - state; + level = video->brightness->levels[state - 1]; + return acpi_video_device_lcd_set_level(video, level); +} + +static const struct thermal_cooling_device_ops video_cooling_ops = { + .get_max_state = video_get_max_state, + .get_cur_state = video_get_cur_state, + .set_cur_state = video_set_cur_state, +}; + +/* + * -------------------------------------------------------------------------- + * Video Management + * -------------------------------------------------------------------------- + */ + +static int +acpi_video_device_lcd_query_levels(struct acpi_video_device *device, + union acpi_object **levels) +{ + int status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + + *levels = NULL; + + status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer); + if (!ACPI_SUCCESS(status)) + return status; + obj = (union acpi_object *)buffer.pointer; + if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_ERR PREFIX "Invalid _BCL data\n"); + status = -EFAULT; + goto err; + } + + *levels = obj; + + return 0; + +err: + kfree(buffer.pointer); + + return status; +} + +static int +acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) +{ + int status; + int state; + + status = acpi_execute_simple_method(device->dev->handle, + "_BCM", level); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating _BCM failed")); + return -EIO; + } + + device->brightness->curr = level; + for (state = 2; state < device->brightness->count; state++) + if (level == device->brightness->levels[state]) { + if (device->backlight) + device->backlight->props.brightness = state - 2; + return 0; + } + + ACPI_ERROR((AE_INFO, "Current brightness invalid")); + return -EINVAL; +} + +/* + * For some buggy _BQC methods, we need to add a constant value to + * the _BQC return value to get the actual current brightness level + */ + +static int bqc_offset_aml_bug_workaround; +static int __init video_set_bqc_offset(const struct dmi_system_id *d) +{ + bqc_offset_aml_bug_workaround = 9; + return 0; +} + +static int __init video_disable_native_backlight(const struct dmi_system_id *d) +{ + use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF; + return 0; +} + +static int __init video_enable_native_backlight(const struct dmi_system_id *d) +{ + use_native_backlight_dmi = NATIVE_BACKLIGHT_ON; + return 0; +} + +static struct dmi_system_id video_dmi_table[] __initdata = { + /* + * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 + */ + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 5720", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 5710Z", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "eMachines E510", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"), + DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 5315", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 7720", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"), + }, + }, + + /* + * These models have a working acpi_video backlight control, and using + * native backlight causes a regression where backlight does not work + * when userspace is not handling brightness key events. Disable + * native_backlight on these to fix this: + * https://bugzilla.kernel.org/show_bug.cgi?id=81691 + */ + { + .callback = video_disable_native_backlight, + .ident = "ThinkPad T420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"), + }, + }, + { + .callback = video_disable_native_backlight, + .ident = "ThinkPad T520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"), + }, + }, + { + .callback = video_disable_native_backlight, + .ident = "ThinkPad X201s", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), + }, + }, + + /* The native backlight controls do not work on some older machines */ + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */ + .callback = video_disable_native_backlight, + .ident = "HP ENVY 15 Notebook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"), + }, + }, + + { + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"), + }, + }, + { + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "3570R/370R/470R/450R/510R/4450RV"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 730U3E/740U3E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), + }, + }, + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "900X3C/900X3D/900X3E/900X4C/900X4D"), + }, + }, + + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */ + .callback = video_disable_native_backlight, + .ident = "Dell XPS15 L521X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), + }, + }, + + /* Non win8 machines which need native backlight nevertheless */ + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ + .callback = video_enable_native_backlight, + .ident = "Lenovo Ideapad Z570", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), + }, + }, + {} +}; + +static unsigned long long +acpi_video_bqc_value_to_level(struct acpi_video_device *device, + unsigned long long bqc_value) +{ + unsigned long long level; + + if (device->brightness->flags._BQC_use_index) { + /* + * _BQC returns an index that doesn't account for + * the first 2 items with special meaning, so we need + * to compensate for that by offsetting ourselves + */ + if (device->brightness->flags._BCL_reversed) + bqc_value = device->brightness->count - 3 - bqc_value; + + level = device->brightness->levels[bqc_value + 2]; + } else { + level = bqc_value; + } + + level += bqc_offset_aml_bug_workaround; + + return level; +} + +static int +acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, + unsigned long long *level, bool raw) +{ + acpi_status status = AE_OK; + int i; + + if (device->cap._BQC || device->cap._BCQ) { + char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; + + status = acpi_evaluate_integer(device->dev->handle, buf, + NULL, level); + if (ACPI_SUCCESS(status)) { + if (raw) { + /* + * Caller has indicated he wants the raw + * value returned by _BQC, so don't furtherly + * mess with the value. + */ + return 0; + } + + *level = acpi_video_bqc_value_to_level(device, *level); + + for (i = 2; i < device->brightness->count; i++) + if (device->brightness->levels[i] == *level) { + device->brightness->curr = *level; + return 0; + } + /* + * BQC returned an invalid level. + * Stop using it. + */ + ACPI_WARNING((AE_INFO, + "%s returned an invalid level", + buf)); + device->cap._BQC = device->cap._BCQ = 0; + } else { + /* + * Fixme: + * should we return an error or ignore this failure? + * dev->brightness->curr is a cached value which stores + * the correct current backlight level in most cases. + * ACPI video backlight still works w/ buggy _BQC. + * http://bugzilla.kernel.org/show_bug.cgi?id=12233 + */ + ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf)); + device->cap._BQC = device->cap._BCQ = 0; + } + } + + *level = device->brightness->curr; + return 0; +} + +static int +acpi_video_device_EDID(struct acpi_video_device *device, + union acpi_object **edid, ssize_t length) +{ + int status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + + + *edid = NULL; + + if (!device) + return -ENODEV; + if (length == 128) + arg0.integer.value = 1; + else if (length == 256) + arg0.integer.value = 2; + else + return -EINVAL; + + status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = buffer.pointer; + + if (obj && obj->type == ACPI_TYPE_BUFFER) + *edid = obj; + else { + printk(KERN_ERR PREFIX "Invalid _DDC data\n"); + status = -EFAULT; + kfree(obj); + } + + return status; +} + +/* bus */ + +/* + * Arg: + * video : video bus device pointer + * bios_flag : + * 0. The system BIOS should NOT automatically switch(toggle) + * the active display output. + * 1. The system BIOS should automatically switch (toggle) the + * active display output. No switch event. + * 2. The _DGS value should be locked. + * 3. The system BIOS should not automatically switch (toggle) the + * active display output, but instead generate the display switch + * event notify code. + * lcd_flag : + * 0. The system BIOS should automatically control the brightness level + * of the LCD when the power changes from AC to DC + * 1. The system BIOS should NOT automatically control the brightness + * level of the LCD when the power changes from AC to DC. + * Return Value: + * -EINVAL wrong arg. + */ + +static int +acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) +{ + acpi_status status; + + if (!video->cap._DOS) + return 0; + + if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) + return -EINVAL; + video->dos_setting = (lcd_flag << 2) | bios_flag; + status = acpi_execute_simple_method(video->device->handle, "_DOS", + (lcd_flag << 2) | bios_flag); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} + +/* + * Simple comparison function used to sort backlight levels. + */ + +static int +acpi_video_cmp_level(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +/* + * Decides if _BQC/_BCQ for this system is usable + * + * We do this by changing the level first and then read out the current + * brightness level, if the value does not match, find out if it is using + * index. If not, clear the _BQC/_BCQ capability. + */ +static int acpi_video_bqc_quirk(struct acpi_video_device *device, + int max_level, int current_level) +{ + struct acpi_video_device_brightness *br = device->brightness; + int result; + unsigned long long level; + int test_level; + + /* don't mess with existing known broken systems */ + if (bqc_offset_aml_bug_workaround) + return 0; + + /* + * Some systems always report current brightness level as maximum + * through _BQC, we need to test another value for them. + */ + test_level = current_level == max_level ? br->levels[3] : max_level; + + result = acpi_video_device_lcd_set_level(device, test_level); + if (result) + return result; + + result = acpi_video_device_lcd_get_level_current(device, &level, true); + if (result) + return result; + + if (level != test_level) { + /* buggy _BQC found, need to find out if it uses index */ + if (level < br->count) { + if (br->flags._BCL_reversed) + level = br->count - 3 - level; + if (br->levels[level + 2] == test_level) + br->flags._BQC_use_index = 1; + } + + if (!br->flags._BQC_use_index) + device->cap._BQC = device->cap._BCQ = 0; + } + + return 0; +} + + +/* + * Arg: + * device : video output device (LCD, CRT, ..) + * + * Return Value: + * Maximum brightness level + * + * Allocate and initialize device->brightness. + */ + +static int +acpi_video_init_brightness(struct acpi_video_device *device) +{ + union acpi_object *obj = NULL; + int i, max_level = 0, count = 0, level_ac_battery = 0; + unsigned long long level, level_old; + union acpi_object *o; + struct acpi_video_device_brightness *br = NULL; + int result = -EINVAL; + u32 value; + + if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " + "LCD brightness level\n")); + goto out; + } + + if (obj->package.count < 2) + goto out; + + br = kzalloc(sizeof(*br), GFP_KERNEL); + if (!br) { + printk(KERN_ERR "can't allocate memory\n"); + result = -ENOMEM; + goto out; + } + + br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels), + GFP_KERNEL); + if (!br->levels) { + result = -ENOMEM; + goto out_free; + } + + for (i = 0; i < obj->package.count; i++) { + o = (union acpi_object *)&obj->package.elements[i]; + if (o->type != ACPI_TYPE_INTEGER) { + printk(KERN_ERR PREFIX "Invalid data\n"); + continue; + } + value = (u32) o->integer.value; + /* Skip duplicate entries */ + if (count > 2 && br->levels[count - 1] == value) + continue; + + br->levels[count] = value; + + if (br->levels[count] > max_level) + max_level = br->levels[count]; + count++; + } + + /* + * some buggy BIOS don't export the levels + * when machine is on AC/Battery in _BCL package. + * In this case, the first two elements in _BCL packages + * are also supported brightness levels that OS should take care of. + */ + for (i = 2; i < count; i++) { + if (br->levels[i] == br->levels[0]) + level_ac_battery++; + if (br->levels[i] == br->levels[1]) + level_ac_battery++; + } + + if (level_ac_battery < 2) { + level_ac_battery = 2 - level_ac_battery; + br->flags._BCL_no_ac_battery_levels = 1; + for (i = (count - 1 + level_ac_battery); i >= 2; i--) + br->levels[i] = br->levels[i - level_ac_battery]; + count += level_ac_battery; + } else if (level_ac_battery > 2) + ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package")); + + /* Check if the _BCL package is in a reversed order */ + if (max_level == br->levels[2]) { + br->flags._BCL_reversed = 1; + sort(&br->levels[2], count - 2, sizeof(br->levels[2]), + acpi_video_cmp_level, NULL); + } else if (max_level != br->levels[count - 1]) + ACPI_ERROR((AE_INFO, + "Found unordered _BCL package")); + + br->count = count; + device->brightness = br; + + /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ + br->curr = level = max_level; + + if (!device->cap._BQC) + goto set_level; + + result = acpi_video_device_lcd_get_level_current(device, + &level_old, true); + if (result) + goto out_free_levels; + + result = acpi_video_bqc_quirk(device, max_level, level_old); + if (result) + goto out_free_levels; + /* + * cap._BQC may get cleared due to _BQC is found to be broken + * in acpi_video_bqc_quirk, so check again here. + */ + if (!device->cap._BQC) + goto set_level; + + level = acpi_video_bqc_value_to_level(device, level_old); + /* + * On some buggy laptops, _BQC returns an uninitialized + * value when invoked for the first time, i.e. + * level_old is invalid (no matter whether it's a level + * or an index). Set the backlight to max_level in this case. + */ + for (i = 2; i < br->count; i++) + if (level == br->levels[i]) + break; + if (i == br->count || !level) + level = max_level; + +set_level: + result = acpi_video_device_lcd_set_level(device, level); + if (result) + goto out_free_levels; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "found %d brightness levels\n", count - 2)); + kfree(obj); + return result; + +out_free_levels: + kfree(br->levels); +out_free: + kfree(br); +out: + device->brightness = NULL; + kfree(obj); + return result; +} + +/* + * Arg: + * device : video output device (LCD, CRT, ..) + * + * Return Value: + * None + * + * Find out all required AML methods defined under the output + * device. + */ + +static void acpi_video_device_find_cap(struct acpi_video_device *device) +{ + if (acpi_has_method(device->dev->handle, "_ADR")) + device->cap._ADR = 1; + if (acpi_has_method(device->dev->handle, "_BCL")) + device->cap._BCL = 1; + if (acpi_has_method(device->dev->handle, "_BCM")) + device->cap._BCM = 1; + if (acpi_has_method(device->dev->handle, "_BQC")) { + device->cap._BQC = 1; + } else if (acpi_has_method(device->dev->handle, "_BCQ")) { + printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n"); + device->cap._BCQ = 1; + } + + if (acpi_has_method(device->dev->handle, "_DDC")) + device->cap._DDC = 1; +} + +/* + * Arg: + * device : video output device (VGA) + * + * Return Value: + * None + * + * Find out all required AML methods defined under the video bus device. + */ + +static void acpi_video_bus_find_cap(struct acpi_video_bus *video) +{ + if (acpi_has_method(video->device->handle, "_DOS")) + video->cap._DOS = 1; + if (acpi_has_method(video->device->handle, "_DOD")) + video->cap._DOD = 1; + if (acpi_has_method(video->device->handle, "_ROM")) + video->cap._ROM = 1; + if (acpi_has_method(video->device->handle, "_GPD")) + video->cap._GPD = 1; + if (acpi_has_method(video->device->handle, "_SPD")) + video->cap._SPD = 1; + if (acpi_has_method(video->device->handle, "_VPO")) + video->cap._VPO = 1; +} + +/* + * Check whether the video bus device has required AML method to + * support the desired features + */ + +static int acpi_video_bus_check(struct acpi_video_bus *video) +{ + acpi_status status = -ENOENT; + struct pci_dev *dev; + + if (!video) + return -EINVAL; + + dev = acpi_get_pci_dev(video->device->handle); + if (!dev) + return -ENODEV; + pci_dev_put(dev); + + /* + * Since there is no HID, CID and so on for VGA driver, we have + * to check well known required nodes. + */ + + /* Does this device support video switching? */ + if (video->cap._DOS || video->cap._DOD) { + if (!video->cap._DOS) { + printk(KERN_WARNING FW_BUG + "ACPI(%s) defines _DOD but not _DOS\n", + acpi_device_bid(video->device)); + } + video->flags.multihead = 1; + status = 0; + } + + /* Does this device support retrieving a video ROM? */ + if (video->cap._ROM) { + video->flags.rom = 1; + status = 0; + } + + /* Does this device support configuring which video device to POST? */ + if (video->cap._GPD && video->cap._SPD && video->cap._VPO) { + video->flags.post = 1; + status = 0; + } + + return status; +} + +/* + * -------------------------------------------------------------------------- + * Driver Interface + * -------------------------------------------------------------------------- + */ + +/* device interface */ +static struct acpi_video_device_attrib * +acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id) +{ + struct acpi_video_enumerated_device *ids; + int i; + + for (i = 0; i < video->attached_count; i++) { + ids = &video->attached_array[i]; + if ((ids->value.int_val & 0xffff) == device_id) + return &ids->value.attrib; + } + + return NULL; +} + +static int +acpi_video_get_device_type(struct acpi_video_bus *video, + unsigned long device_id) +{ + struct acpi_video_enumerated_device *ids; + int i; + + for (i = 0; i < video->attached_count; i++) { + ids = &video->attached_array[i]; + if ((ids->value.int_val & 0xffff) == device_id) + return ids->value.int_val; + } + + return 0; +} + +static int +acpi_video_bus_get_one_device(struct acpi_device *device, + struct acpi_video_bus *video) +{ + unsigned long long device_id; + int status, device_type; + struct acpi_video_device *data; + struct acpi_video_device_attrib *attribute; + + status = + acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); + /* Some device omits _ADR, we skip them instead of fail */ + if (ACPI_FAILURE(status)) + return 0; + + data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); + if (!data) + return -ENOMEM; + + strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); + device->driver_data = data; + + data->device_id = device_id; + data->video = video; + data->dev = device; + INIT_DELAYED_WORK(&data->switch_brightness_work, + acpi_video_switch_brightness); + + attribute = acpi_video_get_device_attr(video, device_id); + + if (attribute && attribute->device_id_scheme) { + switch (attribute->display_type) { + case ACPI_VIDEO_DISPLAY_CRT: + data->flags.crt = 1; + break; + case ACPI_VIDEO_DISPLAY_TV: + data->flags.tvout = 1; + break; + case ACPI_VIDEO_DISPLAY_DVI: + data->flags.dvi = 1; + break; + case ACPI_VIDEO_DISPLAY_LCD: + data->flags.lcd = 1; + break; + default: + data->flags.unknown = 1; + break; + } + if (attribute->bios_can_detect) + data->flags.bios = 1; + } else { + /* Check for legacy IDs */ + device_type = acpi_video_get_device_type(video, device_id); + /* Ignore bits 16 and 18-20 */ + switch (device_type & 0xffe2ffff) { + case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: + data->flags.crt = 1; + break; + case ACPI_VIDEO_DISPLAY_LEGACY_PANEL: + data->flags.lcd = 1; + break; + case ACPI_VIDEO_DISPLAY_LEGACY_TV: + data->flags.tvout = 1; + break; + default: + data->flags.unknown = 1; + } + } + + acpi_video_device_bind(video, data); + acpi_video_device_find_cap(data); + + mutex_lock(&video->device_list_lock); + list_add_tail(&data->entry, &video->video_device_list); + mutex_unlock(&video->device_list_lock); + + return status; +} + +/* + * Arg: + * video : video bus device + * + * Return: + * none + * + * Enumerate the video device list of the video bus, + * bind the ids with the corresponding video devices + * under the video bus. + */ + +static void acpi_video_device_rebind(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + + mutex_lock(&video->device_list_lock); + + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_device_bind(video, dev); + + mutex_unlock(&video->device_list_lock); +} + +/* + * Arg: + * video : video bus device + * device : video output device under the video + * bus + * + * Return: + * none + * + * Bind the ids with the corresponding video devices + * under the video bus. + */ + +static void +acpi_video_device_bind(struct acpi_video_bus *video, + struct acpi_video_device *device) +{ + struct acpi_video_enumerated_device *ids; + int i; + + for (i = 0; i < video->attached_count; i++) { + ids = &video->attached_array[i]; + if (device->device_id == (ids->value.int_val & 0xffff)) { + ids->bind_info = device; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); + } + } +} + +static bool acpi_video_device_in_dod(struct acpi_video_device *device) +{ + struct acpi_video_bus *video = device->video; + int i; + + /* + * If we have a broken _DOD or we have more than 8 output devices + * under the graphics controller node that we can't proper deal with + * in the operation region code currently, no need to test. + */ + if (!video->attached_count || video->child_count > 8) + return true; + + for (i = 0; i < video->attached_count; i++) { + if ((video->attached_array[i].value.int_val & 0xfff) == + (device->device_id & 0xfff)) + return true; + } + + return false; +} + +/* + * Arg: + * video : video bus device + * + * Return: + * < 0 : error + * + * Call _DOD to enumerate all devices attached to display adapter + * + */ + +static int acpi_video_device_enumerate(struct acpi_video_bus *video) +{ + int status; + int count; + int i; + struct acpi_video_enumerated_device *active_list; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *dod = NULL; + union acpi_object *obj; + + status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); + if (!ACPI_SUCCESS(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); + return status; + } + + dod = buffer.pointer; + if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data")); + status = -EFAULT; + goto out; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", + dod->package.count)); + + active_list = kcalloc(1 + dod->package.count, + sizeof(struct acpi_video_enumerated_device), + GFP_KERNEL); + if (!active_list) { + status = -ENOMEM; + goto out; + } + + count = 0; + for (i = 0; i < dod->package.count; i++) { + obj = &dod->package.elements[i]; + + if (obj->type != ACPI_TYPE_INTEGER) { + printk(KERN_ERR PREFIX + "Invalid _DOD data in element %d\n", i); + continue; + } + + active_list[count].value.int_val = obj->integer.value; + active_list[count].bind_info = NULL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, + (int)obj->integer.value)); + count++; + } + + kfree(video->attached_array); + + video->attached_array = active_list; + video->attached_count = count; + +out: + kfree(buffer.pointer); + return status; +} + +static int +acpi_video_get_next_level(struct acpi_video_device *device, + u32 level_current, u32 event) +{ + int min, max, min_above, max_below, i, l, delta = 255; + max = max_below = 0; + min = min_above = 255; + /* Find closest level to level_current */ + for (i = 2; i < device->brightness->count; i++) { + l = device->brightness->levels[i]; + if (abs(l - level_current) < abs(delta)) { + delta = l - level_current; + if (!delta) + break; + } + } + /* Ajust level_current to closest available level */ + level_current += delta; + for (i = 2; i < device->brightness->count; i++) { + l = device->brightness->levels[i]; + if (l < min) + min = l; + if (l > max) + max = l; + if (l < min_above && l > level_current) + min_above = l; + if (l > max_below && l < level_current) + max_below = l; + } + + switch (event) { + case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: + return (level_current < max) ? min_above : min; + case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: + return (level_current < max) ? min_above : max; + case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: + return (level_current > min) ? max_below : min; + case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: + case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: + return 0; + default: + return level_current; + } +} + +static void +acpi_video_switch_brightness(struct work_struct *work) +{ + struct acpi_video_device *device = container_of(to_delayed_work(work), + struct acpi_video_device, switch_brightness_work); + unsigned long long level_current, level_next; + int event = device->switch_brightness_event; + int result = -EINVAL; + + /* no warning message if acpi_backlight=vendor or a quirk is used */ + if (!acpi_video_verify_backlight_support()) + return; + + if (!device->brightness) + goto out; + + result = acpi_video_device_lcd_get_level_current(device, + &level_current, + false); + if (result) + goto out; + + level_next = acpi_video_get_next_level(device, level_current, event); + + result = acpi_video_device_lcd_set_level(device, level_next); + + if (!result) + backlight_force_update(device->backlight, + BACKLIGHT_UPDATE_HOTKEY); + +out: + if (result) + printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); +} + +int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, + void **edid) +{ + struct acpi_video_bus *video; + struct acpi_video_device *video_device; + union acpi_object *buffer = NULL; + acpi_status status; + int i, length; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + video = acpi_driver_data(device); + + for (i = 0; i < video->attached_count; i++) { + video_device = video->attached_array[i].bind_info; + length = 256; + + if (!video_device) + continue; + + if (!video_device->cap._DDC) + continue; + + if (type) { + switch (type) { + case ACPI_VIDEO_DISPLAY_CRT: + if (!video_device->flags.crt) + continue; + break; + case ACPI_VIDEO_DISPLAY_TV: + if (!video_device->flags.tvout) + continue; + break; + case ACPI_VIDEO_DISPLAY_DVI: + if (!video_device->flags.dvi) + continue; + break; + case ACPI_VIDEO_DISPLAY_LCD: + if (!video_device->flags.lcd) + continue; + break; + } + } else if (video_device->device_id != device_id) { + continue; + } + + status = acpi_video_device_EDID(video_device, &buffer, length); + + if (ACPI_FAILURE(status) || !buffer || + buffer->type != ACPI_TYPE_BUFFER) { + length = 128; + status = acpi_video_device_EDID(video_device, &buffer, + length); + if (ACPI_FAILURE(status) || !buffer || + buffer->type != ACPI_TYPE_BUFFER) { + continue; + } + } + + *edid = buffer->buffer.pointer; + return length; + } + + return -ENODEV; +} +EXPORT_SYMBOL(acpi_video_get_edid); + +static int +acpi_video_bus_get_devices(struct acpi_video_bus *video, + struct acpi_device *device) +{ + int status = 0; + struct acpi_device *dev; + + /* + * There are systems where video module known to work fine regardless + * of broken _DOD and ignoring returned value here doesn't cause + * any issues later. + */ + acpi_video_device_enumerate(video); + + list_for_each_entry(dev, &device->children, node) { + + status = acpi_video_bus_get_one_device(dev, video); + if (status) { + dev_err(&dev->dev, "Can't attach device\n"); + break; + } + video->child_count++; + } + return status; +} + +/* acpi_video interface */ + +/* + * Win8 requires setting bit2 of _DOS to let firmware know it shouldn't + * preform any automatic brightness change on receiving a notification. + */ +static int acpi_video_bus_start_devices(struct acpi_video_bus *video) +{ + return acpi_video_bus_DOS(video, 0, + acpi_osi_is_win8() ? 1 : 0); +} + +static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) +{ + return acpi_video_bus_DOS(video, 0, + acpi_osi_is_win8() ? 0 : 1); +} + +static void acpi_video_bus_notify(struct acpi_device *device, u32 event) +{ + struct acpi_video_bus *video = acpi_driver_data(device); + struct input_dev *input; + int keycode = 0; + + if (!video || !video->input) + return; + + input = video->input; + + switch (event) { + case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, + * most likely via hotkey. */ + keycode = KEY_SWITCHVIDEOMODE; + break; + + case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video + * connector. */ + acpi_video_device_enumerate(video); + acpi_video_device_rebind(video); + keycode = KEY_SWITCHVIDEOMODE; + break; + + case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ + keycode = KEY_SWITCHVIDEOMODE; + break; + case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ + keycode = KEY_VIDEO_NEXT; + break; + case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ + keycode = KEY_VIDEO_PREV; + break; + + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + if (acpi_notifier_call_chain(device, event, 0)) + /* Something vetoed the keypress. */ + keycode = 0; + + if (keycode) { + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + } + + return; +} + +static void brightness_switch_event(struct acpi_video_device *video_device, + u32 event) +{ + if (!brightness_switch_enabled) + return; + + video_device->switch_brightness_event = event; + schedule_delayed_work(&video_device->switch_brightness_work, HZ / 10); +} + +static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_video_device *video_device = data; + struct acpi_device *device = NULL; + struct acpi_video_bus *bus; + struct input_dev *input; + int keycode = 0; + + if (!video_device) + return; + + device = video_device->dev; + bus = video_device->video; + input = bus->input; + + switch (event) { + case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESS_CYCLE; + break; + case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESSUP; + break; + case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESSDOWN; + break; + case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESS_ZERO; + break; + case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ + brightness_switch_event(video_device, event); + keycode = KEY_DISPLAY_OFF; + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + acpi_notifier_call_chain(device, event, 0); + + if (keycode) { + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + } + + return; +} + +static int acpi_video_resume(struct notifier_block *nb, + unsigned long val, void *ign) +{ + struct acpi_video_bus *video; + struct acpi_video_device *video_device; + int i; + + switch (val) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: + return NOTIFY_DONE; + } + + video = container_of(nb, struct acpi_video_bus, pm_nb); + + dev_info(&video->device->dev, "Restoring backlight state\n"); + + for (i = 0; i < video->attached_count; i++) { + video_device = video->attached_array[i].bind_info; + if (video_device && video_device->backlight) + acpi_video_set_brightness(video_device->backlight); + } + + return NOTIFY_OK; +} + +static acpi_status +acpi_video_bus_match(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + struct acpi_device *device = context; + struct acpi_device *sibling; + int result; + + if (handle == device->handle) + return AE_CTRL_TERMINATE; + + result = acpi_bus_get_device(handle, &sibling); + if (result) + return AE_OK; + + if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) + return AE_ALREADY_EXISTS; + + return AE_OK; +} + +static void acpi_video_dev_register_backlight(struct acpi_video_device *device) +{ + struct backlight_properties props; + struct pci_dev *pdev; + acpi_handle acpi_parent; + struct device *parent = NULL; + int result; + static int count; + char *name; + + /* + * Do not create backlight device for video output + * device that is not in the enumerated list. + */ + if (!acpi_video_device_in_dod(device)) { + dev_dbg(&device->dev->dev, "not in _DOD list, ignore\n"); + return; + } + + result = acpi_video_init_brightness(device); + if (result) + return; + name = kasprintf(GFP_KERNEL, "acpi_video%d", count); + if (!name) + return; + count++; + + acpi_get_parent(device->dev->handle, &acpi_parent); + + pdev = acpi_get_pci_dev(acpi_parent); + if (pdev) { + parent = &pdev->dev; + pci_dev_put(pdev); + } + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_FIRMWARE; + props.max_brightness = device->brightness->count - 3; + device->backlight = backlight_device_register(name, + parent, + device, + &acpi_backlight_ops, + &props); + kfree(name); + if (IS_ERR(device->backlight)) + return; + + /* + * Save current brightness level in case we have to restore it + * before acpi_video_device_lcd_set_level() is called next time. + */ + device->backlight->props.brightness = + acpi_video_get_brightness(device->backlight); + + device->cooling_dev = thermal_cooling_device_register("LCD", + device->dev, &video_cooling_ops); + if (IS_ERR(device->cooling_dev)) { + /* + * Set cooling_dev to NULL so we don't crash trying to free it. + * Also, why the hell we are returning early and not attempt to + * register video output if cooling device registration failed? + * -- dtor + */ + device->cooling_dev = NULL; + return; + } + + dev_info(&device->dev->dev, "registered as cooling_device%d\n", + device->cooling_dev->id); + result = sysfs_create_link(&device->dev->dev.kobj, + &device->cooling_dev->device.kobj, + "thermal_cooling"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); + result = sysfs_create_link(&device->cooling_dev->device.kobj, + &device->dev->dev.kobj, "device"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); +} + +static void acpi_video_run_bcl_for_osi(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + union acpi_object *levels; + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) { + if (!acpi_video_device_lcd_query_levels(dev, &levels)) + kfree(levels); + } + mutex_unlock(&video->device_list_lock); +} + +static int acpi_video_bus_register_backlight(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + + if (video->backlight_registered) + return 0; + + acpi_video_run_bcl_for_osi(video); + + if (!acpi_video_verify_backlight_support()) + return 0; + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_register_backlight(dev); + mutex_unlock(&video->device_list_lock); + + video->backlight_registered = true; + + video->pm_nb.notifier_call = acpi_video_resume; + video->pm_nb.priority = 0; + return register_pm_notifier(&video->pm_nb); +} + +static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device) +{ + if (device->backlight) { + backlight_device_unregister(device->backlight); + device->backlight = NULL; + } + if (device->brightness) { + kfree(device->brightness->levels); + kfree(device->brightness); + device->brightness = NULL; + } + if (device->cooling_dev) { + sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&device->cooling_dev->device.kobj, "device"); + thermal_cooling_device_unregister(device->cooling_dev); + device->cooling_dev = NULL; + } +} + +static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + int error; + + if (!video->backlight_registered) + return 0; + + error = unregister_pm_notifier(&video->pm_nb); + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_unregister_backlight(dev); + mutex_unlock(&video->device_list_lock); + + video->backlight_registered = false; + + return error; +} + +static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device) +{ + acpi_status status; + struct acpi_device *adev = device->dev; + + status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + acpi_video_device_notify, device); + if (ACPI_FAILURE(status)) + dev_err(&adev->dev, "Error installing notify handler\n"); + else + device->flags.notify = 1; +} + +static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) +{ + struct input_dev *input; + struct acpi_video_device *dev; + int error; + + video->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto out; + } + + error = acpi_video_bus_start_devices(video); + if (error) + goto err_free_input; + + snprintf(video->phys, sizeof(video->phys), + "%s/video/input0", acpi_device_hid(video->device)); + + input->name = acpi_device_name(video->device); + input->phys = video->phys; + input->id.bustype = BUS_HOST; + input->id.product = 0x06; + input->dev.parent = &video->device->dev; + input->evbit[0] = BIT(EV_KEY); + set_bit(KEY_SWITCHVIDEOMODE, input->keybit); + set_bit(KEY_VIDEO_NEXT, input->keybit); + set_bit(KEY_VIDEO_PREV, input->keybit); + set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit); + set_bit(KEY_BRIGHTNESSUP, input->keybit); + set_bit(KEY_BRIGHTNESSDOWN, input->keybit); + set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); + set_bit(KEY_DISPLAY_OFF, input->keybit); + + error = input_register_device(input); + if (error) + goto err_stop_dev; + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_add_notify_handler(dev); + mutex_unlock(&video->device_list_lock); + + return 0; + +err_stop_dev: + acpi_video_bus_stop_devices(video); +err_free_input: + input_free_device(input); + video->input = NULL; +out: + return error; +} + +static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev) +{ + if (dev->flags.notify) { + acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY, + acpi_video_device_notify); + dev->flags.notify = 0; + } +} + +static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_remove_notify_handler(dev); + mutex_unlock(&video->device_list_lock); + + acpi_video_bus_stop_devices(video); + input_unregister_device(video->input); + video->input = NULL; +} + +static int acpi_video_backlight_notify(struct notifier_block *nb, + unsigned long val, void *bd) +{ + struct backlight_device *backlight = bd; + struct acpi_video_bus *video; + + /* acpi_video_verify_backlight_support only cares about raw devices */ + if (backlight->props.type != BACKLIGHT_RAW) + return NOTIFY_DONE; + + video = container_of(nb, struct acpi_video_bus, backlight_nb); + + switch (val) { + case BACKLIGHT_REGISTERED: + if (!acpi_video_verify_backlight_support()) + acpi_video_bus_unregister_backlight(video); + break; + case BACKLIGHT_UNREGISTERED: + acpi_video_bus_register_backlight(video); + break; + } + + return NOTIFY_OK; +} + +static int acpi_video_bus_add_backlight_notify_handler( + struct acpi_video_bus *video) +{ + int error; + + video->backlight_nb.notifier_call = acpi_video_backlight_notify; + video->backlight_nb.priority = 0; + error = backlight_register_notifier(&video->backlight_nb); + if (error == 0) + video->backlight_notifier_registered = true; + + return error; +} + +static int acpi_video_bus_remove_backlight_notify_handler( + struct acpi_video_bus *video) +{ + if (!video->backlight_notifier_registered) + return 0; + + video->backlight_notifier_registered = false; + + return backlight_unregister_notifier(&video->backlight_nb); +} + +static int acpi_video_bus_put_devices(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev, *next; + + mutex_lock(&video->device_list_lock); + list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { + list_del(&dev->entry); + kfree(dev); + } + mutex_unlock(&video->device_list_lock); + + return 0; +} + +static int instance; + +static int acpi_video_bus_add(struct acpi_device *device) +{ + struct acpi_video_bus *video; + int error; + acpi_status status; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, + device->parent->handle, 1, + acpi_video_bus_match, NULL, + device, NULL); + if (status == AE_ALREADY_EXISTS) { + printk(KERN_WARNING FW_BUG + "Duplicate ACPI video bus devices for the" + " same VGA controller, please try module " + "parameter \"video.allow_duplicates=1\"" + "if the current driver doesn't work.\n"); + if (!allow_duplicates) + return -ENODEV; + } + + video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); + if (!video) + return -ENOMEM; + + /* a hack to fix the duplicate name "VID" problem on T61 */ + if (!strcmp(device->pnp.bus_id, "VID")) { + if (instance) + device->pnp.bus_id[3] = '0' + instance; + instance++; + } + /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */ + if (!strcmp(device->pnp.bus_id, "VGA")) { + if (instance) + device->pnp.bus_id[3] = '0' + instance; + instance++; + } + + video->device = device; + strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); + strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); + device->driver_data = video; + + acpi_video_bus_find_cap(video); + error = acpi_video_bus_check(video); + if (error) + goto err_free_video; + + mutex_init(&video->device_list_lock); + INIT_LIST_HEAD(&video->video_device_list); + + error = acpi_video_bus_get_devices(video, device); + if (error) + goto err_put_video; + + printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", + ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), + video->flags.multihead ? "yes" : "no", + video->flags.rom ? "yes" : "no", + video->flags.post ? "yes" : "no"); + mutex_lock(&video_list_lock); + list_add_tail(&video->entry, &video_bus_head); + mutex_unlock(&video_list_lock); + + acpi_video_bus_register_backlight(video); + acpi_video_bus_add_notify_handler(video); + acpi_video_bus_add_backlight_notify_handler(video); + + return 0; + +err_put_video: + acpi_video_bus_put_devices(video); + kfree(video->attached_array); +err_free_video: + kfree(video); + device->driver_data = NULL; + + return error; +} + +static int acpi_video_bus_remove(struct acpi_device *device) +{ + struct acpi_video_bus *video = NULL; + + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + video = acpi_driver_data(device); + + acpi_video_bus_remove_backlight_notify_handler(video); + acpi_video_bus_remove_notify_handler(video); + acpi_video_bus_unregister_backlight(video); + acpi_video_bus_put_devices(video); + + mutex_lock(&video_list_lock); + list_del(&video->entry); + mutex_unlock(&video_list_lock); + + kfree(video->attached_array); + kfree(video); + + return 0; +} + +static int __init is_i740(struct pci_dev *dev) +{ + if (dev->device == 0x00D1) + return 1; + if (dev->device == 0x7000) + return 1; + return 0; +} + +static int __init intel_opregion_present(void) +{ + int opregion = 0; + struct pci_dev *dev = NULL; + u32 address; + + for_each_pci_dev(dev) { + if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) + continue; + if (dev->vendor != PCI_VENDOR_ID_INTEL) + continue; + /* We don't want to poke around undefined i740 registers */ + if (is_i740(dev)) + continue; + pci_read_config_dword(dev, 0xfc, &address); + if (!address) + continue; + opregion = 1; + } + return opregion; +} + +int acpi_video_register(void) +{ + int ret; + + if (register_count) { + /* + * if the function of acpi_video_register is already called, + * don't register the acpi_vide_bus again and return no error. + */ + return 0; + } + + mutex_init(&video_list_lock); + INIT_LIST_HEAD(&video_bus_head); + + ret = acpi_bus_register_driver(&acpi_video_bus); + if (ret) + return ret; + + /* + * When the acpi_video_bus is loaded successfully, increase + * the counter reference. + */ + register_count = 1; + + return 0; +} +EXPORT_SYMBOL(acpi_video_register); + +void acpi_video_unregister(void) +{ + if (!register_count) { + /* + * If the acpi video bus is already unloaded, don't + * unload it again and return directly. + */ + return; + } + acpi_bus_unregister_driver(&acpi_video_bus); + + register_count = 0; + + return; +} +EXPORT_SYMBOL(acpi_video_unregister); + +void acpi_video_unregister_backlight(void) +{ + struct acpi_video_bus *video; + + if (!register_count) + return; + + mutex_lock(&video_list_lock); + list_for_each_entry(video, &video_bus_head, entry) + acpi_video_bus_unregister_backlight(video); + mutex_unlock(&video_list_lock); +} +EXPORT_SYMBOL(acpi_video_unregister_backlight); + +/* + * This is kind of nasty. Hardware using Intel chipsets may require + * the video opregion code to be run first in order to initialise + * state before any ACPI video calls are made. To handle this we defer + * registration of the video class until the opregion code has run. + */ + +static int __init acpi_video_init(void) +{ + /* + * Let the module load even if ACPI is disabled (e.g. due to + * a broken BIOS) so that i915.ko can still be loaded on such + * old systems without an AcpiOpRegion. + * + * acpi_video_register() will report -ENODEV later as well due + * to acpi_disabled when i915.ko tries to register itself afterwards. + */ + if (acpi_disabled) + return 0; + + dmi_check_system(video_dmi_table); + + if (intel_opregion_present()) + return 0; + + return acpi_video_register(); +} + +static void __exit acpi_video_exit(void) +{ + acpi_video_unregister(); + + return; +} + +module_init(acpi_video_init); +module_exit(acpi_video_exit); diff --git a/kernel/drivers/acpi/video_detect.c b/kernel/drivers/acpi/video_detect.c new file mode 100644 index 000000000..c42feb2ba --- /dev/null +++ b/kernel/drivers/acpi/video_detect.c @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2008 SuSE Linux Products GmbH + * Thomas Renninger + * + * May be copied or modified under the terms of the GNU General Public License + * + * video_detect.c: + * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c + * There a Linux specific (Spec does not provide a HID for video devices) is + * assigned + * + * After PCI devices are glued with ACPI devices + * acpi_get_pci_dev() can be called to identify ACPI graphics + * devices for which a real graphics card is plugged in + * + * Now acpi_video_get_capabilities() can be called to check which + * capabilities the graphics cards plugged in support. The check for general + * video capabilities will be triggered by the first caller of + * acpi_video_get_capabilities(NULL); which will happen when the first + * backlight switching supporting driver calls: + * acpi_video_backlight_support(); + * + * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B) + * are available, video.ko should be used to handle the device. + * + * Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop, + * sony_acpi,... can take care about backlight brightness. + * + * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) + * this file will not be compiled, acpi_video_get_capabilities() and + * acpi_video_backlight_support() will always return 0 and vendor specific + * drivers always can handle backlight. + * + */ + +#include +#include +#include +#include + +#include "internal.h" + +ACPI_MODULE_NAME("video"); +#define _COMPONENT ACPI_VIDEO_COMPONENT + +static long acpi_video_support; +static bool acpi_video_caps_checked; + +static acpi_status +acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + long *cap = context; + + if (acpi_has_method(handle, "_BCM") && + acpi_has_method(handle, "_BCL")) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight " + "support\n")); + *cap |= ACPI_VIDEO_BACKLIGHT; + if (!acpi_has_method(handle, "_BQC")) + printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, " + "cannot determine initial brightness\n"); + /* We have backlight support, no need to scan further */ + return AE_CTRL_TERMINATE; + } + return 0; +} + +/* Returns true if the ACPI object is a video device which can be + * handled by video.ko. + * The device will get a Linux specific CID added in scan.c to + * identify the device as an ACPI graphics device + * Be aware that the graphics device may not be physically present + * Use acpi_video_get_capabilities() to detect general ACPI video + * capabilities of present cards + */ +long acpi_is_video_device(acpi_handle handle) +{ + long video_caps = 0; + + /* Is this device able to support video switching ? */ + if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS")) + video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; + + /* Is this device able to retrieve a video ROM ? */ + if (acpi_has_method(handle, "_ROM")) + video_caps |= ACPI_VIDEO_ROM_AVAILABLE; + + /* Is this device able to configure which video head to be POSTed ? */ + if (acpi_has_method(handle, "_VPO") && + acpi_has_method(handle, "_GPD") && + acpi_has_method(handle, "_SPD")) + video_caps |= ACPI_VIDEO_DEVICE_POSTING; + + /* Only check for backlight functionality if one of the above hit. */ + if (video_caps) + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, + ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL, + &video_caps, NULL); + + return video_caps; +} +EXPORT_SYMBOL(acpi_is_video_device); + +static acpi_status +find_video(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + long *cap = context; + struct pci_dev *dev; + struct acpi_device *acpi_dev; + + const struct acpi_device_id video_ids[] = { + {ACPI_VIDEO_HID, 0}, + {"", 0}, + }; + if (acpi_bus_get_device(handle, &acpi_dev)) + return AE_OK; + + if (!acpi_match_device_ids(acpi_dev, video_ids)) { + dev = acpi_get_pci_dev(handle); + if (!dev) + return AE_OK; + pci_dev_put(dev); + *cap |= acpi_is_video_device(handle); + } + return AE_OK; +} + +/* Force to use vendor driver when the ACPI device is known to be + * buggy */ +static int video_detect_force_vendor(const struct dmi_system_id *d) +{ + acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; + return 0; +} + +static struct dmi_system_id video_detect_dmi_table[] = { + /* On Samsung X360, the BIOS will set a flag (VDRV) if generic + * ACPI backlight device is used. This flag will definitively break + * the backlight interface (even the vendor interface) untill next + * reboot. It's why we should prevent video.ko from being used here + * and we can't rely on a later call to acpi_video_unregister(). + */ + { + .callback = video_detect_force_vendor, + .ident = "X360", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "X360"), + DMI_MATCH(DMI_BOARD_NAME, "X360"), + }, + }, + { + .callback = video_detect_force_vendor, + .ident = "Asus UL30VT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "UL30VT"), + }, + }, + { + .callback = video_detect_force_vendor, + .ident = "Asus UL30A", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "UL30A"), + }, + }, + { + .callback = video_detect_force_vendor, + .ident = "Dell Inspiron 5737", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"), + }, + }, + { }, +}; + +/* + * Returns the video capabilities of a specific ACPI graphics device + * + * if NULL is passed as argument all ACPI devices are enumerated and + * all graphics capabilities of physically present devices are + * summarized and returned. This is cached and done only once. + */ +long acpi_video_get_capabilities(acpi_handle graphics_handle) +{ + long caps = 0; + struct acpi_device *tmp_dev; + acpi_status status; + + if (acpi_video_caps_checked && graphics_handle == NULL) + return acpi_video_support; + + if (!graphics_handle) { + /* Only do the global walk through all graphics devices once */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_video, NULL, + &caps, NULL); + /* There might be boot param flags set already... */ + acpi_video_support |= caps; + acpi_video_caps_checked = 1; + /* Add blacklists here. Be careful to use the right *DMI* bits + * to still be able to override logic via boot params, e.g.: + * + * if (dmi_name_in_vendors("XY")) { + * acpi_video_support |= + * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; + *} + */ + + dmi_check_system(video_detect_dmi_table); + } else { + status = acpi_bus_get_device(graphics_handle, &tmp_dev); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid device")); + return 0; + } + acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle, + ACPI_UINT32_MAX, find_video, NULL, + &caps, NULL); + } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n", + graphics_handle ? caps : acpi_video_support, + graphics_handle ? "on device " : "in general", + graphics_handle ? acpi_device_bid(tmp_dev) : "")); + return caps; +} +EXPORT_SYMBOL(acpi_video_get_capabilities); + +static void acpi_video_caps_check(void) +{ + /* + * We must check whether the ACPI graphics device is physically plugged + * in. Therefore this must be called after binding PCI and ACPI devices + */ + if (!acpi_video_caps_checked) + acpi_video_get_capabilities(NULL); +} + +bool acpi_osi_is_win8(void) +{ + return acpi_gbl_osi_data >= ACPI_OSI_WIN_8; +} +EXPORT_SYMBOL(acpi_osi_is_win8); + +/* Promote the vendor interface instead of the generic video module. + * This function allow DMI blacklists to be implemented by externals + * platform drivers instead of putting a big blacklist in video_detect.c + * After calling this function you will probably want to call + * acpi_video_unregister() to make sure the video module is not loaded + */ +void acpi_video_dmi_promote_vendor(void) +{ + acpi_video_caps_check(); + acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; +} +EXPORT_SYMBOL(acpi_video_dmi_promote_vendor); + +/* To be called when a driver who previously promoted the vendor + * interface */ +void acpi_video_dmi_demote_vendor(void) +{ + acpi_video_caps_check(); + acpi_video_support &= ~ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; +} +EXPORT_SYMBOL(acpi_video_dmi_demote_vendor); + +/* Returns true if video.ko can do backlight switching */ +int acpi_video_backlight_support(void) +{ + acpi_video_caps_check(); + + /* First check for boot param -> highest prio */ + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO) + return 1; + + /* Then check for DMI blacklist -> second highest prio */ + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO) + return 1; + + /* Then go the default way */ + return acpi_video_support & ACPI_VIDEO_BACKLIGHT; +} +EXPORT_SYMBOL(acpi_video_backlight_support); + +/* + * Use acpi_backlight=vendor/video to force that backlight switching + * is processed by vendor specific acpi drivers or video.ko driver. + */ +static int __init acpi_backlight(char *str) +{ + if (str == NULL || *str == '\0') + return 1; + else { + if (!strcmp("vendor", str)) + acpi_video_support |= + ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR; + if (!strcmp("video", str)) + acpi_video_support |= + ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO; + } + return 1; +} +__setup("acpi_backlight=", acpi_backlight); diff --git a/kernel/drivers/acpi/wakeup.c b/kernel/drivers/acpi/wakeup.c new file mode 100644 index 000000000..1638401ab --- /dev/null +++ b/kernel/drivers/acpi/wakeup.c @@ -0,0 +1,97 @@ +/* + * wakeup.c - support wakeup devices + * Copyright (C) 2004 Li Shaohua + */ + +#include +#include +#include +#include + +#include "internal.h" +#include "sleep.h" + +/* + * We didn't lock acpi_device_lock in the file, because it invokes oops in + * suspend/resume and isn't really required as this is called in S-state. At + * that time, there is no device hotplug + **/ +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME("wakeup_devices") + +/** + * acpi_enable_wakeup_devices - Enable wake-up device GPEs. + * @sleep_state: ACPI system sleep state. + * + * Enable wakeup device power of devices with the state.enable flag set and set + * the wakeup enable mask bits in the GPE registers that correspond to wakeup + * devices. + */ +void acpi_enable_wakeup_devices(u8 sleep_state) +{ + struct list_head *node, *next; + + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + + if (!dev->wakeup.flags.valid + || sleep_state > (u32) dev->wakeup.sleep_state + || !(device_may_wakeup(&dev->dev) + || dev->wakeup.prepare_count)) + continue; + + if (device_may_wakeup(&dev->dev)) + acpi_enable_wakeup_device_power(dev, sleep_state); + + /* The wake-up power should have been enabled already. */ + acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number, + ACPI_GPE_ENABLE); + } +} + +/** + * acpi_disable_wakeup_devices - Disable devices' wakeup capability. + * @sleep_state: ACPI system sleep state. + */ +void acpi_disable_wakeup_devices(u8 sleep_state) +{ + struct list_head *node, *next; + + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + + if (!dev->wakeup.flags.valid + || sleep_state > (u32) dev->wakeup.sleep_state + || !(device_may_wakeup(&dev->dev) + || dev->wakeup.prepare_count)) + continue; + + acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number, + ACPI_GPE_DISABLE); + + if (device_may_wakeup(&dev->dev)) + acpi_disable_wakeup_device_power(dev); + } +} + +int __init acpi_wakeup_device_init(void) +{ + struct list_head *node, *next; + + mutex_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device *dev = container_of(node, + struct acpi_device, + wakeup_list); + if (device_can_wakeup(&dev->dev)) { + /* Button GPEs are supposed to be always enabled. */ + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number); + device_set_wakeup_enable(&dev->dev, true); + } + } + mutex_unlock(&acpi_device_lock); + return 0; +} -- cgit 1.2.3-korg