Separate SMM code into its own file (smm.c from rombios32.c).
[seabios.git] / src / smm.c
1 // System Management Mode support (on emulators)
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2006 Fabrice Bellard
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "pci.h" // PCIDevice
9 #include "util.h" // wbinvd
10
11 asm(
12     ".global smm_relocation_start\n"
13     ".global smm_relocation_end\n"
14     ".global smm_code_start\n"
15     ".global smm_code_end\n"
16     "  .code16\n"
17
18     /* code to relocate SMBASE to 0xa0000 */
19     "smm_relocation_start:\n"
20     "  mov $0x38000 + 0x7efc, %ebx\n"
21     "  addr32 mov (%ebx), %al\n"  /* revision ID to see if x86_64 or x86 */
22     "  cmp $0x64, %al\n"
23     "  je 1f\n"
24     "  mov $0x38000 + 0x7ef8, %ebx\n"
25     "  jmp 2f\n"
26     "1:\n"
27     "  mov $0x38000 + 0x7f00, %ebx\n"
28     "2:\n"
29     "  movl $0xa0000, %eax\n"
30     "  addr32 movl %eax, (%ebx)\n"
31     /* indicate to the BIOS that the SMM code was executed */
32     "  mov $0x00, %al\n"
33     "  movw $0xb3, %dx\n"
34     "  outb %al, %dx\n"
35     "  rsm\n"
36     "smm_relocation_end:\n"
37
38     /* minimal SMM code to enable or disable ACPI */
39     "smm_code_start:\n"
40     "  movw $0xb2, %dx\n"
41     "  inb %dx, %al\n"
42     "  cmp $0xf0, %al\n"
43     "  jne 1f\n"
44
45     /* ACPI disable */
46     "  mov $" __stringify(BUILD_PM_IO_BASE) " + 0x04, %dx\n" /* PMCNTRL */
47     "  inw %dx, %ax\n"
48     "  andw $~1, %ax\n"
49     "  outw %ax, %dx\n"
50
51     "  jmp 2f\n"
52
53     "1:\n"
54     "  cmp $0xf1, %al\n"
55     "  jne 2f\n"
56
57     /* ACPI enable */
58     "  mov $" __stringify(BUILD_PM_IO_BASE) " + 0x04, %dx\n" /* PMCNTRL */
59     "  inw %dx, %ax\n"
60     "  orw $1, %ax\n"
61     "  outw %ax, %dx\n"
62
63     "2:\n"
64     "  rsm\n"
65     "smm_code_end:\n"
66     "  .code32\n"
67     );
68
69 extern u8 smm_relocation_start, smm_relocation_end;
70 extern u8 smm_code_start, smm_code_end;
71
72 void
73 smm_init()
74 {
75     if (!CONFIG_USE_SMM)
76         return;
77
78     // This code is hardcoded for PIIX4 Power Management device.
79     PCIDevice i440_pcidev, d;
80     int ret = pci_find_device(0x8086, 0x7113, 0, &d);
81     if (ret)
82         // Device not found
83         return;
84     ret = pci_find_device(0x8086, 0x1237, 0, &i440_pcidev);
85     if (ret)
86         return;
87
88     /* check if SMM init is already done */
89     u32 value = pci_config_readl(d, 0x58);
90     if (value & (1 << 25))
91         return;
92
93     /* copy the SMM relocation code */
94     memcpy((void *)0x38000, &smm_relocation_start,
95            &smm_relocation_end - &smm_relocation_start);
96
97     /* enable SMI generation when writing to the APMC register */
98     pci_config_writel(d, 0x58, value | (1 << 25));
99
100     /* init APM status port */
101     outb(0x01, 0xb3);
102
103     /* raise an SMI interrupt */
104     outb(0x00, 0xb2);
105
106     /* wait until SMM code executed */
107     while (inb(0xb3) != 0x00)
108         ;
109
110     /* enable the SMM memory window */
111     pci_config_writeb(i440_pcidev, 0x72, 0x02 | 0x48);
112
113     /* copy the SMM code */
114     memcpy((void *)0xa8000, &smm_code_start,
115            &smm_code_end - &smm_code_start);
116     wbinvd();
117
118     /* close the SMM memory window and enable normal SMM */
119     pci_config_writeb(i440_pcidev, 0x72, 0x02 | 0x08);
120 }