Xenon/src/microcode.zig

110 lines
3.5 KiB
Zig

const std = @import("std");
const amd = @import("microcode/amd.zig");
const cpu = @import("cpu.zig");
const cpio = @import("formats/cpio.zig");
const intel = @import("microcode/intel.zig");
const log = @import("logger.zig").log;
const msr = @import("msr.zig");
/// Base path to microcode updates
const microcode_path = "sys/microcode/";
const amd_vendor_string = "AuthenticAMD";
const intel_vendor_string = "GenuineIntel";
const log_update_found = "[microcode] Found microcode update file";
const panic_filename_overflow = "Microcode filename buffer overflow";
/// Returns the patchlevel of the current logical processor,
/// if known
pub fn patchlevel() ?u64 {
const vendor = cpu.CpuId.vendor();
return if (std.mem.eql(u8, &vendor, amd_vendor_string))
cpu.rdmsr(msr.ucode_amd_patchlevel)
else
cpu.rdmsr(msr.ia32_bios_sign_id) >> 32;
}
/// Update the microcode of the current logical processor (manifacturer dependent)
/// Returns true if the update was successful
/// This function should be run sequentially on each logical core
/// to ensure the updates are fully applied
pub fn update(context: var, fileFn: fn (@TypeOf(context), []const u8) ?[]const u8) bool {
var filename_buffer = [_]u8{0} ** 64;
const sign = cpu.CpuId.signature();
const vendor = cpu.CpuId.vendor();
if (std.mem.eql(u8, &vendor, amd_vendor_string)) {
const family = sign.family();
const filename = std.fmt.bufPrint(&filename_buffer, microcode_path ++ "amd/microcode_amd_fam{0x:0>2}h.bin", .{family}) catch @panic(panic_filename_overflow);
const microcode = if (fileFn(context, filename)) |mc|
mc
else if (fileFn(context, microcode_path ++ "amd/microcode_amd.bin")) |mc|
mc
else
return false;
log(.Debug, log_update_found, .{});
if (amd.apply(microcode)) |applied| {
return applied;
} else |err| {
log(.Debug, "[microcode] Microcode update failed: {}", .{err});
return false;
}
} else if (std.mem.eql(u8, &vendor, intel_vendor_string)) {
const filename = std.fmt.bufPrint(&filename_buffer, microcode_path ++ "intel/{0x:0>2}-{1x:0>2}-{2x:0>2}", .{ sign.baseFamily(), sign.baseModel(), sign.stepping() }) catch @panic(panic_filename_overflow);
const microcode = if (fileFn(context, filename)) |mc|
mc
else
return false;
log(.Debug, log_update_found, .{});
const microcode_align = if (@ptrToInt(&microcode[0]) & 0b1111 == 0) bl: {
break :bl @intToPtr([*]align(16) u8, @ptrToInt(&microcode[0]))[0..microcode.len];
} else {
log(.Warning, "[microcode] Update failed due to misaligned data", .{});
return false;
};
if (intel.apply(microcode_align)) |applied| {
return applied;
} else |err| {
log(.Debug, "[microcode] Microcode update failed: {}", .{err});
return false;
}
}
return false;
}
pub fn update_cpio(disk: cpio.Cpio) bool {
const fileFn = struct {
pub fn fileFn(context: cpio.Cpio, filename: []const u8) ?[]const u8 {
var cpio_context = context;
if (cpio_context.findFile(filename)) |err_file| {
const file = err_file catch return null;
return file.data() catch return null;
}
return null;
}
}.fileFn;
return update(disk, fileFn);
}
pub const MicrocodeError = error{
EndOfData,
Invalid,
ChecksumMismatch,
Incompatible,
};