APM fixes; don't save/clear/restore %esp high bits.
[seabios.git] / src / apm.c
1 // 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 //
7 // This file may be distributed under the terms of the GNU GPLv3 license.
8
9 #include "farptr.h" // GET_VAR
10 #include "biosvar.h" // struct bregs
11 #include "ioport.h" // outb
12 #include "util.h" // irq_enable
13
14 static void
15 out_str(const char *str_cs)
16 {
17     if (CONFIG_COREBOOT) {
18         dprintf(1, "APM request '%s'\n", str_cs);
19         return;
20     }
21
22     u8 *s = (u8*)str_cs;
23     for (;;) {
24         u8 c = GET_VAR(CS, *s);
25         if (!c)
26             break;
27         outb(c, PORT_BIOS_APM);
28         s++;
29     }
30 }
31
32 // APM installation check
33 static void
34 handle_155300(struct bregs *regs)
35 {
36     regs->ah = 1; // APM major version
37     regs->al = 2; // APM minor version
38     regs->bh = 'P';
39     regs->bl = 'M';
40     // bit 0 : 16 bit interface supported
41     // bit 1 : 32 bit interface supported
42     regs->cx = 0x03;
43     set_success(regs);
44 }
45
46 // APM real mode interface connect
47 static void
48 handle_155301(struct bregs *regs)
49 {
50     set_success(regs);
51 }
52
53 // Assembler entry points defined in romlayout.S
54 extern void apm16protected_entry();
55 extern void apm32protected_entry();
56
57 // APM 16 bit protected mode interface connect
58 static void
59 handle_155302(struct bregs *regs)
60 {
61     regs->bx = (u32)apm16protected_entry;
62     regs->ax = SEG_BIOS; // 16 bit code segment base
63     regs->si = 0xfff0;   // 16 bit code segment size
64     regs->cx = SEG_BIOS; // data segment address
65     regs->di = 0xfff0;   // data segment length
66     set_success(regs);
67 }
68
69 // APM 32 bit protected mode interface connect
70 static void
71 handle_155303(struct bregs *regs)
72 {
73     regs->ax = SEG_BIOS; // 32 bit code segment base
74     regs->ebx = (u32)apm32protected_entry;
75     regs->cx = SEG_BIOS; // 16 bit code segment base
76     // 32 bit code segment size (low 16 bits)
77     // 16 bit code segment size (high 16 bits)
78     regs->esi = 0xfff0fff0;
79     regs->dx = SEG_BIOS; // data segment address
80     regs->di = 0xfff0; // data segment length
81     set_success(regs);
82 }
83
84 // APM interface disconnect
85 static void
86 handle_155304(struct bregs *regs)
87 {
88     set_success(regs);
89 }
90
91 // APM cpu idle
92 static void
93 handle_155305(struct bregs *regs)
94 {
95     irq_enable();
96     hlt();
97     set_success(regs);
98 }
99
100 // APM cpu busy
101 static void
102 handle_155306(struct bregs *regs)
103 {
104     set_success(regs);
105 }
106
107 // APM Set Power State
108 static void
109 handle_155307(struct bregs *regs)
110 {
111     if (regs->bx != 1) {
112         set_success(regs);
113         return;
114     }
115     switch (regs->cx) {
116     case 1:
117         out_str("Standby");
118         break;
119     case 2:
120         out_str("Suspend");
121         break;
122     case 3:
123         irq_disable();
124         out_str("Shutdown");
125         for (;;)
126             hlt();
127         break;
128     }
129     set_success(regs);
130 }
131
132 static void
133 handle_155308(struct bregs *regs)
134 {
135     set_success(regs);
136 }
137
138 // Get Power Status
139 static void
140 handle_15530a(struct bregs *regs)
141 {
142     regs->bh = 0x01; // on line
143     regs->bl = 0xff; // unknown battery status
144     regs->ch = 0x80; // no system battery
145     regs->cl = 0xff; // unknown remaining time
146     regs->dx = 0xffff; // unknown remaining time
147     regs->si = 0x00; // zero battery
148     set_success(regs);
149 }
150
151 #define RET_ENOEVENT 0x80
152
153 // Get PM Event
154 static void
155 handle_15530b(struct bregs *regs)
156 {
157     set_code_fail_silent(regs, RET_ENOEVENT);
158 }
159
160 // APM Driver Version
161 static void
162 handle_15530e(struct bregs *regs)
163 {
164     regs->ah = 1;
165     regs->al = 2;
166     set_success(regs);
167 }
168
169 // APM Engage / Disengage
170 static void
171 handle_15530f(struct bregs *regs)
172 {
173     set_success(regs);
174 }
175
176 // APM Get Capabilities
177 static void
178 handle_155310(struct bregs *regs)
179 {
180     regs->bl = 0;
181     regs->cx = 0;
182     set_success(regs);
183 }
184
185 static void
186 handle_1553XX(struct bregs *regs)
187 {
188     set_fail(regs);
189 }
190
191 void VISIBLE16
192 handle_1553(struct bregs *regs)
193 {
194     if (! CONFIG_APMBIOS) {
195         set_code_fail(regs, RET_EUNSUPPORTED);
196         return;
197     }
198
199     //debug_stub(regs);
200     switch (regs->al) {
201     case 0x00: handle_155300(regs); break;
202     case 0x01: handle_155301(regs); break;
203     case 0x02: handle_155302(regs); break;
204     case 0x03: handle_155303(regs); break;
205     case 0x04: handle_155304(regs); break;
206     case 0x05: handle_155305(regs); break;
207     case 0x06: handle_155306(regs); break;
208     case 0x07: handle_155307(regs); break;
209     case 0x08: handle_155308(regs); break;
210     case 0x0a: handle_15530a(regs); break;
211     case 0x0b: handle_15530b(regs); break;
212     case 0x0e: handle_15530e(regs); break;
213     case 0x0f: handle_15530f(regs); break;
214     case 0x10: handle_155310(regs); break;
215     default:   handle_1553XX(regs); break;
216     }
217 }