196ff3b75SFabien Thomas /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni *
496ff3b75SFabien Thomas * Copyright (c) 2011 Fabien Thomas <fabient@FreeBSD.org>.
596ff3b75SFabien Thomas * All rights reserved.
696ff3b75SFabien Thomas *
796ff3b75SFabien Thomas * Redistribution and use in source and binary forms, with or without
896ff3b75SFabien Thomas * modification, are permitted provided that the following conditions
996ff3b75SFabien Thomas * are met:
1096ff3b75SFabien Thomas * 1. Redistributions of source code must retain the above copyright
1196ff3b75SFabien Thomas * notice, this list of conditions and the following disclaimer.
1296ff3b75SFabien Thomas * 2. Redistributions in binary form must reproduce the above copyright
1396ff3b75SFabien Thomas * notice, this list of conditions and the following disclaimer in the
1496ff3b75SFabien Thomas * documentation and/or other materials provided with the distribution.
1596ff3b75SFabien Thomas *
1696ff3b75SFabien Thomas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1796ff3b75SFabien Thomas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1896ff3b75SFabien Thomas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1996ff3b75SFabien Thomas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2096ff3b75SFabien Thomas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2196ff3b75SFabien Thomas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2296ff3b75SFabien Thomas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2396ff3b75SFabien Thomas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2496ff3b75SFabien Thomas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2596ff3b75SFabien Thomas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2696ff3b75SFabien Thomas */
2796ff3b75SFabien Thomas
2896ff3b75SFabien Thomas #include <sys/cdefs.h>
2996ff3b75SFabien Thomas #include <assert.h>
3096ff3b75SFabien Thomas #include <stdio.h>
3196ff3b75SFabien Thomas #include <stdlib.h>
3296ff3b75SFabien Thomas #include <string.h>
3396ff3b75SFabien Thomas #include <unistd.h>
3496ff3b75SFabien Thomas #include <fcntl.h>
3596ff3b75SFabien Thomas #include <err.h>
36bab89cefSEitan Adler #include <errno.h>
3796ff3b75SFabien Thomas
3896ff3b75SFabien Thomas #include <sys/types.h>
3996ff3b75SFabien Thomas #include <sys/stat.h>
4096ff3b75SFabien Thomas #include <sys/mman.h>
4196ff3b75SFabien Thomas #include <sys/ioctl.h>
4296ff3b75SFabien Thomas #include <sys/ioccom.h>
4396ff3b75SFabien Thomas #include <sys/cpuctl.h>
4496ff3b75SFabien Thomas
4596ff3b75SFabien Thomas #include <machine/cpufunc.h>
4696ff3b75SFabien Thomas #include <machine/specialreg.h>
4796ff3b75SFabien Thomas
4896ff3b75SFabien Thomas #include "cpucontrol.h"
4996ff3b75SFabien Thomas #include "via.h"
5096ff3b75SFabien Thomas
5196ff3b75SFabien Thomas int
via_probe(int fd)5296ff3b75SFabien Thomas via_probe(int fd)
5396ff3b75SFabien Thomas {
5496ff3b75SFabien Thomas char vendor[13];
5596ff3b75SFabien Thomas int error;
5696ff3b75SFabien Thomas cpuctl_cpuid_args_t idargs = {
5796ff3b75SFabien Thomas .level = 0,
5896ff3b75SFabien Thomas };
5996ff3b75SFabien Thomas
6096ff3b75SFabien Thomas error = ioctl(fd, CPUCTL_CPUID, &idargs);
6196ff3b75SFabien Thomas if (error < 0) {
6296ff3b75SFabien Thomas WARN(0, "ioctl()");
6396ff3b75SFabien Thomas return (1);
6496ff3b75SFabien Thomas }
6596ff3b75SFabien Thomas ((uint32_t *)vendor)[0] = idargs.data[1];
6696ff3b75SFabien Thomas ((uint32_t *)vendor)[1] = idargs.data[3];
6796ff3b75SFabien Thomas ((uint32_t *)vendor)[2] = idargs.data[2];
6896ff3b75SFabien Thomas vendor[12] = '\0';
6996ff3b75SFabien Thomas if (strncmp(vendor, CENTAUR_VENDOR_ID, sizeof(CENTAUR_VENDOR_ID)) != 0)
7096ff3b75SFabien Thomas return (1);
7196ff3b75SFabien Thomas
7296ff3b75SFabien Thomas /* TODO: detect Nano CPU. */
7396ff3b75SFabien Thomas return (0);
7496ff3b75SFabien Thomas }
7596ff3b75SFabien Thomas
7696ff3b75SFabien Thomas void
via_update(const struct ucode_update_params * params)77dee401e8SConrad Meyer via_update(const struct ucode_update_params *params)
7896ff3b75SFabien Thomas {
79dee401e8SConrad Meyer int devfd;
80dee401e8SConrad Meyer const char *dev, *path;
81dee401e8SConrad Meyer const uint32_t *fw_image;
8296ff3b75SFabien Thomas uint32_t sum;
8396ff3b75SFabien Thomas unsigned int i;
8496ff3b75SFabien Thomas size_t payload_size;
85dee401e8SConrad Meyer const via_fw_header_t *fw_header;
86de879bdaSEitan Adler uint32_t signature;
8796ff3b75SFabien Thomas int32_t revision;
88dee401e8SConrad Meyer const void *fw_data;
8996ff3b75SFabien Thomas size_t data_size, total_size;
9096ff3b75SFabien Thomas cpuctl_msr_args_t msrargs = {
9196ff3b75SFabien Thomas .msr = MSR_IA32_PLATFORM_ID,
9296ff3b75SFabien Thomas };
9396ff3b75SFabien Thomas cpuctl_cpuid_args_t idargs = {
9496ff3b75SFabien Thomas .level = 1, /* Signature. */
9596ff3b75SFabien Thomas };
9696ff3b75SFabien Thomas cpuctl_update_args_t args;
9796ff3b75SFabien Thomas int error;
9896ff3b75SFabien Thomas
99dee401e8SConrad Meyer dev = params->dev_path;
100dee401e8SConrad Meyer path = params->fw_path;
101dee401e8SConrad Meyer devfd = params->devfd;
102dee401e8SConrad Meyer fw_image = params->fwimage;
103dee401e8SConrad Meyer
10496ff3b75SFabien Thomas assert(path);
10596ff3b75SFabien Thomas assert(dev);
10696ff3b75SFabien Thomas
10796ff3b75SFabien Thomas error = ioctl(devfd, CPUCTL_CPUID, &idargs);
10896ff3b75SFabien Thomas if (error < 0) {
10996ff3b75SFabien Thomas WARN(0, "ioctl(%s)", dev);
11096ff3b75SFabien Thomas goto fail;
11196ff3b75SFabien Thomas }
11296ff3b75SFabien Thomas signature = idargs.data[0];
11396ff3b75SFabien Thomas error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
11496ff3b75SFabien Thomas if (error < 0) {
11596ff3b75SFabien Thomas WARN(0, "ioctl(%s)", dev);
11696ff3b75SFabien Thomas goto fail;
11796ff3b75SFabien Thomas }
11896ff3b75SFabien Thomas
11996ff3b75SFabien Thomas /*
12096ff3b75SFabien Thomas * MSR_IA32_PLATFORM_ID contains flag in BCD in bits 52-50.
12196ff3b75SFabien Thomas */
12296ff3b75SFabien Thomas msrargs.msr = MSR_BIOS_SIGN;
12396ff3b75SFabien Thomas error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
12496ff3b75SFabien Thomas if (error < 0) {
12596ff3b75SFabien Thomas WARN(0, "ioctl(%s)", dev);
12696ff3b75SFabien Thomas goto fail;
12796ff3b75SFabien Thomas }
12896ff3b75SFabien Thomas revision = msrargs.data >> 32; /* Revision in the high dword. */
12996ff3b75SFabien Thomas WARNX(2, "found cpu type %#x family %#x model %#x stepping %#x.",
13096ff3b75SFabien Thomas (signature >> 12) & 0x03, (signature >> 8) & 0x0f,
13196ff3b75SFabien Thomas (signature >> 4) & 0x0f, (signature >> 0) & 0x0f);
132dee401e8SConrad Meyer
133dee401e8SConrad Meyer if (params->fwsize < sizeof(*fw_header)) {
13496ff3b75SFabien Thomas WARNX(2, "file too short: %s", path);
13596ff3b75SFabien Thomas goto fail;
13696ff3b75SFabien Thomas }
13796ff3b75SFabien Thomas
138dee401e8SConrad Meyer fw_header = (const via_fw_header_t *)fw_image;
13996ff3b75SFabien Thomas if (fw_header->signature != VIA_HEADER_SIGNATURE ||
14096ff3b75SFabien Thomas fw_header->loader_revision != VIA_LOADER_REVISION) {
14196ff3b75SFabien Thomas WARNX(2, "%s is not a valid via firmware: version mismatch",
14296ff3b75SFabien Thomas path);
14396ff3b75SFabien Thomas goto fail;
14496ff3b75SFabien Thomas }
14596ff3b75SFabien Thomas data_size = fw_header->data_size;
14696ff3b75SFabien Thomas total_size = fw_header->total_size;
147dee401e8SConrad Meyer if (total_size > params->fwsize) {
14896ff3b75SFabien Thomas WARNX(2, "file too short: %s", path);
14996ff3b75SFabien Thomas goto fail;
15096ff3b75SFabien Thomas }
15196ff3b75SFabien Thomas payload_size = data_size + sizeof(*fw_header);
15296ff3b75SFabien Thomas
15396ff3b75SFabien Thomas /*
15496ff3b75SFabien Thomas * Check the primary checksum.
15596ff3b75SFabien Thomas */
15696ff3b75SFabien Thomas sum = 0;
15796ff3b75SFabien Thomas for (i = 0; i < (payload_size / sizeof(uint32_t)); i++)
158dee401e8SConrad Meyer sum += *((const uint32_t *)fw_image + i);
15996ff3b75SFabien Thomas if (sum != 0) {
16096ff3b75SFabien Thomas WARNX(2, "%s: update data checksum invalid", path);
16196ff3b75SFabien Thomas goto fail;
16296ff3b75SFabien Thomas }
16396ff3b75SFabien Thomas
16496ff3b75SFabien Thomas fw_data = fw_header + 1; /* Pointer to the update data. */
16596ff3b75SFabien Thomas
16696ff3b75SFabien Thomas /*
16796ff3b75SFabien Thomas * Check if the given image is ok for this cpu.
16896ff3b75SFabien Thomas */
16996ff3b75SFabien Thomas if (signature != fw_header->cpu_signature)
17096ff3b75SFabien Thomas goto fail;
17196ff3b75SFabien Thomas
17296ff3b75SFabien Thomas if (fw_header->revision != 0 && revision >= fw_header->revision) {
17396ff3b75SFabien Thomas WARNX(1, "skipping %s of rev %#x: up to date",
17496ff3b75SFabien Thomas path, fw_header->revision);
17596ff3b75SFabien Thomas goto fail;
17696ff3b75SFabien Thomas }
17796ff3b75SFabien Thomas fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
17896ff3b75SFabien Thomas path, dev, revision, fw_header->revision);
179dee401e8SConrad Meyer args.data = __DECONST(void *, fw_data);
18096ff3b75SFabien Thomas args.size = data_size;
18196ff3b75SFabien Thomas error = ioctl(devfd, CPUCTL_UPDATE, &args);
18296ff3b75SFabien Thomas if (error < 0) {
183bab89cefSEitan Adler error = errno;
18496ff3b75SFabien Thomas fprintf(stderr, "failed.\n");
185bab89cefSEitan Adler errno = error;
18696ff3b75SFabien Thomas WARN(0, "ioctl()");
18796ff3b75SFabien Thomas goto fail;
18896ff3b75SFabien Thomas }
18996ff3b75SFabien Thomas fprintf(stderr, "done.\n");
19096ff3b75SFabien Thomas
19196ff3b75SFabien Thomas fail:
19296ff3b75SFabien Thomas return;
19396ff3b75SFabien Thomas }
194