This patch implements MBI (modular bios interface) support to the i830 chipset.
[coreboot.git] / src / northbridge / intel / i82830 / i82830_smihandler.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2010 coresystems GmbH
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; version 2 of
9  * the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19  * MA 02110-1301 USA
20  */
21
22 #include <types.h>
23 #include <string.h>
24 #include <arch/io.h>
25 #include <arch/romcc_io.h>
26 #include <console/console.h>
27 #include <cpu/x86/cache.h>
28 #include <cpu/x86/smm.h>
29 #include <device/pci_def.h>
30 #include "i82830.h"
31
32 extern unsigned char *mbi;
33 extern u32 mbi_len;
34
35 #define DEBUG_SMI
36
37 /* If YABEL is enabled and it's not running at 0x00000000, we have to add some
38  * offset to all our mbi object memory accesses
39  */
40 #if defined(CONFIG_PCI_OPTION_ROM_RUN_YABEL) && !defined(CONFIG_YABEL_DIRECTHW)
41 #define OBJ_OFFSET CONFIG_YABEL_VIRTMEM_LOCATION
42 #else
43 #define OBJ_OFFSET 0x00000
44 #endif
45
46 /* I830M */
47 #define SMRAM           0x90
48 #define   D_OPEN        (1 << 6)
49 #define   D_CLS         (1 << 5)
50 #define   D_LCK         (1 << 4)
51 #define   G_SMRANE      (1 << 3)
52 #define   C_BASE_SEG    ((0 << 2) | (1 << 1) | (0 << 0))
53
54
55 typedef struct {
56         u32 mhid;
57         u32 function;
58         u32 retsts;
59         u32 rfu;
60 } __attribute__((packed)) banner_id_t;
61
62 #define MSH_OK                  0x0000
63 #define MSH_OK_RESTART          0x0001
64 #define MSH_FWH_ERR             0x00ff
65 #define MSH_IF_BAD_ID           0x0100
66 #define MSH_IF_BAD_FUNC         0x0101
67 #define MSH_IF_MBI_CORRUPT      0x0102
68 #define MSH_IF_BAD_HANDLE       0x0103
69 #define MSH_ALRDY_ATCHED        0x0104
70 #define MSH_NOT_ATCHED          0x0105
71 #define MSH_IF                  0x0106
72 #define MSH_IF_INVADDR          0x0107
73 #define MSH_IF_UKN_TYPE         0x0108
74 #define MSH_IF_NOT_FOUND        0x0109
75 #define MSH_IF_NO_KEY           0x010a
76 #define MSH_IF_BUF_SIZE         0x010b
77 #define MSH_IF_NOT_PENDING      0x010c
78
79 static void
80 dump(u8 * addr, u32 len)
81 {
82         printk_debug("\n%s(%p, %x):\n", __func__, addr, len);
83         while (len) {
84                 unsigned int tmpCnt = len;
85                 unsigned char x;
86                 if (tmpCnt > 8)
87                         tmpCnt = 8;
88                 printk_debug("\n%p: ", addr);
89                 // print hex
90                 while (tmpCnt--) {
91                         x = *addr++;
92                         printk_debug("%02x ", x);
93                 }
94                 tmpCnt = len;
95                 if (tmpCnt > 8)
96                         tmpCnt = 8;
97                 len -= tmpCnt;
98                 //reset addr ptr to print ascii
99                 addr = addr - tmpCnt;
100                 // print ascii
101                 while (tmpCnt--) {
102                         x = *addr++;
103                         if ((x < 32) || (x >= 127)) {
104                                 //non-printable char
105                                 x = '.';
106                         }
107                         printk_debug("%c", x);
108                 }
109         }
110         printk_debug("\n");
111 }
112
113 typedef struct {
114         banner_id_t banner;
115         u16 versionmajor;
116         u16 versionminor;
117         u32 smicombuffersize;
118 } __attribute__((packed)) version_t;
119
120 typedef struct {
121         u16 header_id;
122         u16 attributes;
123         u16 size;
124         u8  name_len;
125         u8 reserved;
126         u32 type;
127         u32 header_ext;
128         u8 name[0];
129 } __attribute__((packed)) mbi_header_t;
130
131 typedef struct {
132         banner_id_t banner;
133         u64 handle;
134         u32 objnum;
135         mbi_header_t header;
136 } __attribute__((packed)) obj_header_t;
137
138 typedef struct {
139         banner_id_t banner;
140         u64 handle;
141         u32 objnum;
142         u32 start;
143         u32 numbytes;
144         u32 buflen;
145         u32 buffer;
146 } __attribute__((packed)) get_object_t;
147
148 static void mbi_call(u8 subf, banner_id_t *banner_id)
149 {
150         // printk_debug("MBI\n");
151         // printk_debug("|- sub function %x\n", subf);
152         // printk_debug("|- banner id @ %x\n", (u32)banner_id);
153         // printk_debug("|  |- mhid %x\n", banner_id->mhid);
154         // printk_debug("|  |- function %x\n", banner_id->function);
155         // printk_debug("|  |- return status %x\n", banner_id->retsts);
156         // printk_debug("|  |- rfu %x\n", banner_id->rfu);
157
158         switch(banner_id->function) {
159         case 0x0001: {
160                 version_t *version;
161                 printk_debug("|- MBI_QueryInterface\n");
162                 version = (version_t *)banner_id;
163                 version->banner.retsts = MSH_OK;
164                 version->versionmajor=1;
165                 version->versionminor=3;
166                 version->smicombuffersize=0x1000;
167                 break;
168         }
169         case 0x0002:
170                 printk_debug("|- MBI_Attach\n");
171                 printk_debug("|  |- Not Implemented!\n");
172                 break;
173         case 0x0003:
174                 printk_debug("|- MBI_Detach\n");
175                 printk_debug("|  |- Not Implemented!\n");
176                 break;
177         case 0x0201: {
178                 obj_header_t *obj_header = (obj_header_t *)banner_id;
179                 mbi_header_t *mbi_header = NULL;
180                 printk_debug("|- MBI_GetObjectHeader\n");
181                 printk_debug("|  |- objnum = %d\n", obj_header->objnum);
182
183                 int i, count=0;
184                 obj_header->banner.retsts = MSH_IF_NOT_FOUND;
185
186                 for (i=0; i< mbi_len;) {
187                         int len;
188
189                         if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
190                                 i+=16;
191                                 continue;
192                         }
193                         
194                         mbi_header = (mbi_header_t *)&mbi[i];
195                         len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + mbi_header->name_len, 16);
196                         
197                         if (obj_header->objnum == count) {
198                                 int headerlen = ALIGN(sizeof(mbi_header) + mbi_header->name_len + 15, 16);
199                                 // printk_debug("|  |- headerlen = %d\n", headerlen);
200                                 memcpy(&obj_header->header, mbi_header, headerlen);
201                                 obj_header->banner.retsts = MSH_OK;
202                                 printk_debug("|     |- MBI module '");
203                                 int j;
204                                 for (j=0; j < mbi_header->name_len && mbi_header->name[j]; j++)
205                                         printk_debug("%c",  mbi_header->name[j]);
206                                 printk_debug("' found.\n", obj_header->objnum);
207                                 // dump(banner_id, sizeof(obj_header_t) + 16);
208                                 break;
209                         }
210                         i += len;
211                         count++;
212                 }
213                 if (obj_header->banner.retsts == MSH_IF_NOT_FOUND) 
214                         printk_debug("|     |- MBI object #%d not found.\n", obj_header->objnum);
215                 break;
216         }
217         case 0x0203: {
218                 get_object_t *getobj = (get_object_t *)banner_id;
219                 mbi_header_t *mbi_header = NULL;
220                 printk_debug("|- MBI_GetObject\n");
221                 // printk_debug("|  |- handle = %016lx\n", getobj->handle);
222                 printk_debug("|  |- objnum = %d\n", getobj->objnum);
223                 printk_debug("|  |- start = %x\n", getobj->start);
224                 printk_debug("|  |- numbytes = %x\n", getobj->numbytes);
225                 printk_debug("|  |- buflen = %x\n", getobj->buflen);
226                 printk_debug("|  |- buffer = %x\n", getobj->buffer);
227
228                 int i, count=0;
229                 getobj->banner.retsts = MSH_IF_NOT_FOUND;
230
231                 for (i=0; i< mbi_len;) {
232                         int len;
233
234                         if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
235                                 i+=16;
236                                 continue;
237                         }
238                         
239                         mbi_header = (mbi_header_t *)&mbi[i];
240                         len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + mbi_header->name_len, 16);
241                         
242                         if (getobj->objnum == count) {
243                                 printk_debug("|  |- len = %x\n", len);
244                                 memcpy((void *)(getobj->buffer + OBJ_OFFSET),
245                                                 ((char *)mbi_header) + 0x20 , (len > getobj->buflen ? getobj->buflen : len));
246
247                                 getobj->banner.retsts = MSH_OK;
248                                 //dump(banner_id, sizeof(getobj) + len);
249                                 break;
250                         }
251                         i += len;
252                         count++;
253                 }
254                 if (getobj->banner.retsts == MSH_IF_NOT_FOUND) 
255                         printk_debug("MBI module %d not found.\n", getobj->objnum);
256                 break;
257         }
258         default:
259                 printk_debug("|- function %x\n", banner_id->function);
260                 printk_debug("|  |- Unknown Function!\n");
261                 break;
262         }
263         printk_debug("\n");
264         //dump(banner_id, 0x20);
265 }
266
267 #define SMI_IFC_SUCCESS             1
268 #define SMI_IFC_FAILURE_GENERIC     0
269 #define SMI_IFC_FAILURE_INVALID     2
270 #define SMI_IFC_FAILURE_CRITICAL    4
271 #define SMI_IFC_FAILURE_NONCRITICAL 6
272
273 #define PC10    0x10
274 #define PC11    0x11
275 #define PC12    0x12
276 #define PC13    0x13
277
278 void smi_interface_call(void)
279 {
280         u32 mmio;
281         mmio = pci_read_config32(PCI_DEV(0, 0x02, 0), 0x14);
282         // mmio &= 0xfff80000;
283         // printk_debug("mmio=%x\n", mmio);
284
285         u16 swsmi;
286         swsmi=pci_read_config16(PCI_DEV(0, 0x02, 0), 0xe0);
287
288         if (!(swsmi & 1))
289                 return;
290
291         swsmi &= ~(1 << 0); // clear SMI toggle
292
293         switch ((swsmi>>1) & 0xf) {
294         case 0:
295                 printk_debug("Interface Function Presence Test.\n");
296                 swsmi = 0;
297                 swsmi &= ~(7 << 5); // Exit: Result
298                 swsmi |= (SMI_IFC_SUCCESS << 5);
299                 swsmi &= 0xff;
300                 swsmi |= (PC13 << 8);
301                 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
302                 // pathetic
303                 write32(mmio + 0x71428, 0x494e5443);
304                 return;
305         case 4:
306                 printk_debug("Get BIOS Data.\n");
307                 printk_debug("swsmi=%04x\n", swsmi);
308                 break;
309         case 5:
310                 printk_debug("Call MBI Functions.\n");
311                 mbi_call(swsmi >> 8, (banner_id_t *)((readl(mmio + 0x71428) & 0x000fffff) + OBJ_OFFSET) );
312                 // swsmi = 0x0000;
313                 swsmi &= ~(7 << 5); // Exit: Result
314                 swsmi |= (SMI_IFC_SUCCESS << 5);
315                 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
316                 return;
317         case 6:
318                 printk_debug("System BIOS Callbacks.\n");
319                 printk_debug("swsmi=%04x\n", swsmi);
320                 break;
321         default:
322                 printk_debug("Unknown SMI interface call %04x\n", swsmi);
323                 break;
324         }
325
326         swsmi &= ~(7 << 5); // Exit: Result
327         swsmi |= (SMI_IFC_FAILURE_CRITICAL << 7);
328         pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
329 }
330
331 /**
332  * @brief read and clear ERRSTS
333  * @return ERRSTS register
334  */
335 static u16 reset_err_status(void)
336 {
337         u16 reg16;
338
339         reg16 = pci_read_config16(PCI_DEV(0, 0x00, 0), ERRSTS);
340         /* set status bits are cleared by writing 1 to them */
341         pci_write_config16(PCI_DEV(0, 0x00, 0), ERRSTS, reg16);
342
343         return reg16;
344 }
345
346 static void dump_err_status(u32 errsts)
347 {
348         printk_debug("ERRSTS: ");
349         if (errsts & (1 << 12)) printk_debug("MBI ");
350         if (errsts & (1 <<  9)) printk_debug("LCKF ");
351         if (errsts & (1 <<  8)) printk_debug("DTF ");
352         if (errsts & (1 <<  5)) printk_debug("UNSC ");
353         if (errsts & (1 <<  4)) printk_debug("OOGF ");
354         if (errsts & (1 <<  3)) printk_debug("IAAF ");
355         if (errsts & (1 <<  2)) printk_debug("ITTEF ");
356         printk_debug("\n");
357 }
358
359 void northbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
360 {
361         u16 errsts;
362
363         /* We need to clear the SMI status registers, or we won't see what's
364          * happening in the following calls.
365          */
366         errsts = reset_err_status();
367         if (errsts & (1 << 12)) {
368                 smi_interface_call();
369         } else {
370                 if (errsts)
371                         dump_err_status(errsts);
372         }
373
374 }