summaryrefslogtreecommitdiff
path: root/src/cpu/via/nano/update_ucode.c
blob: 0ccddc7be0e384a2a1709bf4ac33ff86d472b79a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2012  Alexandru Gagniuc <mr.nuke.me@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "update_ucode.h"
#include <cpu/x86/msr.h>
#include <console/console.h>
#include <stddef.h>
#include <arch/cpu.h>
#include <cbfs.h>

static ucode_update_status nano_apply_ucode(const nano_ucode_header *ucode)
{
	printk(BIOS_SPEW, "Attempting to apply microcode update\n");

	msr_t msr;
	/* Address of ucode block goes in msr.lo for 32-bit mode
	 * Now remember, we need to pass the address of the actual microcode,
	 * not the header. The header is just there to help us. */
	msr.lo = (unsigned int)(&(ucode->ucode_start));
	msr.hi = 0;
	wrmsr(IA32_BIOS_UPDT_TRIG, msr);

	/* Let's see if we updated successfully */
	msr = rdmsr(MSR_UCODE_UPDATE_STATUS);

	return msr.lo & 0x07;
}

static void nano_print_ucode_info(const nano_ucode_header *ucode)
{
	printk(BIOS_SPEW, "Microcode update information:\n");
	printk(BIOS_SPEW, "Name: %8s\n", ucode->name);
	printk(BIOS_SPEW, "Date: %u/%u/%u\n", ucode->month,
	       ucode->day, ucode->year);
}

static ucode_validity nano_ucode_is_valid(const nano_ucode_header *ucode)
{
	/* We must have a valid signature */
	if (ucode->signature != NANO_UCODE_SIGNATURE)
		return NANO_UCODE_SIGNATURE_ERROR;
	/* The size of the head must be exactly 12 double words */
	if ((ucode->total_size - ucode->payload_size) != NANO_UCODE_HEADER_SIZE)
		return NANO_UCODE_WRONG_SIZE;

	/* How about a checksum ? Checksum must be 0
	 * Two's complement done over the entire file, including the header */
	int i;
	u32 check = 0;
	u32 *raw = (void *) ucode;
	for (i = 0; i < ((ucode->total_size) >> 2); i++) {
		check += raw[i];
	}
	if (check != 0)
		return NANO_UCODE_CHECKSUM_FAIL;
	/* Made it here huh? Then it looks valid to us.
	 * If there's anything else wrong, the CPU will reject the update */
	return NANO_UCODE_VALID;
}

static void nano_print_ucode_status(ucode_update_status stat)
{
	switch (stat)
	{
	case UCODE_UPDATE_SUCCESS:
		printk(BIOS_INFO, "Microcode update successful.\n");
		break;
	case UCODE_UPDATE_FAIL:
		printk(BIOS_ALERT, "Microcode update failed, bad environment."
				   "Update was not applied.\n");
		break;
	case UCODE_UPDATE_WRONG_CPU:
		printk(BIOS_ALERT, "Update not applicable to this CPU.\n");
		break;
	case UCODE_INVALID_UPDATE_BLOCK:
		printk(BIOS_ALERT, "Microcode block invalid."
				   "Update was not applied.\n");
		break;
	default:
		printk(BIOS_ALERT, "Unknown status. No update applied.\n");
	}
}

unsigned int nano_update_ucode(void)
{
	size_t i;
	unsigned int n_updates = 0;
	u32 fms = cpuid_eax(0x1);
	/* Considering we are running with eXecute-In-Place (XIP), there's no
	 * need to worry that accessing data from ROM will slow us down.
	 * Microcode data should be aligned to a 4-byte boundary, but CBFS
	 * already does that for us (Do you, CBFS?) */
	u32 *ucode_data;
	size_t ucode_len;

	ucode_data = cbfs_boot_map_with_leak("cpu_microcode_blob.bin",
					     CBFS_TYPE_MICROCODE, &ucode_len);
	/* Oops, did you forget to include the microcode ? */
	if (ucode_data == NULL) {
		printk(BIOS_ALERT, "WARNING: No microcode file found in CBFS. "
				   "Aborting microcode updates\n");
		return 0;
	}

	/* We might do a lot of loops searching for the microcode updates, but
	 * keep in mind, nano_ucode_is_valid searches for the signature before
	 * doing anything else. */
	for (i = 0; i < (ucode_len >> 2); /* don't increment i here */)
	{
		ucode_update_status stat;
		const nano_ucode_header * ucode = (void *)(&ucode_data[i]);
		if (nano_ucode_is_valid(ucode) != NANO_UCODE_VALID) {
			i++;
			continue;
		}
		/* Since we have a valid microcode, there's no need to search
		 * in this region, so we restart our search at the end of this
		 * microcode */
		i += (ucode->total_size >> 2);
		/* Is the microcode compatible with our CPU? */
		if (ucode->applicable_fms != fms) continue;
		/* For our most curious users */
		nano_print_ucode_info(ucode);
		/* The meat of the pie */
		stat = nano_apply_ucode(ucode);
		/* The user might want to know how the update went */
		nano_print_ucode_status(stat);
		if (stat == UCODE_UPDATE_SUCCESS) n_updates++;
	}

	return n_updates;
}