blob: fd71a01ef9c48a1b83dd7535223b93ab3115f985 [file] [log] [blame]
Kevin O'Connor95b2f782008-03-05 19:52:06 -05001// Basic support for apmbios callbacks.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2005 Struan Bartlett
5// Copyright (C) 2004 Fabrice Bellard
6//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05007// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor95b2f782008-03-05 19:52:06 -05008
Kevin O'Connor15157a32008-12-13 11:10:37 -05009#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "bregs.h" // struct bregs
11#include "config.h" // CONFIG_*
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040012#include "fw/paravirt.h" // runningOnQEMU
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "output.h" // dprintf
Kevin O'Connor3df600b2013-09-14 19:28:55 -040014#include "stacks.h" // yield_toirq
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040015#include "util.h" // apm_shutdown
Kevin O'Connor4ade5232013-09-18 21:41:48 -040016#include "x86.h" // outb
Kevin O'Connor95b2f782008-03-05 19:52:06 -050017
18static void
19out_str(const char *str_cs)
20{
Kevin O'Connor897fb112013-02-07 23:32:48 -050021 if (!runningOnQEMU()) {
Kevin O'Connorac8df8c2008-05-24 23:46:33 -040022 dprintf(1, "APM request '%s'\n", str_cs);
Kevin O'Connorf64f0db2008-05-18 02:42:58 -040023 return;
24 }
25
Kevin O'Connor95b2f782008-03-05 19:52:06 -050026 u8 *s = (u8*)str_cs;
27 for (;;) {
Kevin O'Connor15157a32008-12-13 11:10:37 -050028 u8 c = GET_GLOBAL(*s);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050029 if (!c)
30 break;
Kevin O'Connor2ad37442008-05-06 19:49:01 -040031 outb(c, PORT_BIOS_APM);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050032 s++;
33 }
34}
35
36// APM installation check
37static void
38handle_155300(struct bregs *regs)
39{
40 regs->ah = 1; // APM major version
41 regs->al = 2; // APM minor version
42 regs->bh = 'P';
43 regs->bl = 'M';
44 // bit 0 : 16 bit interface supported
45 // bit 1 : 32 bit interface supported
46 regs->cx = 0x03;
Kevin O'Connor6c781222008-03-09 12:19:23 -040047 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050048}
49
50// APM real mode interface connect
51static void
52handle_155301(struct bregs *regs)
53{
Kevin O'Connor6c781222008-03-09 12:19:23 -040054 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050055}
56
Kevin O'Connor44d65302008-03-08 11:34:46 -050057// Assembler entry points defined in romlayout.S
Kevin O'Connor47c8e312011-07-10 22:57:32 -040058extern void entry_apm16(void);
59extern void entry_apm32(void);
Kevin O'Connor44d65302008-03-08 11:34:46 -050060
Kevin O'Connor95b2f782008-03-05 19:52:06 -050061// APM 16 bit protected mode interface connect
62static void
63handle_155302(struct bregs *regs)
64{
Kevin O'Connor47c8e312011-07-10 22:57:32 -040065 regs->bx = (u32)entry_apm16;
Kevin O'Connor95b2f782008-03-05 19:52:06 -050066 regs->ax = SEG_BIOS; // 16 bit code segment base
67 regs->si = 0xfff0; // 16 bit code segment size
68 regs->cx = SEG_BIOS; // data segment address
69 regs->di = 0xfff0; // data segment length
Kevin O'Connor6c781222008-03-09 12:19:23 -040070 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050071}
72
73// APM 32 bit protected mode interface connect
74static void
75handle_155303(struct bregs *regs)
76{
Kevin O'Connor44d65302008-03-08 11:34:46 -050077 regs->ax = SEG_BIOS; // 32 bit code segment base
Kevin O'Connor47c8e312011-07-10 22:57:32 -040078 regs->ebx = (u32)entry_apm32;
Kevin O'Connor44d65302008-03-08 11:34:46 -050079 regs->cx = SEG_BIOS; // 16 bit code segment base
Kevin O'Connor95b2f782008-03-05 19:52:06 -050080 // 32 bit code segment size (low 16 bits)
81 // 16 bit code segment size (high 16 bits)
82 regs->esi = 0xfff0fff0;
Kevin O'Connor44d65302008-03-08 11:34:46 -050083 regs->dx = SEG_BIOS; // data segment address
Kevin O'Connor95b2f782008-03-05 19:52:06 -050084 regs->di = 0xfff0; // data segment length
Kevin O'Connor6c781222008-03-09 12:19:23 -040085 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050086}
87
88// APM interface disconnect
89static void
90handle_155304(struct bregs *regs)
91{
Kevin O'Connor6c781222008-03-09 12:19:23 -040092 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050093}
94
95// APM cpu idle
96static void
97handle_155305(struct bregs *regs)
98{
Kevin O'Connor94c749c2012-05-28 11:44:02 -040099 yield_toirq();
Kevin O'Connor6c781222008-03-09 12:19:23 -0400100 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500101}
102
Kevin O'Connor0fae9e12008-06-07 14:51:14 -0400103// APM cpu busy
104static void
105handle_155306(struct bregs *regs)
106{
107 set_success(regs);
108}
109
Kevin O'Connor244caf82010-09-15 21:48:16 -0400110void
111apm_shutdown(void)
112{
Gerd Hoffmann5b631092013-07-25 09:47:18 +0200113 u16 pm1a_cnt = GET_GLOBAL(acpi_pm1a_cnt);
114
Kevin O'Connor244caf82010-09-15 21:48:16 -0400115 irq_disable();
Gerd Hoffmann5b631092013-07-25 09:47:18 +0200116 if (pm1a_cnt)
117 outw(0x2000, pm1a_cnt);
Kevin O'Connor244caf82010-09-15 21:48:16 -0400118 out_str("Shutdown");
119 for (;;)
120 hlt();
121}
122
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500123// APM Set Power State
124static void
125handle_155307(struct bregs *regs)
126{
127 if (regs->bx != 1) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400128 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500129 return;
130 }
131 switch (regs->cx) {
132 case 1:
133 out_str("Standby");
134 break;
135 case 2:
136 out_str("Suspend");
137 break;
138 case 3:
Kevin O'Connor244caf82010-09-15 21:48:16 -0400139 apm_shutdown();
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500140 break;
141 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400142 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500143}
144
145static void
146handle_155308(struct bregs *regs)
147{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400148 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500149}
150
151// Get Power Status
152static void
153handle_15530a(struct bregs *regs)
154{
155 regs->bh = 0x01; // on line
156 regs->bl = 0xff; // unknown battery status
157 regs->ch = 0x80; // no system battery
158 regs->cl = 0xff; // unknown remaining time
159 regs->dx = 0xffff; // unknown remaining time
160 regs->si = 0x00; // zero battery
Kevin O'Connor6c781222008-03-09 12:19:23 -0400161 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500162}
163
Kevin O'Connor0fae9e12008-06-07 14:51:14 -0400164#define RET_ENOEVENT 0x80
165
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500166// Get PM Event
167static void
168handle_15530b(struct bregs *regs)
169{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500170 set_code_invalid_silent(regs, RET_ENOEVENT);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500171}
172
173// APM Driver Version
174static void
175handle_15530e(struct bregs *regs)
176{
177 regs->ah = 1;
178 regs->al = 2;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400179 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500180}
181
182// APM Engage / Disengage
183static void
184handle_15530f(struct bregs *regs)
185{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400186 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500187}
188
189// APM Get Capabilities
190static void
191handle_155310(struct bregs *regs)
192{
193 regs->bl = 0;
194 regs->cx = 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400195 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500196}
197
198static void
199handle_1553XX(struct bregs *regs)
200{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500201 set_unimplemented(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500202}
203
Kevin O'Connorc0031482010-01-01 13:03:17 -0500204void
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500205handle_1553(struct bregs *regs)
206{
Kevin O'Connor7ba6b302008-05-26 15:19:33 -0400207 if (! CONFIG_APMBIOS) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500208 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor7ba6b302008-05-26 15:19:33 -0400209 return;
210 }
211
Kevin O'Connorc38e4802008-03-15 11:07:50 -0400212 //debug_stub(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500213 switch (regs->al) {
214 case 0x00: handle_155300(regs); break;
215 case 0x01: handle_155301(regs); break;
216 case 0x02: handle_155302(regs); break;
217 case 0x03: handle_155303(regs); break;
218 case 0x04: handle_155304(regs); break;
219 case 0x05: handle_155305(regs); break;
Kevin O'Connor0fae9e12008-06-07 14:51:14 -0400220 case 0x06: handle_155306(regs); break;
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500221 case 0x07: handle_155307(regs); break;
222 case 0x08: handle_155308(regs); break;
223 case 0x0a: handle_15530a(regs); break;
224 case 0x0b: handle_15530b(regs); break;
225 case 0x0e: handle_15530e(regs); break;
226 case 0x0f: handle_15530f(regs); break;
227 case 0x10: handle_155310(regs); break;
228 default: handle_1553XX(regs); break;
229 }
230}
Kevin O'Connorc0031482010-01-01 13:03:17 -0500231
Kevin O'Connord3e43672012-05-28 11:37:53 -0400232void VISIBLE16 VISIBLE32SEG
233handle_apm(struct bregs *regs)
Kevin O'Connorc0031482010-01-01 13:03:17 -0500234{
235 debug_enter(regs, DEBUG_HDL_apm);
236 handle_1553(regs);
237}