Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * Interface to Advanced Power Management 1.2 BIOS
3
 *
4
 * This is, in many ways, a giant hack, and when things settle down 
5
 * a bit and standardize, hopefully we can write a driver that deals
6
 * more directly with the hardware and thus might be a bit cleaner.
7
 * 
8
 * ACPI might be the answer, but at the moment this is simpler
9
 * and more widespread.
10
 */
11
 
12
#include	"u.h"
13
#include	"../port/lib.h"
14
#include	"mem.h"
15
#include	"dat.h"
16
#include	"fns.h"
17
#include	"io.h"
18
#include	"ureg.h"
19
 
20
extern int apmfarcall(ushort, ulong, Ureg*);		/* apmjump.s */
21
 
22
static int
23
getreg(ulong *reg, ISAConf *isa, char *name)
24
{
25
	int i;
26
	int nl;
27
 
28
	nl = strlen(name);
29
	for(i=0; i<isa->nopt; i++){
30
		if(cistrncmp(isa->opt[i], name, nl)==0 && isa->opt[i][nl] == '='){
31
			*reg = strtoul(isa->opt[i]+nl+1, nil, 16);
32
			return 0;
33
		}
34
	}
35
	return -1;
36
}
37
 
38
/*
39
 * Segment descriptors look like this.
40
 *
41
 * d1: [base 31:24] [gran] [is32bit] [0] [unused] [limit 19:16] 
42
		[present] [privlev] [type 3:0] [base 23:16]
43
 * d0: [base 15:00] [limit 15:00]
44
 *
45
 * gran is 0 for 1-byte granularity, 1 for 4k granularity
46
 * type is 0 for system segment, 1 for code/data.
47
 *
48
 * clearly we know way too much about the memory unit.
49
 * however, knowing this much about the memory unit
50
 * means that the memory unit need not know anything
51
 * about us.
52
 *
53
 * what a crock.
54
 */
55
static void
56
setgdt(int sel, ulong base, ulong limit, int flag)
57
{
58
	if(sel < 0 || sel >= NGDT)
59
		panic("setgdt");
60
 
61
	base = (ulong)KADDR(base);
62
	m->gdt[sel].d0 = (base<<16) | (limit&0xFFFF);
63
	m->gdt[sel].d1 = (base&0xFF000000) | (limit&0x000F0000) |
64
			((base>>16)&0xFF) | SEGP | SEGPL(0) | flag;
65
}
66
 
67
static	ulong ax, cx, dx, di, ebx, esi;
68
static Ureg apmu;
69
static long
70
apmread(Chan*, void *a, long n, vlong off)
71
{
72
	if(off < 0)
73
		error("badarg");
74
 
75
	if(n+off > sizeof apmu)
76
		n = sizeof apmu - off;
77
	if(n <= 0)
78
		return 0;
79
	memmove(a, (char*)&apmu+off, n);
80
	return n;
81
}
82
 
83
static long
84
apmwrite(Chan*, void *a, long n, vlong off)
85
{
86
	int s;
87
	if(off || n != sizeof apmu)
88
		error("write a Ureg");
89
 
90
	memmove(&apmu, a, sizeof apmu);
91
	s = splhi();
92
	apmfarcall(APMCSEL, ebx, &apmu);
93
	splx(s);
94
	return n;
95
}
96
 
97
void
98
apmlink(void)
99
{
100
	ISAConf isa;
101
	char *s;
102
 
103
	if(isaconfig("apm", 0, &isa) == 0)
104
		return;
105
 
106
/* XXX use realmode() */
107
 
108
	/*
109
	 * APM info passed from boot loader.
110
	 * Now we need to set up the GDT entries for APM.
111
	 *
112
	 * AX = 32-bit code segment base address
113
	 * EBX = 32-bit code segment offset
114
	 * CX = 16-bit code segment base address
115
	 * DX = 32-bit data segment base address
116
	 * ESI = <16-bit code segment length> <32-bit code segment length> (hi then lo)
117
	 * DI = 32-bit data segment length
118
	 */
119
 
120
	if(getreg(&ax, &isa, s="ax") < 0
121
	|| getreg(&ebx, &isa, s="ebx") < 0
122
	|| getreg(&cx, &isa, s="cx") < 0
123
	|| getreg(&dx, &isa, s="dx") < 0
124
	|| getreg(&esi, &isa, s="esi") < 0
125
	|| getreg(&di, &isa, s="di") < 0){
126
		print("apm: missing register %s\n", s);
127
		return;
128
	}
129
 
130
	/*
131
	 * The NEC Versa SX bios does not report the correct 16-bit code
132
	 * segment length when loaded directly from mbr -> 9load (as compared
133
	 * with going through ld.com).  We'll make both code segments 64k-1 bytes.
134
	 */
135
	esi = 0xFFFFFFFF;
136
 
137
	/*
138
	 * We are required by the BIOS to set up three consecutive segments,
139
	 * one for the APM 32-bit code, one for the APM 16-bit code, and 
140
	 * one for the APM data.  The BIOS handler uses the code segment it
141
	 * get called with to determine the other two segment selector.
142
	 */
143
	setgdt(APMCSEG, ax<<4, ((esi&0xFFFF)-1)&0xFFFF, SEGEXEC|SEGR|SEGD);
144
	setgdt(APMCSEG16, cx<<4, ((esi>>16)-1)&0xFFFF, SEGEXEC|SEGR);
145
	setgdt(APMDSEG, dx<<4, (di-1)&0xFFFF, SEGDATA|SEGW|SEGD);
146
 
147
	addarchfile("apm", 0660, apmread, apmwrite);
148
 
149
print("apm0: configured cbase %.8lux off %.8lux\n", ax<<4, ebx);
150
 
151
	return;
152
}
153