// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2014 Google, Inc * * Memory Type Range Regsters - these are used to tell the CPU whether * memory is cacheable and if so the cache write mode to use. * * These can speed up booting. See the mtrr command. * * Reference: Intel Architecture Software Developer's Manual, Volume 3: * System Programming */ /* * Note that any console output (e.g. debug()) in this file will likely fail * since the MTRR registers are sometimes in flux. */ #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; /* Prepare to adjust MTRRs */ void mtrr_open(struct mtrr_state *state, bool do_caches) { if (!gd->arch.has_mtrr) return; if (do_caches) { state->enable_cache = dcache_status(); if (state->enable_cache) disable_caches(); } state->deftype = native_read_msr(MTRR_DEF_TYPE_MSR); wrmsrl(MTRR_DEF_TYPE_MSR, state->deftype & ~MTRR_DEF_TYPE_EN); } /* Clean up after adjusting MTRRs, and enable them */ void mtrr_close(struct mtrr_state *state, bool do_caches) { if (!gd->arch.has_mtrr) return; wrmsrl(MTRR_DEF_TYPE_MSR, state->deftype | MTRR_DEF_TYPE_EN); if (do_caches && state->enable_cache) enable_caches(); } static void set_var_mtrr(uint reg, uint type, uint64_t start, uint64_t size) { u64 mask; wrmsrl(MTRR_PHYS_BASE_MSR(reg), start | type); mask = ~(size - 1); mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1; wrmsrl(MTRR_PHYS_MASK_MSR(reg), mask | MTRR_PHYS_MASK_VALID); } void mtrr_read_all(struct mtrr_info *info) { int reg_count = mtrr_get_var_count(); int i; for (i = 0; i < reg_count; i++) { info->mtrr[i].base = native_read_msr(MTRR_PHYS_BASE_MSR(i)); info->mtrr[i].mask = native_read_msr(MTRR_PHYS_MASK_MSR(i)); } } void mtrr_write_all(struct mtrr_info *info) { int reg_count = mtrr_get_var_count(); struct mtrr_state state; int i; for (i = 0; i < reg_count; i++) { mtrr_open(&state, true); wrmsrl(MTRR_PHYS_BASE_MSR(i), info->mtrr[i].base); wrmsrl(MTRR_PHYS_MASK_MSR(i), info->mtrr[i].mask); mtrr_close(&state, true); } } static void write_mtrrs(void *arg) { struct mtrr_info *info = arg; mtrr_write_all(info); } static void read_mtrrs(void *arg) { struct mtrr_info *info = arg; mtrr_read_all(info); } /** * mtrr_copy_to_aps() - Copy the MTRRs from the boot CPU to other CPUs * * Return: 0 on success, -ve on failure */ static int mtrr_copy_to_aps(void) { struct mtrr_info info; int ret; ret = mp_run_on_cpus(MP_SELECT_BSP, read_mtrrs, &info); if (ret == -ENXIO) return 0; else if (ret) return log_msg_ret("bsp", ret); ret = mp_run_on_cpus(MP_SELECT_APS, write_mtrrs, &info); if (ret) return log_msg_ret("bsp", ret); return 0; } static int h_comp_mtrr(const void *p1, const void *p2) { const struct mtrr_request *req1 = p1; const struct mtrr_request *req2 = p2; s64 diff = req1->start - req2->start; return diff < 0 ? -1 : diff > 0 ? 1 : 0; } int mtrr_commit(bool do_caches) { struct mtrr_request *req = gd->arch.mtrr_req; struct mtrr_state state; int ret; int i; debug("%s: enabled=%d, count=%d\n", __func__, gd->arch.has_mtrr, gd->arch.mtrr_req_count); if (!gd->arch.has_mtrr) return -ENOSYS; debug("open\n"); mtrr_open(&state, do_caches); debug("open done\n"); qsort(req, gd->arch.mtrr_req_count, sizeof(*req), h_comp_mtrr); for (i = 0; i < gd->arch.mtrr_req_count; i++, req++) mtrr_set_next_var(req->type, req->start, req->size); debug("close\n"); mtrr_close(&state, do_caches); debug("mtrr done\n"); if (gd->flags & GD_FLG_RELOC) { ret = mtrr_copy_to_aps(); if (ret) return log_msg_ret("copy", ret); } return 0; } int mtrr_add_request(int type, uint64_t start, uint64_t size) { struct mtrr_request *req; uint64_t mask; debug("%s: count=%d\n", __func__, gd->arch.mtrr_req_count); if (!gd->arch.has_mtrr) return -ENOSYS; if (!is_power_of_2(size)) return -EINVAL; if (gd->arch.mtrr_req_count == MAX_MTRR_REQUESTS) return -ENOSPC; req = &gd->arch.mtrr_req[gd->arch.mtrr_req_count++]; req->type = type; req->start = start; req->size = size; debug("%d: type=%d, %08llx %08llx\n", gd->arch.mtrr_req_count - 1, req->type, req->start, req->size); mask = ~(req->size - 1); mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1; mask |= MTRR_PHYS_MASK_VALID; debug(" %016llx %016llx\n", req->start | req->type, mask); return 0; } int mtrr_get_var_count(void) { return msr_read(MSR_MTRR_CAP_MSR).lo & MSR_MTRR_CAP_VCNT; } static int get_free_var_mtrr(void) { struct msr_t maskm; int vcnt; int i; vcnt = mtrr_get_var_count(); /* Identify the first var mtrr which is not valid */ for (i = 0; i < vcnt; i++) { maskm = msr_read(MTRR_PHYS_MASK_MSR(i)); if ((maskm.lo & MTRR_PHYS_MASK_VALID) == 0) return i; } /* No free var mtrr */ return -ENOSPC; } int mtrr_set_next_var(uint type, uint64_t start, uint64_t size) { int mtrr; if (!is_power_of_2(size)) return -EINVAL; mtrr = get_free_var_mtrr(); if (mtrr < 0) return mtrr; set_var_mtrr(mtrr, type, start, size); debug("MTRR %x: start=%x, size=%x\n", mtrr, (uint)start, (uint)size); return 0; } /** enum mtrr_opcode - supported operations for mtrr_do_oper() */ enum mtrr_opcode { MTRR_OP_SET, MTRR_OP_SET_VALID, }; /** * struct mtrr_oper - An MTRR operation to perform on a CPU * * @opcode: Indicates operation to perform * @reg: MTRR reg number to select (0-7, -1 = all) * @valid: Valid value to write for MTRR_OP_SET_VALID * @base: Base value to write for MTRR_OP_SET * @mask: Mask value to write for MTRR_OP_SET */ struct mtrr_oper { enum mtrr_opcode opcode; int reg; bool valid; u64 base; u64 mask; }; static void mtrr_do_oper(void *arg) { struct mtrr_oper *oper = arg; u64 mask; switch (oper->opcode) { case MTRR_OP_SET_VALID: mask = native_read_msr(MTRR_PHYS_MASK_MSR(oper->reg)); if (oper->valid) mask |= MTRR_PHYS_MASK_VALID; else mask &= ~MTRR_PHYS_MASK_VALID; wrmsrl(MTRR_PHYS_MASK_MSR(oper->reg), mask); break; case MTRR_OP_SET: wrmsrl(MTRR_PHYS_BASE_MSR(oper->reg), oper->base); wrmsrl(MTRR_PHYS_MASK_MSR(oper->reg), oper->mask); break; } } static int mtrr_start_op(int cpu_select, struct mtrr_oper *oper) { struct mtrr_state state; int ret; mtrr_open(&state, true); ret = mp_run_on_cpus(cpu_select, mtrr_do_oper, oper); mtrr_close(&state, true); if (ret) return log_msg_ret("run", ret); return 0; } int mtrr_set_valid(int cpu_select, int reg, bool valid) { struct mtrr_oper oper; oper.opcode = MTRR_OP_SET_VALID; oper.reg = reg; oper.valid = valid; return mtrr_start_op(cpu_select, &oper); } int mtrr_set(int cpu_select, int reg, u64 base, u64 mask) { struct mtrr_oper oper; oper.opcode = MTRR_OP_SET; oper.reg = reg; oper.base = base; oper.mask = mask; return mtrr_start_op(cpu_select, &oper); }