/*-
* Copyright (c) 2019 Leandro Lupori
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <openfirm.h>
#include <stand.h>
/* PVR */
#define PVR_CPU_P8E 0x004b0000
#define PVR_CPU_P8NVL 0x004c0000
#define PVR_CPU_P8 0x004d0000
#define PVR_CPU_P9 0x004e0000
#define PVR_CPU_MASK 0xffff0000
#define PVR_ISA_207 0x0f000004
#define PVR_ISA_300 0x0f000005
#define PVR_ISA_MASK 0xffffffff
/* loader version of kernel's CPU_MAXSIZE */
#define MAX_CPUS ((uint32_t)256u)
/* Option Vectors' settings */
/* length of ignored OV */
#define OV_IGN_LEN 0
/* byte 1 (of any OV) */
#define OV_IGN 0x80
/* Option Vector 5 */
/* byte 2 */
#define OV5_LPAR 0x80
#define OV5_SPLPAR 0x40
#define OV5_DRMEM 0x20
#define OV5_LP 0x10
#define OV5_ALPHA_PART 0x08
#define OV5_DMA_DELAY 0x04
#define OV5_DONATE_CPU 0x02
#define OV5_MSI 0x01
/* 9-12: max cpus */
#define OV5_MAX_CPUS(n) ((MAX_CPUS >> (3*8 - (n)*8)) & 0xff)
/* 13-14: LoPAPR Level */
#define LOPAPR_LEVEL 0x0101 /* 1.1 */
#define OV5_LOPAPR_LEVEL(n) ((LOPAPR_LEVEL >> (8 - (n)*8)) & 0xff)
/* byte 17: Platform Facilities */
#define OV5_RNG 0x80
#define OV5_COMP_ENG 0x40
#define OV5_ENC_ENG 0x20
/* byte 21: Sub-Processors */
#define OV5_NO_SUBPROCS 0
#define OV5_SUBPROCS 1
/* byte 23: interrupt controller */
#define OV5_INTC_XICS 0
/* byte 24: MMU */
#define OV5_MMU_HPT 0
/* byte 25: HPT MMU Extensions */
#define OV5_HPT_EXT_NONE 0
/* byte 26: Radix MMU Extensions */
#define OV5_RPT_EXT_NONE 0
struct pvr {
uint32_t mask;
uint32_t val;
};
struct opt_vec_ignore {
char data[2];
} __packed;
struct opt_vec4 {
char data[3];
} __packed;
struct opt_vec5 {
char data[27];
} __packed;
static struct ibm_arch_vec {
struct pvr pvr_list[7];
uint8_t num_opts;
struct opt_vec_ignore vec1;
struct opt_vec_ignore vec2;
struct opt_vec_ignore vec3;
struct opt_vec4 vec4;
struct opt_vec5 vec5;
} __packed ibm_arch_vec = {
/* pvr_list */ {
{ PVR_CPU_MASK, PVR_CPU_P8 }, /* POWER8 */
{ PVR_CPU_MASK, PVR_CPU_P8E }, /* POWER8E */
{ PVR_CPU_MASK, PVR_CPU_P8NVL }, /* POWER8NVL */
{ PVR_CPU_MASK, PVR_CPU_P9 }, /* POWER9 */
{ PVR_ISA_MASK, PVR_ISA_207 }, /* All ISA 2.07 */
{ PVR_ISA_MASK, PVR_ISA_300 }, /* All ISA 3.00 */
{ 0, 0xffffffffu } /* terminator */
},
4, /* num_opts (4 actually means 5 option vectors) */
{ OV_IGN_LEN, OV_IGN }, /* OV1 */
{ OV_IGN_LEN, OV_IGN }, /* OV2 */
{ OV_IGN_LEN, OV_IGN }, /* OV3 */
/* OV4 (can't be ignored) */ {
sizeof(struct opt_vec4) - 2, /* length (n-2) */
0,
10 /* Minimum VP entitled capacity percentage * 100
* (if absent assume 10%) */
},
/* OV5 */ {
sizeof(struct opt_vec5) - 2, /* length (n-2) */
0, /* don't ignore */
OV5_LPAR | OV5_SPLPAR | OV5_LP | OV5_MSI,
0,
0, /* Cooperative Memory Over-commitment */
0, /* Associativity Information Option */
0, /* Binary Option Controls */
0, /* Reserved */
0, /* Reserved */
OV5_MAX_CPUS(0),
OV5_MAX_CPUS(1), /* 10 */
OV5_MAX_CPUS(2),
OV5_MAX_CPUS(3),
OV5_LOPAPR_LEVEL(0),
OV5_LOPAPR_LEVEL(1),
0, /* Reserved */
0, /* Reserved */
0, /* Platform Facilities */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */ /* 20 */
OV5_NO_SUBPROCS,
0, /* DRMEM_V2 */
OV5_INTC_XICS,
OV5_MMU_HPT,
OV5_HPT_EXT_NONE,
OV5_RPT_EXT_NONE
}
};
static __inline register_t
mfpvr(void)
{
register_t value;
__asm __volatile ("mfpvr %0" : "=r"(value));
return (value);
}
static __inline int
ppc64_hv(void)
{
int hv;
/* PSL_HV is bit 3 of 64-bit MSR */
__asm __volatile ("mfmsr %0\n\t"
"rldicl %0,%0,4,63" : "=r"(hv));
return (hv);
}
int
ppc64_cas(void)
{
int rc;
ihandle_t ihandle;
cell_t err;
/* Perform CAS only for POWER8 and later cores */
switch (mfpvr() & PVR_CPU_MASK) {
case PVR_CPU_P8:
case PVR_CPU_P8E:
case PVR_CPU_P8NVL:
case PVR_CPU_P9:
break;
default:
return (0);
}
/* Skip CAS when running on PowerNV */
if (ppc64_hv())
return (0);
ihandle = OF_open("/");
if (ihandle == -1) {
printf("cas: failed to open / node\n");
return (-1);
}
if (rc = OF_call_method("ibm,client-architecture-support",
ihandle, 1, 1, &ibm_arch_vec, &err))
printf("cas: failed to call CAS method\n");
else if (err) {
printf("cas: error: 0x%08lX\n", err);
rc = -1;
}
OF_close(ihandle);
return (rc);
}