Home | History | Annotate | Line # | Download | only in riscv
      1 /*	$NetBSD: cpu.c,v 1.12 2026/09/29 18:46:38 skrll Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2023 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Nick Hudson
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "opt_multiprocessor.h"
     33 
     34 #include <sys/cdefs.h>
     35 __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.12 2026/09/29 18:46:38 skrll Exp $");
     36 
     37 #include <sys/param.h>
     38 
     39 #include <sys/cpu.h>
     40 #include <sys/device.h>
     41 #include <sys/kmem.h>
     42 #include <sys/reboot.h>
     43 #include <sys/sysctl.h>
     44 
     45 #include <riscv/cpu.h>
     46 #include <riscv/cpuvar.h>
     47 #include <riscv/machdep.h>
     48 #include <riscv/sbi.h>
     49 #include <riscv/sysreg.h>
     50 
     51 #ifdef MULTIPROCESSOR
     52 #define NCPUINFO	MAXCPUS
     53 #else
     54 #define NCPUINFO	1
     55 #endif /* MULTIPROCESSOR */
     56 
     57 struct cpu_arch {
     58 	uint64_t	 ca_id;
     59 	const char	*ca_name;
     60 };
     61 
     62 struct cpu_arch cpu_arch_sifive[] = {
     63     {
     64 	.ca_id = CPU_SIFIVE_ARCH_7SERIES,
     65 	.ca_name = "7-Series Processor (E7, S7, U7 series)",
     66     },
     67     { },	// terminator
     68 };
     69 
     70 struct cpu_arch cpu_arch_thead[] = {
     71     {
     72 	.ca_id = 0,
     73 	.ca_name = "9-Series Processor (C9, E9 series)",
     74     },
     75     { },	// terminator
     76 };
     77 
     78 struct cpu_vendor {
     79 	uint32_t	 	 cv_id;
     80 	const char		*cv_name;
     81 	struct cpu_arch		*cv_arch;
     82 } cpu_vendors[] = {
     83     {
     84 	.cv_id = CPU_VENDOR_SIFIVE,
     85 	.cv_name = "SiFive",
     86 	.cv_arch = cpu_arch_sifive,
     87     },
     88     {
     89 	.cv_id = CPU_VENDOR_THEAD,
     90 	.cv_name = "T-Head",
     91 	.cv_arch = cpu_arch_thead,
     92     },
     93 };
     94 
     95 /*
     96  * Our exported cpu_info structs; indexed by BP as 0 and APs [1, ncpu - 1]
     97  */
     98 struct cpu_info cpu_info_store[NCPUINFO] = {
     99 	[0] = {
    100 		.ci_cpl = IPL_HIGH,
    101 		.ci_curlwp = &lwp0,
    102 		.ci_tlb_info = &pmap_tlb0_info,
    103 #ifdef MULTIPROCESSOR
    104 		.ci_flags = CPUF_PRIMARY | CPUF_PRESENT | CPUF_RUNNING,
    105 #endif
    106 	}
    107 };
    108 
    109 /*
    110  * setup the per-cpu sysctl tree.
    111  */
    112 static void
    113 cpu_setup_sysctl(device_t dv, struct cpu_info *ci)
    114 {
    115 	const struct sysctlnode *cpunode = NULL;
    116 
    117 	sysctl_createv(NULL, 0, NULL, &cpunode,
    118 		       CTLFLAG_PERMANENT,
    119 		       CTLTYPE_NODE, device_xname(dv), NULL,
    120 		       NULL, 0, NULL, 0,
    121 		       CTL_MACHDEP,
    122 		       CTL_CREATE, CTL_EOL);
    123 
    124 	if (cpunode == NULL)
    125 		return;
    126 }
    127 
    128 
    129 static void
    130 cpu_identify(device_t self, struct cpu_info *ci)
    131 {
    132 	const register_t mvendorid = sbi_get_mvendorid().value;
    133 	const register_t marchid = sbi_get_marchid().value;
    134 	const uint32_t mimpid = sbi_get_mimpid().value;
    135 	struct cpu_arch *cv_arch = NULL;
    136 	const char *cv_name = NULL;
    137 	const char *ca_name = NULL;
    138 	char vendor[128];
    139 	char arch[128];
    140 
    141 	for (size_t i = 0; i < __arraycount(cpu_vendors); i++) {
    142 		if (mvendorid == cpu_vendors[i].cv_id) {
    143 			cv_name = cpu_vendors[i].cv_name;
    144 			cv_arch = cpu_vendors[i].cv_arch;
    145 			break;
    146 		}
    147 	}
    148 
    149 	if (cv_arch != NULL) {
    150 		for (size_t i = 0; cv_arch[i].ca_name != NULL; i++) {
    151 			if (marchid == cv_arch[i].ca_id) {
    152 				ca_name = cv_arch[i].ca_name;
    153 				break;
    154 			}
    155 		}
    156 	}
    157 
    158 	if (cv_name == NULL) {
    159 		snprintf(vendor, sizeof(vendor), "vendor %" PRIxREGISTER, mvendorid);
    160 		cv_name = vendor;
    161 	}
    162 	if (ca_name == NULL) {
    163 		snprintf(arch, sizeof(arch), "arch %" PRIxREGISTER, marchid);
    164 		ca_name = arch;
    165 	}
    166 
    167 	aprint_naive("\n");
    168 	aprint_normal(": %s %s imp. %" PRIx32 "\n", cv_name, ca_name, mimpid);
    169 	aprint_verbose_dev(ci->ci_dev,
    170 	    "vendor 0x%" PRIxREGISTER " arch. %" PRIxREGISTER " imp. %" PRIx32 "\n",
    171 	    mvendorid, marchid, mimpid);
    172 }
    173 
    174 
    175 void
    176 cpu_attach(device_t dv, cpuid_t hartid)
    177 {
    178 	struct cpu_info *ci;
    179 
    180 	/* Check for the BP */
    181 	if (hartid == cpu_bphartid) {
    182 		ci = curcpu();
    183 		KASSERTMSG(ci == &cpu_info_store[0], "ci %p", ci);
    184 		ci->ci_cpuid = hartid;
    185 		ci->ci_cpu_freq = riscv_timer_frequency_get();
    186 	} else {
    187 #ifdef MULTIPROCESSOR
    188 		if ((boothowto & RB_MD1) != 0) {
    189 			aprint_naive("\n");
    190 			aprint_normal(": multiprocessor boot disabled\n");
    191 			return;
    192 		}
    193 
    194 		KASSERT(hartid < MAXCPUS);
    195 		KASSERT(cpu_hartindex[hartid] < MAXCPUS);
    196 
    197 		ci = &cpu_info_store[cpu_hartindex[hartid]];
    198 
    199 		ci->ci_cpl = IPL_HIGH;
    200 		ci->ci_cpuid = hartid;
    201 
    202 		if (!cpu_hatched_p(cpu_hartindex[hartid])) {
    203 			ci->ci_dev = dv;
    204 			device_set_private(dv, ci);
    205 			ci->ci_index = -1;
    206 
    207 			aprint_naive(": disabled\n");
    208 			aprint_normal(": disabled (unresponsive)\n");
    209 			return;
    210 		}
    211 #else /* MULTIPROCESSOR */
    212 		aprint_naive(": disabled\n");
    213 		aprint_normal(": disabled (uniprocessor kernel)\n");
    214 		return;
    215 #endif /* MULTIPROCESSOR */
    216 	}
    217 
    218 	ci->ci_dev = dv;
    219 	device_set_private(dv, ci);
    220 
    221 	const char * const xname = device_xname(dv);
    222 
    223 	evcnt_attach_dynamic(&ci->ci_ev_fpu_loads, EVCNT_TYPE_MISC, NULL,
    224 	    xname, "fpu loads");
    225 	evcnt_attach_dynamic(&ci->ci_ev_fpu_saves, EVCNT_TYPE_MISC, NULL,
    226 	    xname, "fpu saves");
    227 	evcnt_attach_dynamic(&ci->ci_ev_fpu_reenables, EVCNT_TYPE_MISC, NULL,
    228 	    xname, "fpu reenables");
    229 
    230 	cpu_identify(dv, ci);
    231 
    232 #ifdef MULTIPROCESSOR
    233 	ipi_init(ci);
    234 
    235 	kcpuset_create(&ci->ci_multicastcpus, true);
    236 	kcpuset_create(&ci->ci_watchcpus, true);
    237 	kcpuset_create(&ci->ci_ddbcpus, true);
    238 
    239 	if (hartid != cpu_bphartid) {
    240 		mi_cpu_attach(ci);
    241 	}
    242 #endif /* MULTIPROCESSOR */
    243 	cpu_setup_sysctl(dv, ci);
    244 }
    245 
    246 #ifdef MULTIPROCESSOR
    247 /*
    248  * Initialise a secondary processor.
    249  *
    250  * printf isn't available as kmutex(9) relies on curcpu which isn't setup yet.
    251  *
    252  */
    253 void __noasan
    254 cpu_init_secondary_processor(u_int cpuindex)
    255 {
    256 	cpu_set_hatched(cpuindex);
    257 
    258 	/*
    259 	 * return to assembly to wait for cpu_boot_secondary_processors
    260 	 */
    261 }
    262 
    263 
    264 /*
    265  * When we are called, the MMU and caches are on and we are running on the stack
    266  * of the idlelwp for this cpu.
    267  */
    268 void
    269 cpu_hatch(struct cpu_info *ci, unsigned long cpuindex)
    270 {
    271 	KASSERT(curcpu() == ci);
    272 
    273 	// Show this CPU as present.
    274 	atomic_or_ulong(&ci->ci_flags, CPUF_PRESENT);
    275 
    276 	ci->ci_cpu_freq = riscv_timer_frequency_get();
    277 
    278 	riscv_timer_init();
    279 
    280 	kcpuset_set(cpus_hatched, cpu_index(ci));
    281 	kcpuset_set(cpus_running, cpu_index(ci));
    282 
    283 	/*
    284 	 * clear my bit of the mailbox to tell cpu_boot_secondary_processors().
    285 	 * Consider that if there are cpu0, 1, 2, 3, and cpu2 is unresponsive,
    286 	 * ci_index for each would be cpu0=0, cpu1=1, cpu2=undef, cpu3=2.
    287 	 * therefore we have to use device_unit instead of ci_index for mbox.
    288 	 */
    289 
    290 	cpu_clr_mbox(cpuindex);
    291 }
    292 #endif /* MULTIPROCESSOR */
    293