1/* $NetBSD: apmbios.c,v 1.18 2011/04/26 15:51:23 joerg Exp $ */ 2 3/*- 4 * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by John Kohl and Christopher G. Demetriou. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: apmbios.c,v 1.18 2011/04/26 15:51:23 joerg Exp $"); 34 35#include "opt_apm.h" 36 37#ifdef APM_NOIDLE 38#error APM_NOIDLE option deprecated; use APM_NO_IDLE instead 39#endif 40 41#if defined(DEBUG) && !defined(APMDEBUG) 42#define APMDEBUG 43#endif 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/signalvar.h> 48#include <sys/kernel.h> 49#include <sys/proc.h> 50#include <sys/kthread.h> 51#include <sys/malloc.h> 52#include <sys/device.h> 53#include <sys/fcntl.h> 54#include <sys/ioctl.h> 55#include <sys/select.h> 56#include <sys/poll.h> 57#include <sys/conf.h> 58#include <sys/bus.h> 59#include <sys/cpu.h> 60 61#include <uvm/uvm_extern.h> 62 63#include <machine/cpufunc.h> 64#include <machine/gdt.h> 65#include <machine/psl.h> 66 67#include <dev/ic/i8253reg.h> 68#include <dev/isa/isareg.h> 69#include <dev/isa/isavar.h> 70#include <dev/apm/apmvar.h> 71#include <i386/isa/nvram.h> 72 73#include <machine/bioscall.h> 74#ifdef APM_USE_KVM86 75#include <machine/kvm86.h> 76#endif 77#include <machine/apmvar.h> 78 79#if defined(APMDEBUG) 80#define DPRINTF(f, x) do { if (apmdebug & (f)) printf x; } while (0) 81#else 82#define DPRINTF(f, x) 83#endif 84 85static void apmbiosattach(device_t, device_t, void *); 86static int apmbiosmatch(device_t, cfdata_t, void *); 87 88#if 0 89static void apm_devpowmgt_enable(int, u_int); 90#endif 91static void apm_disconnect(void *); 92static void apm_enable(void *, int); 93static int apm_get_powstat(void *, u_int, struct apm_power_info *); 94static int apm_get_event(void *, u_int *, u_int *); 95static void apm_cpu_busy(void *); 96static void apm_cpu_idle(void *); 97static void apm_get_capabilities(void *, u_int *, u_int *); 98 99struct apm_connect_info apminfo; 100 101static struct apm_accessops apm_accessops = { 102 apm_disconnect, 103 apm_enable, 104 apm_set_powstate, 105 apm_get_powstat, 106 apm_get_event, 107 apm_cpu_busy, 108 apm_cpu_idle, 109 apm_get_capabilities, 110}; 111 112extern int apmdebug; 113extern int apm_enabled; 114extern int apm_force_64k_segments; 115extern int apm_allow_bogus_segments; 116extern int apm_bogus_bios; 117extern int apm_minver; 118extern int apm_inited; 119extern int apm_do_idle; 120extern int apm_v12_enabled; 121extern int apm_v11_enabled; 122 123static void apm_perror(const char *, struct bioscallregs *, ...) 124 __attribute__((__format__(__printf__,1,3))); 125static void apm_powmgt_enable(int); 126static void apm_powmgt_engage(int, u_int); 127static int apm_get_ver(struct apm_softc *); 128 129CFATTACH_DECL_NEW(apmbios, sizeof(struct apm_softc), 130 apmbiosmatch, apmbiosattach, NULL, NULL); 131 132#ifdef APMDEBUG 133int apmcall_debug(int, struct bioscallregs *, int); 134static void acallpr(int, const char *, struct bioscallregs *); 135 136/* bitmask defns for printing apm call args/results */ 137#define ACPF_AX 0x00000001 138#define ACPF_AX_HI 0x00000002 139#define ACPF_EAX 0x00000004 140#define ACPF_BX 0x00000008 141#define ACPF_BX_HI 0x00000010 142#define ACPF_EBX 0x00000020 143#define ACPF_CX 0x00000040 144#define ACPF_CX_HI 0x00000080 145#define ACPF_ECX 0x00000100 146#define ACPF_DX 0x00000200 147#define ACPF_DX_HI 0x00000400 148#define ACPF_EDX 0x00000800 149#define ACPF_SI 0x00001000 150#define ACPF_SI_HI 0x00002000 151#define ACPF_ESI 0x00004000 152#define ACPF_DI 0x00008000 153#define ACPF_DI_HI 0x00010000 154#define ACPF_EDI 0x00020000 155#define ACPF_FLAGS 0x00040000 156#define ACPF_FLAGS_HI 0x00080000 157#define ACPF_EFLAGS 0x00100000 158 159struct acallinfo { 160 const char *name; 161 int inflag; 162 int outflag; 163}; 164 165static struct acallinfo aci[] = { 166 { "install_check", ACPF_BX, ACPF_AX|ACPF_BX|ACPF_CX }, 167 { "connectreal", ACPF_BX, 0 }, 168 { "connect16", ACPF_BX, ACPF_AX|ACPF_BX|ACPF_CX|ACPF_SI|ACPF_DI }, 169 { "connect32", ACPF_BX, ACPF_AX|ACPF_EBX|ACPF_CX|ACPF_DX|ACPF_ESI|ACPF_DI }, 170 { "disconnect", ACPF_BX, 0 }, 171 { "cpu_idle", 0, 0 }, 172 { "cpu_busy", 0, 0 }, 173 { "set_power_state", ACPF_BX|ACPF_CX, 0 }, 174 { "enable_power_state", ACPF_BX|ACPF_CX, 0 }, 175 { "restore_defaults", ACPF_BX, 0 }, 176 { "get_power_status", ACPF_BX, ACPF_BX|ACPF_CX|ACPF_DX|ACPF_SI }, 177 { "get_event", 0, ACPF_BX|ACPF_CX }, 178 { "get_power_state" , ACPF_BX, ACPF_CX }, 179 { "enable_dev_power_mgt", ACPF_BX|ACPF_CX, 0 }, 180 { "driver_version", ACPF_BX|ACPF_CX, ACPF_AX }, 181 { "engage_power_mgt", ACPF_BX|ACPF_CX, 0 }, 182 { "get_caps", ACPF_BX, ACPF_BX|ACPF_CX }, 183 { "resume_timer", ACPF_BX|ACPF_CX|ACPF_SI|ACPF_DI, ACPF_CX|ACPF_SI|ACPF_DI }, 184 { "resume_ring", ACPF_BX|ACPF_CX, ACPF_CX }, 185 { "timer_reqs", ACPF_BX|ACPF_CX, ACPF_CX }, 186}; 187 188static void acallpr(int flag, const char *tag, struct bioscallregs *b) { 189 if (!flag) return; 190 printf("%s ", tag); 191 if (flag & ACPF_AX) printf("ax=%#x ", b->AX); 192 if (flag & ACPF_AX_HI) printf("ax_hi=%#x ", b->AX_HI); 193 if (flag & ACPF_EAX) printf("eax=%#x ", b->EAX); 194 if (flag & ACPF_BX ) printf("bx=%#x ", b->BX); 195 if (flag & ACPF_BX_HI ) printf("bx_hi=%#x ", b->BX_HI); 196 if (flag & ACPF_EBX ) printf("ebx=%#x ", b->EBX); 197 if (flag & ACPF_CX ) printf("cx=%#x ", b->CX); 198 if (flag & ACPF_CX_HI ) printf("cx_hi=%#x ", b->CX_HI); 199 if (flag & ACPF_ECX ) printf("ecx=%#x ", b->ECX); 200 if (flag & ACPF_DX ) printf("dx=%#x ", b->DX); 201 if (flag & ACPF_DX_HI ) printf("dx_hi=%#x ", b->DX_HI); 202 if (flag & ACPF_EDX ) printf("edx=%#x ", b->EDX); 203 if (flag & ACPF_SI ) printf("si=%#x ", b->SI); 204 if (flag & ACPF_SI_HI ) printf("si_hi=%#x ", b->SI_HI); 205 if (flag & ACPF_ESI ) printf("esi=%#x ", b->ESI); 206 if (flag & ACPF_DI ) printf("di=%#x ", b->DI); 207 if (flag & ACPF_DI_HI ) printf("di_hi=%#x ", b->DI_HI); 208 if (flag & ACPF_EDI ) printf("edi=%#x ", b->EDI); 209 if (flag & ACPF_FLAGS ) printf("flags=%#x ", b->FLAGS); 210 if (flag & ACPF_FLAGS_HI) printf("flags_hi=%#x ", b->FLAGS_HI); 211 if (flag & ACPF_EFLAGS ) printf("eflags=%#x ", b->EFLAGS); 212} 213 214int 215apmcall_debug(int func, struct bioscallregs *regs, int line) 216{ 217 int rv; 218 int print = (apmdebug & APMDEBUG_APMCALLS) != 0; 219 const char *name; 220 int inf; 221 int outf = 0; /* XXX: gcc */ 222 long long milli; 223 224 if (print) { 225 if (func >= sizeof(aci) / sizeof(aci[0])) { 226 name = 0; 227 inf = outf = 0; 228 } else { 229 name = aci[func].name; 230 inf = aci[func].inflag; 231 outf = aci[func].outflag; 232 } 233 inittodr(time_second); /* update timestamp */ 234 milli = time_second % 1000; 235 if (name) 236 printf("apmcall@%03lld: %s/%#x (line=%d) ", 237 milli, name, func, line); 238 else 239 printf("apmcall@%03lld: %#x (line=%d) ", 240 milli, func, line); 241 acallpr(inf, "in:", regs); 242 } 243 rv = apmcall(func, regs); 244 if (print) { 245 if (rv) { 246 printf(" => error %#x (%s)\n", regs->AX >> 8, 247 apm_strerror(regs->AX >> 8)); 248 } else { 249 printf(" => "); 250 acallpr(outf, "out:", regs); 251 printf("\n"); 252 } 253 } 254 return (rv); 255} 256 257#define apmcall(f, r) apmcall_debug((f), (r), __LINE__) 258#endif /* APMDEBUG */ 259 260static void 261apm_perror(const char *str, struct bioscallregs *regs, ...) /* XXX cgd */ 262{ 263 va_list ap; 264 265 printf("APM "); 266 267 va_start(ap, regs); 268 vprintf(str, ap); /* XXX cgd */ 269 va_end(ap); 270 271 printf(": %s (0x%x)\n", apm_strerror(APM_ERR_CODE(regs)), regs->AX); 272} 273 274static void 275apm_powmgt_enable(int onoff) 276{ 277 struct bioscallregs regs; 278 279 regs.BX = apm_minver == 0 ? APM_MGT_ALL : APM_DEV_ALLDEVS; 280 regs.CX = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE; 281 if (apmcall(APM_PWR_MGT_ENABLE, ®s) != 0) 282 apm_perror("power management enable all <%s>", ®s, 283 onoff ? "enable" : "disable"); 284} 285 286static void 287apm_powmgt_engage(int onoff, u_int dev) 288{ 289 struct bioscallregs regs; 290 291 if (apm_minver == 0) 292 return; 293 regs.BX = dev; 294 regs.CX = onoff ? APM_MGT_ENGAGE : APM_MGT_DISENGAGE; 295 if (apmcall(APM_PWR_MGT_ENGAGE, ®s) != 0) 296 apm_perror("power mgmt engage (device %x)", ®s, dev); 297} 298 299#if 0 300static void 301apm_devpowmgt_enable(int onoff, u_int dev) 302{ 303 struct bioscallregs regs; 304 305 if (apm_minver == 0) 306 return; 307 regs.BX = dev; 308 309 /* 310 * enable is auto BIOS management. 311 * disable is program control. 312 */ 313 regs.CX = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE; 314 if (apmcall(APM_DEVICE_MGMT_ENABLE, ®s) != 0) 315 printf("APM device engage (device %x): %s (%d)\n", 316 dev, apm_strerror(APM_ERR_CODE(®s)), 317 APM_ERR_CODE(®s)); 318} 319#endif 320 321 322static int 323apm_get_ver(struct apm_softc *self) 324{ 325 struct bioscallregs regs; 326 327 regs.CX = 0x0102; /* APM Version 1.2 */ 328 regs.BX = APM_DEV_APM_BIOS; 329 330 if (apm_v12_enabled && apmcall(APM_DRIVER_VERSION, ®s) == 0) 331 return 0x0102; 332 333 regs.CX = 0x0101; /* APM Version 1.1 */ 334 regs.BX = APM_DEV_APM_BIOS; 335 336 if (apm_v11_enabled && apmcall(APM_DRIVER_VERSION, ®s) == 0) 337 return 0x0101; 338 else 339 return 0x0100; 340} 341 342static int 343apm_get_powstat(void *c, u_int batteryid, struct apm_power_info *pi) 344{ 345 struct bioscallregs regs; 346 int error; 347 u_int nbattery = 0, caps; 348 349 if (apm_minver >= 2) { 350 apm_get_capabilities(®s, &nbattery, &caps); 351 if (batteryid > nbattery) 352 return EIO; 353 } else { 354 if (batteryid > 0) 355 return EIO; 356 nbattery = 0; 357 } 358 359 if (batteryid == 0) 360 regs.BX = APM_DEV_ALLDEVS; 361 else 362 regs.BX = APM_DEV_BATTERY(batteryid); 363 364 if ((error = apmcall(APM_POWER_STATUS, ®s)) != 0) 365 return regs.AX >> 8; 366 367 pi->batteryid = batteryid; 368 pi->nbattery = nbattery; 369 pi->battery_life = APM_BATT_LIFE(®s); 370 pi->ac_state = APM_AC_STATE(®s); 371 pi->battery_state = APM_BATT_STATE(®s); 372 pi->battery_flags = APM_BATT_FLAGS(®s); 373 pi->minutes_valid = APM_BATT_REM_VALID(®s); 374 if (pi->minutes_valid) 375 pi->minutes_left = APM_BATT_REMAINING(®s); 376 else 377 pi->minutes_left = 0; 378 return 0; 379} 380 381/* XXX cgd: this doesn't belong here. */ 382#define I386_FLAGBITS "\020\017NT\014OVFL\0130UP\012IEN\011TF\010NF\007ZF\005AF\003PF\001CY" 383 384int 385apm_busprobe(void) 386{ 387 struct bioscallregs regs; 388#ifdef APM_USE_KVM86 389 int res; 390#endif 391#ifdef APMDEBUG 392 char bits[128]; 393#endif 394 395 memset(®s, 0, sizeof(struct bioscallregs)); 396 regs.AX = APM_BIOS_FN(APM_INSTALLATION_CHECK); 397 regs.BX = APM_DEV_APM_BIOS; 398#ifdef APM_USE_KVM86 399 res = kvm86_bioscall_simple(APM_SYSTEM_BIOS, ®s); 400 if (res) { 401 printf("apm_busprobe: kvm86 error\n"); 402 return (0); 403 } 404#else 405 bioscall(APM_SYSTEM_BIOS, ®s); 406#endif 407 DPRINTF(APMDEBUG_PROBE, ("apm: bioscall return: %x %x %x %x %s %x %x\n", 408 regs.AX, regs.BX, regs.CX, regs.DX, 409 (snprintb(bits, sizeof(bits), I386_FLAGBITS, regs.EFLAGS), bits), 410 regs.ESI, regs.EDI)); 411 412 if (regs.FLAGS & PSL_C) { 413 DPRINTF(APMDEBUG_PROBE, ("apm: carry set means no APM bios\n")); 414 return 0; /* no carry -> not installed */ 415 } 416 if (regs.BX != APM_INSTALL_SIGNATURE) { 417 DPRINTF(APMDEBUG_PROBE, ("apm: PM signature not found\n")); 418 return 0; 419 } 420 if ((regs.CX & APM_32BIT_SUPPORT) == 0) { 421 DPRINTF(APMDEBUG_PROBE, ("apm: no 32bit support (busprobe)\n")); 422 return 0; 423 } 424 425 return 1; /* OK to continue probe & complain if something fails */ 426} 427 428static int 429apmbiosmatch(device_t parent, cfdata_t match, void *aux) 430{ 431 /* There can be only one! */ 432 if (apm_inited) 433 return 0; 434 435 /* 436 * apm_busprobe() said 'go' or we wouldn't be here. 437 * APM might not be useful (or might be too weird) 438 * on this machine, but that's handled in attach. 439 * 440 * The apm_enabled global variable is used to allow 441 * users to patch kernels to disable APM support. 442 */ 443 if (apm_enabled) { 444 if (apm_match() == 0) { 445 apm_disconnect(NULL); 446 return 0; 447 } else 448 return 1; 449 } 450 return 0; 451} 452 453#ifdef APMDEBUG 454#define DPRINTF_BIOSRETURN(regs, bits) \ 455 do { \ 456 snprintb(bits, sizeof(bits), I386_FLAGBITS, (regs).EFLAGS); \ 457 DPRINTF(APMDEBUG_ATTACH, \ 458 ("bioscall return: %x %x %x %x %s %x %x", \ 459 (regs).EAX, (regs).EBX, (regs).ECX, (regs).EDX, \ 460 bits, (regs).ESI, (regs).EDI)); \ 461 } while (/*CONSTCOND*/0) 462#else 463#define DPRINTF_BIOSRETURN(regs, bits) 464#endif 465 466static void 467apmbiosattach(device_t parent, device_t self, void *aux) 468{ 469 struct apm_softc *apmsc = device_private(self); 470 struct bioscallregs regs; 471 int apm_data_seg_ok; 472 u_int okbases[] = { 0, biosbasemem*1024 }; 473 u_int oklimits[] = { PAGE_SIZE, IOM_END}; 474 u_int i; 475 int vers; 476#ifdef APM_USE_KVM86 477 int res; 478#endif 479#ifdef APMDEBUG 480 char bits[128]; 481#endif 482 483 aprint_naive(": Power management\n"); 484 aprint_normal(": Advanced Power Management BIOS"); 485 486 apmsc->sc_dev = self; 487 488 memset(®s, 0, sizeof(struct bioscallregs)); 489 regs.AX = APM_BIOS_FN(APM_INSTALLATION_CHECK); 490 regs.BX = APM_DEV_APM_BIOS; 491#ifdef APM_USE_KVM86 492 res = kvm86_bioscall_simple(APM_SYSTEM_BIOS, ®s); 493 if (res) { 494 aprint_error_dev(self, 495 "kvm86 error (APM_INSTALLATION_CHECK)\n"); 496 goto bail_disconnected; 497 } 498#else 499 bioscall(APM_SYSTEM_BIOS, ®s); 500#endif 501 DPRINTF_BIOSRETURN(regs, bits); 502 DPRINTF(APMDEBUG_ATTACH, ("\n%s: ", device_xname(self))); 503 504 apminfo.apm_detail = (u_int)regs.AX | ((u_int)regs.CX << 16); 505 506 /* 507 * call a disconnect in case it was already connected 508 * by some previous code. 509 */ 510 memset(®s, 0, sizeof(struct bioscallregs)); 511 regs.AX = APM_BIOS_FN(APM_DISCONNECT); 512 regs.BX = APM_DEV_APM_BIOS; 513#ifdef APM_USE_KVM86 514 res = kvm86_bioscall_simple(APM_SYSTEM_BIOS, ®s); 515 if (res) { 516 aprint_error_dev(self, "kvm86 error (APM_DISCONNECT)\n"); 517 goto bail_disconnected; 518 } 519#else 520 bioscall(APM_SYSTEM_BIOS, ®s); 521#endif 522 DPRINTF_BIOSRETURN(regs, bits); 523 DPRINTF(APMDEBUG_ATTACH, ("\n%s: ", device_xname(self))); 524 525 if ((apminfo.apm_detail & APM_32BIT_SUPPORTED) == 0) { 526 aprint_error_dev(self, "no 32-bit APM support\n"); 527 goto bail_disconnected; 528 } 529 530 /* 531 * And connect to it. 532 */ 533 memset(®s, 0, sizeof(struct bioscallregs)); 534 regs.AX = APM_BIOS_FN(APM_32BIT_CONNECT); 535 regs.BX = APM_DEV_APM_BIOS; 536#ifdef APM_USE_KVM86 537 res = kvm86_bioscall_simple(APM_SYSTEM_BIOS, ®s); 538 if (res) { 539 aprint_error_dev(self, 540 "kvm86 error (APM_32BIT_CONNECT)\n"); 541 goto bail_disconnected; 542 } 543#else 544 bioscall(APM_SYSTEM_BIOS, ®s); 545#endif 546 DPRINTF_BIOSRETURN(regs, bits); 547 DPRINTF(APMDEBUG_ATTACH, ("\n%s: ", device_xname(self))); 548 549 apminfo.apm_code32_seg_base = regs.AX << 4; 550 apminfo.apm_entrypt = regs.BX; /* spec says EBX, can't map >=64k */ 551 apminfo.apm_code16_seg_base = regs.CX << 4; 552 apminfo.apm_data_seg_base = regs.DX << 4; 553 apminfo.apm_code32_seg_len = regs.SI; 554 apminfo.apm_code16_seg_len = regs.SI_HI; 555 apminfo.apm_data_seg_len = regs.DI; 556 557 vers = (APM_MAJOR_VERS(apminfo.apm_detail) << 8) + 558 APM_MINOR_VERS(apminfo.apm_detail); 559 if (apm_force_64k_segments) { 560 apminfo.apm_code32_seg_len = 65536; 561 apminfo.apm_code16_seg_len = 65536; 562 apminfo.apm_data_seg_len = 65536; 563 } else { 564 switch (vers) { 565 case 0x0100: 566 apminfo.apm_code32_seg_len = 65536; 567 apminfo.apm_code16_seg_len = 65536; 568 apminfo.apm_data_seg_len = 65536; 569 apm_v11_enabled = 0; 570 apm_v12_enabled = 0; 571 break; 572 case 0x0101: 573 apminfo.apm_code16_seg_len = apminfo.apm_code32_seg_len; 574 apm_v12_enabled = 0; 575 /* fall through */ 576 case 0x0102: 577 default: 578 if (apminfo.apm_code32_seg_len == 0) { 579 /* 580 * some BIOSes are lame, even if v1.1. 581 * (Or maybe they want 64k even though they can 582 * only ask for 64k-1?) 583 */ 584 apminfo.apm_code32_seg_len = 65536; 585 DPRINTF(APMDEBUG_ATTACH, 586 ("lame v%d.%d bios gave zero len code32, pegged to 64k\n%s: ", 587 APM_MAJOR_VERS(apminfo.apm_detail), 588 APM_MINOR_VERS(apminfo.apm_detail), 589 device_xname(self))); 590 } 591 if (apminfo.apm_code16_seg_len == 0) { 592 /* 593 * some BIOSes are lame, even if v1.1. 594 * (Or maybe they want 64k even though they can 595 * only ask for 64k-1?) 596 */ 597 apminfo.apm_code16_seg_len = 65536; 598 DPRINTF(APMDEBUG_ATTACH, 599 ("lame v%d.%d bios gave zero len code16, pegged to 64k\n%s: ", 600 APM_MAJOR_VERS(apminfo.apm_detail), 601 APM_MINOR_VERS(apminfo.apm_detail), 602 device_xname(self))); 603 } 604 if (apminfo.apm_data_seg_len == 0) { 605 /* 606 * some BIOSes are lame, even if v1.1. 607 * 608 * leave it alone and assume it does not 609 * want any sensible data segment 610 * mapping, and mark as bogus (but with 611 * expanded size, in case it's in some place 612 * that costs us nothing to map). 613 */ 614 apm_bogus_bios = 1; 615 apminfo.apm_data_seg_len = 65536; 616 DPRINTF(APMDEBUG_ATTACH, 617 ("lame v%d.%d bios gave zero len data, tentative 64k\n%s: ", 618 APM_MAJOR_VERS(apminfo.apm_detail), 619 APM_MINOR_VERS(apminfo.apm_detail), 620 device_xname(self))); 621 } 622 break; 623 } 624 } 625 if (apminfo.apm_code32_seg_len < apminfo.apm_entrypt + 4) { 626 DPRINTF(APMDEBUG_ATTACH, 627 ("nonsensical BIOS code length %d ignored (entry point offset is %d)\n%s: ", 628 apminfo.apm_code32_seg_len, 629 apminfo.apm_entrypt, 630 device_xname(self))); 631 apminfo.apm_code32_seg_len = 65536; 632 } 633 if (apminfo.apm_code32_seg_base < IOM_BEGIN || 634 apminfo.apm_code32_seg_base >= IOM_END) { 635 DPRINTF(APMDEBUG_ATTACH, ("code32 segment starts outside ISA hole [%x]\n%s: ", 636 apminfo.apm_code32_seg_base, device_xname(self))); 637 aprint_error_dev(self, 638 "bogus 32-bit code segment start\n"); 639 goto bail; 640 } 641 if (apminfo.apm_code32_seg_base + 642 apminfo.apm_code32_seg_len > IOM_END) { 643 DPRINTF(APMDEBUG_ATTACH, ("code32 segment oversized: [%x,%x)\n%s: ", 644 apminfo.apm_code32_seg_base, 645 apminfo.apm_code32_seg_base + apminfo.apm_code32_seg_len - 1, 646 device_xname(self))); 647#if 0 648 aprint_error_dev(self, "bogus 32-bit code segment size\n"); 649 goto bail; 650#else 651 apminfo.apm_code32_seg_len = 652 IOM_END - apminfo.apm_code32_seg_base; 653#endif 654 } 655 if (apminfo.apm_code16_seg_base < IOM_BEGIN || 656 apminfo.apm_code16_seg_base >= IOM_END) { 657 DPRINTF(APMDEBUG_ATTACH, ("code16 segment starts outside ISA hole [%x]\n%s: ", 658 apminfo.apm_code16_seg_base, device_xname(self))); 659 aprint_error_dev(self, 660 "bogus 16-bit code segment start\n"); 661 goto bail; 662 } 663 if (apminfo.apm_code16_seg_base + 664 apminfo.apm_code16_seg_len > IOM_END) { 665 DPRINTF(APMDEBUG_ATTACH, 666 ("code16 segment oversized: [%x,%x), giving up\n%s: ", 667 apminfo.apm_code16_seg_base, 668 apminfo.apm_code16_seg_base + apminfo.apm_code16_seg_len - 1, 669 device_xname(self))); 670 /* 671 * give up since we may have to trash the 672 * 32bit segment length otherwise. 673 */ 674 aprint_error_dev(self, "bogus 16-bit code segment size\n"); 675 goto bail; 676 } 677 /* 678 * allow data segment to be zero length, within ISA hole or 679 * at page zero or above biosbasemem and below ISA hole end. 680 * truncate it if it doesn't quite fit in the space 681 * we allow. 682 * 683 * Otherwise, give up if not "apm_bogus_bios". 684 */ 685 apm_data_seg_ok = 0; 686 for (i = 0; i < 2; i++) { 687 if (apminfo.apm_data_seg_base >= okbases[i] && 688 apminfo.apm_data_seg_base < oklimits[i]-1) { 689 /* starts OK */ 690 if (apminfo.apm_data_seg_base + 691 apminfo.apm_data_seg_len > oklimits[i]) { 692 DPRINTF(APMDEBUG_ATTACH, 693 ("data segment oversized: [%x,%x)", 694 apminfo.apm_data_seg_base, 695 apminfo.apm_data_seg_base + apminfo.apm_data_seg_len)); 696 apminfo.apm_data_seg_len = 697 oklimits[i] - apminfo.apm_data_seg_base; 698 DPRINTF(APMDEBUG_ATTACH, 699 ("; resized to [%x,%x)\n%s: ", 700 apminfo.apm_data_seg_base, 701 apminfo.apm_data_seg_base + apminfo.apm_data_seg_len, 702 device_xname(self))); 703 } else { 704 DPRINTF(APMDEBUG_ATTACH, 705 ("data segment fine: [%x,%x)\n%s: ", 706 apminfo.apm_data_seg_base, 707 apminfo.apm_data_seg_base + apminfo.apm_data_seg_len, 708 device_xname(self))); 709 } 710 apm_data_seg_ok = 1; 711 break; 712 } 713 } 714 if (!apm_data_seg_ok && apm_bogus_bios) { 715 if (apm_allow_bogus_segments) { 716 DPRINTF(APMDEBUG_ATTACH, 717 ("bogus bios data seg location, continuing\n%s: ", 718 device_xname(self))); 719 } else { 720 DPRINTF(APMDEBUG_ATTACH, 721 ("bogus bios data seg location, ignoring\n%s: ", 722 device_xname(self))); 723 apminfo.apm_data_seg_base = 0; 724 apminfo.apm_data_seg_len = 0; 725 } 726 apm_data_seg_ok = 1; /* who are we kidding?! */ 727 } 728 if (!apm_data_seg_ok) { 729 DPRINTF(APMDEBUG_ATTACH, 730 ("data segment [%x,%x) not in an available location\n%s: ", 731 apminfo.apm_data_seg_base, 732 apminfo.apm_data_seg_base + apminfo.apm_data_seg_len, 733 device_xname(self))); 734 aprint_error_dev(self, "data segment unavailable\n"); 735 goto bail; 736 } 737 738 /* 739 * set up GDT descriptors for APM 740 */ 741 apminfo.apm_segsel = GSEL(GAPM32CODE_SEL,SEL_KPL); 742 743 /* 744 * Some bogus APM V1.1 BIOSes do not return any 745 * size limits in the registers they are supposed to. 746 * We forced them to zero before calling the BIOS 747 * (see apm_init.S), so if we see zero limits here 748 * we assume that means they should be 64k (and trimmed 749 * if needed for legitimate memory needs). 750 */ 751 DPRINTF(APMDEBUG_ATTACH, ("code32len=%x, datalen=%x\n%s: ", 752 apminfo.apm_code32_seg_len, 753 apminfo.apm_data_seg_len, 754 device_xname(self))); 755 setgdt(GAPM32CODE_SEL, ISA_HOLE_VADDR(apminfo.apm_code32_seg_base), 756 apminfo.apm_code32_seg_len - 1, 757 SDT_MEMERA, SEL_KPL, 1, 0); 758#ifdef GAPM16CODE_SEL 759 setgdt(GAPM16CODE_SEL, ISA_HOLE_VADDR(apminfo.apm_code16_seg_base), 760 apminfo.apm_code16_seg_len - 1, 761 SDT_MEMERA, SEL_KPL, 0, 0); 762#endif 763 if (apminfo.apm_data_seg_len == 0) { 764 /* 765 *if no data area needed, set up the segment 766 * descriptor to just the first byte of the code 767 * segment, read only. 768 */ 769 setgdt(GAPMDATA_SEL, 770 ISA_HOLE_VADDR(apminfo.apm_code32_seg_base), 771 0, SDT_MEMROA, SEL_KPL, 0, 0); 772 } else if (apminfo.apm_data_seg_base < IOM_BEGIN) { 773 bus_space_handle_t memh; 774 775 /* 776 * need page zero or biosbasemem area mapping. 777 * 778 * XXX cheat and use knowledge of bus_space_map() 779 * implementation on i386 so it can be done without 780 * extent checking. 781 */ 782 if (_x86_memio_map(x86_bus_space_mem, 783 apminfo.apm_data_seg_base, 784 apminfo.apm_data_seg_len, 0, &memh)) { 785 aprint_error_dev(self, 786 "couldn't map data segment\n"); 787 goto bail; 788 } 789 DPRINTF(APMDEBUG_ATTACH, 790 ("mapping bios data area %x @ 0x%lx\n%s: ", 791 apminfo.apm_data_seg_base, memh, 792 device_xname(self))); 793 setgdt(GAPMDATA_SEL, (void *)memh, 794 apminfo.apm_data_seg_len - 1, 795 SDT_MEMRWA, SEL_KPL, 1, 0); 796 } else 797 setgdt(GAPMDATA_SEL, ISA_HOLE_VADDR(apminfo.apm_data_seg_base), 798 apminfo.apm_data_seg_len - 1, 799 SDT_MEMRWA, SEL_KPL, 1, 0); 800 801 DPRINTF(APMDEBUG_ATTACH, 802 ("detail %x 32b:%x/%p/%x 16b:%x/%p/%x data %x/%p/%x ep %x (%x:%p) %p\n%s: ", 803 apminfo.apm_detail, 804 apminfo.apm_code32_seg_base, 805 ISA_HOLE_VADDR(apminfo.apm_code32_seg_base), 806 apminfo.apm_code32_seg_len, 807 apminfo.apm_code16_seg_base, 808 ISA_HOLE_VADDR(apminfo.apm_code16_seg_base), 809 apminfo.apm_code16_seg_len, 810 apminfo.apm_data_seg_base, 811 ISA_HOLE_VADDR(apminfo.apm_data_seg_base), 812 apminfo.apm_data_seg_len, 813 apminfo.apm_entrypt, 814 apminfo.apm_segsel, 815 apminfo.apm_entrypt + 816 (char *)ISA_HOLE_VADDR(apminfo.apm_code32_seg_base), 817 &apminfo.apm_segsel, 818 device_xname(self))); 819 820 apmsc->sc_ops = &apm_accessops; 821 apmsc->sc_cookie = apmsc; 822 apmsc->sc_vers = apm_get_ver(apmsc); 823 apmsc->sc_detail = apminfo.apm_detail; 824 apmsc->sc_hwflags = 0; 825 apm_attach(apmsc); 826 827 return; 828 829bail: 830 /* 831 * call a disconnect; we're punting. 832 */ 833 apm_disconnect(apmsc); 834bail_disconnected: 835 aprint_normal_dev(self, "kernel APM support disabled\n"); 836} 837 838static void 839apm_enable(void *sc, int on) 840{ 841 /* 842 * XXX some bogus APM BIOSes don't set the disabled bit in 843 * the connect state, yet still complain about the functions 844 * being disabled when other calls are made. sigh. 845 */ 846 if (apminfo.apm_detail & APM_BIOS_PM_DISABLED) 847 apm_powmgt_enable(on); 848 849 /* 850 * Engage cooperative power mgt (we get to do it) 851 * on all devices (v1.1). 852 */ 853 apm_powmgt_engage(on, APM_DEV_ALLDEVS); 854} 855 856void 857apm_disconnect(void *arg) 858{ 859 struct apm_softc *sc = arg; 860 struct bioscallregs regs; 861#ifdef APMDEBUG 862 char bits[128]; 863#endif 864 /* 865 * We were unable to create the APM thread; bail out. 866 */ 867 memset(®s, 0, sizeof(struct bioscallregs)); 868 regs.AX = APM_BIOS_FN(APM_DISCONNECT); 869 regs.BX = APM_DEV_APM_BIOS; 870#ifdef APM_USE_KVM86 871 (void)kvm86_bioscall_simple(APM_SYSTEM_BIOS, ®s); 872#else 873 bioscall(APM_SYSTEM_BIOS, ®s); 874#endif 875 if (sc == NULL) 876 return; 877 DPRINTF(APMDEBUG_ATTACH, ("\n%s: ", device_xname(sc->sc_dev))); 878 DPRINTF_BIOSRETURN(regs, bits); 879 aprint_error_dev(sc->sc_dev, 880 "unable to create thread, kernel APM support disabled\n"); 881} 882 883static int 884apm_get_event(void *sc, u_int *event_code, u_int *event_info) 885{ 886 int error; 887 struct bioscallregs regs; 888 889 if ((error = apmcall(APM_GET_PM_EVENT, ®s)) != 0) 890 return regs.AX >> 8; 891 *event_code = regs.BX; 892 *event_info = regs.AX; 893 return 0; 894} 895 896int 897apm_set_powstate(void *sc, u_int dev, u_int state) 898{ 899 struct bioscallregs regs; 900 if (!apm_inited || (apm_minver == 0 && state > APM_SYS_OFF)) 901 return EINVAL; 902 903 regs.BX = dev; 904 regs.CX = state; 905 if (apmcall(APM_SET_PWR_STATE, ®s) != 0) { 906 apm_perror("set power state <%x,%x>", ®s, dev, state); 907 return regs.AX >> 8; 908 } 909 return 0; 910} 911 912static void 913apm_cpu_busy(void *sc) 914{ 915 struct bioscallregs regs; 916 917 if (!apm_inited || !apm_do_idle) 918 return; 919 if ((apminfo.apm_detail & APM_IDLE_SLOWS) && 920 apmcall(APM_CPU_BUSY, ®s) != 0) { 921 /* 922 * XXX BIOSes use to set carry without valid 923 * error number 924 */ 925#ifdef APMDEBUG 926 apm_perror("set CPU busy", ®s); 927#endif 928 } 929} 930 931static void 932apm_cpu_idle(void *sc) 933{ 934 struct bioscallregs regs; 935 936 if (!apm_inited || !apm_do_idle) 937 return; 938 if (apmcall(APM_CPU_IDLE, ®s) != 0) { 939 /* 940 * XXX BIOSes use to set carry without valid 941 * error number 942 */ 943#ifdef APMDEBUG 944 apm_perror("set CPU idle", ®s); 945#endif 946 } 947} 948 949/* V1.2 */ 950static void 951apm_get_capabilities(void *sc, u_int *numbatts, u_int *capflags) 952{ 953 struct bioscallregs regs; 954 int error; 955 956 regs.BX = APM_DEV_APM_BIOS; 957 if ((error = apmcall(APM_GET_CAPABILITIES, ®s)) != 0) { 958 apm_perror("get capabilities", ®s); 959 return; 960 } 961 *capflags = regs.CX; 962 *numbatts = APM_NBATTERIES(®s); 963 964#ifdef APMDEBUG 965 /* print out stats */ 966 DPRINTF(APMDEBUG_INFO, ("apm: %d batteries", *numbatts)); 967 if (*capflags & APM_GLOBAL_STANDBY) 968 DPRINTF(APMDEBUG_INFO, (", global standby")); 969 if (*capflags & APM_GLOBAL_SUSPEND) 970 DPRINTF(APMDEBUG_INFO, (", global suspend")); 971 if (*capflags & APM_RTIMER_STANDBY) 972 DPRINTF(APMDEBUG_INFO, (", rtimer standby")); 973 if (*capflags & APM_RTIMER_SUSPEND) 974 DPRINTF(APMDEBUG_INFO, (", rtimer suspend")); 975 if (*capflags & APM_IRRING_STANDBY) 976 DPRINTF(APMDEBUG_INFO, (", internal standby")); 977 if (*capflags & APM_IRRING_SUSPEND) 978 DPRINTF(APMDEBUG_INFO, (", internal suspend")); 979 if (*capflags & APM_PCRING_STANDBY) 980 DPRINTF(APMDEBUG_INFO, (", pccard standby")); 981 if (*capflags & APM_PCRING_SUSPEND) 982 DPRINTF(APMDEBUG_INFO, (", pccard suspend")); 983 DPRINTF(APMDEBUG_INFO, ("\n")); 984#endif 985} 986 987#undef DPRINTF_BIOSRETURN 988