blob: 733e624c26ef20e1f858afdfd484e3a4f0f7182f [file] [log] [blame]
Stefan Reinauer800379f2010-03-01 08:34:19 +00001/*
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
32extern unsigned char *mbi;
33extern u32 mbi_len;
34
Stefan Reinauer72f75b12010-03-01 09:09:33 +000035// #define DEBUG_SMI_I82830
Stefan Reinauer800379f2010-03-01 08:34:19 +000036
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 */
Stefan Reinauer72f75b12010-03-01 09:09:33 +000040#if defined(CONFIG_PCI_OPTION_ROM_RUN_YABEL) && CONFIG_PCI_OPTION_ROM_RUN_YABEL && !CONFIG_YABEL_DIRECTHW
Stefan Reinauer800379f2010-03-01 08:34:19 +000041#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
55typedef 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
Stefan Reinauer72f75b12010-03-01 09:09:33 +000079#ifdef DEBUG_SMI_I82830
Stefan Reinauer800379f2010-03-01 08:34:19 +000080static void
81dump(u8 * addr, u32 len)
82{
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000083 printk(BIOS_DEBUG, "\n%s(%p, %x):\n", __func__, addr, len);
Stefan Reinauer800379f2010-03-01 08:34:19 +000084 while (len) {
85 unsigned int tmpCnt = len;
86 unsigned char x;
87 if (tmpCnt > 8)
88 tmpCnt = 8;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000089 printk(BIOS_DEBUG, "\n%p: ", addr);
Stefan Reinauer800379f2010-03-01 08:34:19 +000090 // print hex
91 while (tmpCnt--) {
92 x = *addr++;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000093 printk(BIOS_DEBUG, "%02x ", x);
Stefan Reinauer800379f2010-03-01 08:34:19 +000094 }
95 tmpCnt = len;
96 if (tmpCnt > 8)
97 tmpCnt = 8;
98 len -= tmpCnt;
99 //reset addr ptr to print ascii
100 addr = addr - tmpCnt;
101 // print ascii
102 while (tmpCnt--) {
103 x = *addr++;
104 if ((x < 32) || (x >= 127)) {
105 //non-printable char
106 x = '.';
107 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000108 printk(BIOS_DEBUG, "%c", x);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000109 }
110 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000111 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000112}
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000113#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000114
115typedef struct {
116 banner_id_t banner;
117 u16 versionmajor;
118 u16 versionminor;
119 u32 smicombuffersize;
120} __attribute__((packed)) version_t;
121
122typedef struct {
123 u16 header_id;
124 u16 attributes;
125 u16 size;
126 u8 name_len;
127 u8 reserved;
128 u32 type;
129 u32 header_ext;
130 u8 name[0];
131} __attribute__((packed)) mbi_header_t;
132
133typedef struct {
134 banner_id_t banner;
135 u64 handle;
136 u32 objnum;
137 mbi_header_t header;
138} __attribute__((packed)) obj_header_t;
139
140typedef struct {
141 banner_id_t banner;
142 u64 handle;
143 u32 objnum;
144 u32 start;
145 u32 numbytes;
146 u32 buflen;
147 u32 buffer;
148} __attribute__((packed)) get_object_t;
149
150static void mbi_call(u8 subf, banner_id_t *banner_id)
151{
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000152#ifdef DEBUG_SMI_I82830
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000153 printk(BIOS_DEBUG, "MBI\n");
154 printk(BIOS_DEBUG, "|- sub function %x\n", subf);
155 printk(BIOS_DEBUG, "|- banner id @ %x\n", (u32)banner_id);
156 printk(BIOS_DEBUG, "| |- mhid %x\n", banner_id->mhid);
157 printk(BIOS_DEBUG, "| |- function %x\n", banner_id->function);
158 printk(BIOS_DEBUG, "| |- return status %x\n", banner_id->retsts);
159 printk(BIOS_DEBUG, "| |- rfu %x\n", banner_id->rfu);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000160#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000161
162 switch(banner_id->function) {
163 case 0x0001: {
164 version_t *version;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000165 printk(BIOS_DEBUG, "|- MBI_QueryInterface\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000166 version = (version_t *)banner_id;
167 version->banner.retsts = MSH_OK;
168 version->versionmajor=1;
169 version->versionminor=3;
170 version->smicombuffersize=0x1000;
171 break;
172 }
173 case 0x0002:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000174 printk(BIOS_DEBUG, "|- MBI_Attach\n");
175 printk(BIOS_DEBUG, "|  |- Not Implemented!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000176 break;
177 case 0x0003:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000178 printk(BIOS_DEBUG, "|- MBI_Detach\n");
179 printk(BIOS_DEBUG, "|  |- Not Implemented!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000180 break;
181 case 0x0201: {
182 obj_header_t *obj_header = (obj_header_t *)banner_id;
183 mbi_header_t *mbi_header = NULL;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000184 printk(BIOS_DEBUG, "|- MBI_GetObjectHeader\n");
185 printk(BIOS_DEBUG, "| |- objnum = %d\n", obj_header->objnum);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000186
187 int i, count=0;
188 obj_header->banner.retsts = MSH_IF_NOT_FOUND;
189
Stefan Reinauerd9466492010-04-11 20:04:50 +0000190 for (i=0; i<mbi_len;) {
Stefan Reinauer800379f2010-03-01 08:34:19 +0000191 int len;
192
193 if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
194 i+=16;
195 continue;
196 }
197
198 mbi_header = (mbi_header_t *)&mbi[i];
199 len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + mbi_header->name_len, 16);
200
201 if (obj_header->objnum == count) {
Stefan Reinauerd9466492010-04-11 20:04:50 +0000202#ifdef DEBUG_SMI_I82830
203 if (mbi_header->name_len == 0xff) {
204 printk(BIOS_DEBUG, "| |- corrupt.\n");
205 break;
206 }
207#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000208 int headerlen = ALIGN(sizeof(mbi_header) + mbi_header->name_len + 15, 16);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000209#ifdef DEBUG_SMI_I82830
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000210 printk(BIOS_DEBUG, "| |- headerlen = %d\n", headerlen);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000211#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000212 memcpy(&obj_header->header, mbi_header, headerlen);
213 obj_header->banner.retsts = MSH_OK;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000214 printk(BIOS_DEBUG, "| |- MBI module '");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000215 int j;
216 for (j=0; j < mbi_header->name_len && mbi_header->name[j]; j++)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000217 printk(BIOS_DEBUG, "%c", mbi_header->name[j]);
218 printk(BIOS_DEBUG, "' found.\n");
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000219#ifdef DEBUG_SMI_I82830
220 dump(banner_id, sizeof(obj_header_t) + 16);
221#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000222 break;
223 }
224 i += len;
225 count++;
226 }
227 if (obj_header->banner.retsts == MSH_IF_NOT_FOUND)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000228 printk(BIOS_DEBUG, "| |- MBI object #%d not found.\n", obj_header->objnum);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000229 break;
230 }
231 case 0x0203: {
232 get_object_t *getobj = (get_object_t *)banner_id;
233 mbi_header_t *mbi_header = NULL;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000234 printk(BIOS_DEBUG, "|- MBI_GetObject\n");
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000235#ifdef DEBUG_SMI_I82830
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000236 printk(BIOS_DEBUG, "| |- handle = %016lx\n", getobj->handle);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000237#endif
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000238 printk(BIOS_DEBUG, "| |- objnum = %d\n", getobj->objnum);
239 printk(BIOS_DEBUG, "| |- start = %x\n", getobj->start);
240 printk(BIOS_DEBUG, "| |- numbytes = %x\n", getobj->numbytes);
241 printk(BIOS_DEBUG, "| |- buflen = %x\n", getobj->buflen);
242 printk(BIOS_DEBUG, "| |- buffer = %x\n", getobj->buffer);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000243
244 int i, count=0;
245 getobj->banner.retsts = MSH_IF_NOT_FOUND;
246
247 for (i=0; i< mbi_len;) {
248 int len;
249
250 if (!(mbi[i] == 0xf0 && mbi [i+1] == 0xf6)) {
251 i+=16;
252 continue;
253 }
254
255 mbi_header = (mbi_header_t *)&mbi[i];
256 len = ALIGN((mbi_header->size * 16) + sizeof(mbi_header) + mbi_header->name_len, 16);
257
258 if (getobj->objnum == count) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000259 printk(BIOS_DEBUG, "| |- len = %x\n", len);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000260 memcpy((void *)(getobj->buffer + OBJ_OFFSET),
261 ((char *)mbi_header) + 0x20 , (len > getobj->buflen ? getobj->buflen : len));
262
263 getobj->banner.retsts = MSH_OK;
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000264#ifdef DEBUG_SMI_I82830
265 dump(banner_id, sizeof(getobj) + len);
266#endif
Stefan Reinauer800379f2010-03-01 08:34:19 +0000267 break;
268 }
269 i += len;
270 count++;
271 }
272 if (getobj->banner.retsts == MSH_IF_NOT_FOUND)
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000273 printk(BIOS_DEBUG, "MBI module %d not found.\n", getobj->objnum);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000274 break;
275 }
276 default:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000277 printk(BIOS_DEBUG, "|- function %x\n", banner_id->function);
278 printk(BIOS_DEBUG, "| |- Unknown Function!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000279 break;
280 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000281 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000282 //dump(banner_id, 0x20);
283}
284
285#define SMI_IFC_SUCCESS 1
286#define SMI_IFC_FAILURE_GENERIC 0
287#define SMI_IFC_FAILURE_INVALID 2
288#define SMI_IFC_FAILURE_CRITICAL 4
289#define SMI_IFC_FAILURE_NONCRITICAL 6
290
291#define PC10 0x10
292#define PC11 0x11
293#define PC12 0x12
294#define PC13 0x13
295
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000296static void smi_interface_call(void)
Stefan Reinauer800379f2010-03-01 08:34:19 +0000297{
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000298 u32 mmio = pci_read_config32(PCI_DEV(0, 0x02, 0), 0x14);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000299 // mmio &= 0xfff80000;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000300 // printk(BIOS_DEBUG, "mmio=%x\n", mmio);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000301 u16 swsmi = pci_read_config16(PCI_DEV(0, 0x02, 0), 0xe0);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000302
303 if (!(swsmi & 1))
304 return;
305
306 swsmi &= ~(1 << 0); // clear SMI toggle
307
308 switch ((swsmi>>1) & 0xf) {
309 case 0:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000310 printk(BIOS_DEBUG, "Interface Function Presence Test.\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000311 swsmi = 0;
312 swsmi &= ~(7 << 5); // Exit: Result
313 swsmi |= (SMI_IFC_SUCCESS << 5);
314 swsmi &= 0xff;
315 swsmi |= (PC13 << 8);
316 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000317 // write magic
Stefan Reinauer800379f2010-03-01 08:34:19 +0000318 write32(mmio + 0x71428, 0x494e5443);
319 return;
320 case 4:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000321 printk(BIOS_DEBUG, "Get BIOS Data.\n");
322 printk(BIOS_DEBUG, "swsmi=%04x\n", swsmi);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000323 break;
324 case 5:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000325 printk(BIOS_DEBUG, "Call MBI Functions.\n");
Stefan Reinauer72f75b12010-03-01 09:09:33 +0000326 mbi_call(swsmi >> 8, (banner_id_t *)((read32(mmio + 0x71428) & 0x000fffff) + OBJ_OFFSET) );
Stefan Reinauer800379f2010-03-01 08:34:19 +0000327 // swsmi = 0x0000;
328 swsmi &= ~(7 << 5); // Exit: Result
329 swsmi |= (SMI_IFC_SUCCESS << 5);
330 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
331 return;
332 case 6:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000333 printk(BIOS_DEBUG, "System BIOS Callbacks.\n");
334 printk(BIOS_DEBUG, "swsmi=%04x\n", swsmi);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000335 break;
336 default:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000337 printk(BIOS_DEBUG, "Unknown SMI interface call %04x\n", swsmi);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000338 break;
339 }
340
341 swsmi &= ~(7 << 5); // Exit: Result
342 swsmi |= (SMI_IFC_FAILURE_CRITICAL << 7);
343 pci_write_config16(PCI_DEV(0, 0x02, 0), 0xe0, swsmi);
344}
345
346/**
347 * @brief read and clear ERRSTS
348 * @return ERRSTS register
349 */
350static u16 reset_err_status(void)
351{
352 u16 reg16;
353
354 reg16 = pci_read_config16(PCI_DEV(0, 0x00, 0), ERRSTS);
355 /* set status bits are cleared by writing 1 to them */
356 pci_write_config16(PCI_DEV(0, 0x00, 0), ERRSTS, reg16);
357
358 return reg16;
359}
360
361static void dump_err_status(u32 errsts)
362{
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000363 printk(BIOS_DEBUG, "ERRSTS: ");
364 if (errsts & (1 << 12)) printk(BIOS_DEBUG, "MBI ");
365 if (errsts & (1 << 9)) printk(BIOS_DEBUG, "LCKF ");
366 if (errsts & (1 << 8)) printk(BIOS_DEBUG, "DTF ");
367 if (errsts & (1 << 5)) printk(BIOS_DEBUG, "UNSC ");
368 if (errsts & (1 << 4)) printk(BIOS_DEBUG, "OOGF ");
369 if (errsts & (1 << 3)) printk(BIOS_DEBUG, "IAAF ");
370 if (errsts & (1 << 2)) printk(BIOS_DEBUG, "ITTEF ");
371 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000372}
373
374void northbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
375{
376 u16 errsts;
377
378 /* We need to clear the SMI status registers, or we won't see what's
379 * happening in the following calls.
380 */
381 errsts = reset_err_status();
382 if (errsts & (1 << 12)) {
383 smi_interface_call();
384 } else {
385 if (errsts)
386 dump_err_status(errsts);
387 }
388
389}