/* SPDX-License-Identifier: GPL-2.0 */
/*
* KVM guest address space mapping code
*
* Copyright IBM Corp. 2024, 2025
* Author(s): Claudio Imbrenda <imbrenda@linux.ibm.com>
*/
#ifndef __KVM_S390_DAT_H
#define __KVM_S390_DAT_H
#include <linux/radix-tree.h>
#include <linux/refcount.h>
#include <linux/io.h>
#include <linux/kvm_types.h>
#include <linux/pgalloc.h>
#include <asm/tlbflush.h>
#include <asm/dat-bits.h>
/*
* Base address and length must be sent at the start of each block, therefore
* it's cheaper to send some clean data, as long as it's less than the size of
* two longs.
*/
#define KVM_S390_MAX_BIT_DISTANCE (2 * sizeof(void *))
/* For consistency */
#define KVM_S390_CMMA_SIZE_MAX ((u32)KVM_S390_SKEYS_MAX)
#define _ASCE(x) ((union asce) { .val = (x), })
#define NULL_ASCE _ASCE(0)
enum {
_DAT_TOKEN_NONE = 0,
_DAT_TOKEN_PIC,
};
#define _CRSTE_TOK(l, t, p) ((union crste) { \
.tok.i = 1, \
.tok.tt = (l), \
.tok.type = (t), \
.tok.par = (p) \
})
#define _CRSTE_PIC(l, p) _CRSTE_TOK(l, _DAT_TOKEN_PIC, p)
#define _CRSTE_HOLE(l) _CRSTE_PIC(l, PGM_ADDRESSING)
#define _CRSTE_EMPTY(l) _CRSTE_TOK(l, _DAT_TOKEN_NONE, 0)
#define _PMD_EMPTY _CRSTE_EMPTY(TABLE_TYPE_SEGMENT)
#define _PTE_TOK(t, p) ((union pte) { .tok.i = 1, .tok.type = (t), .tok.par = (p) })
#define _PTE_EMPTY _PTE_TOK(_DAT_TOKEN_NONE, 0)
/* This fake table type is used for page table walks (both for normal page tables and vSIE) */
#define TABLE_TYPE_PAGE_TABLE -1
enum dat_walk_flags {
DAT_WALK_USES_SKEYS = 0x40,
DAT_WALK_CONTINUE = 0x20,
DAT_WALK_IGN_HOLES = 0x10,
DAT_WALK_SPLIT = 0x08,
DAT_WALK_ALLOC = 0x04,
DAT_WALK_ANY = 0x02,
DAT_WALK_LEAF = 0x01,
DAT_WALK_DEFAULT = 0
};
#define DAT_WALK_SPLIT_ALLOC (DAT_WALK_SPLIT | DAT_WALK_ALLOC)
#define DAT_WALK_ALLOC_CONTINUE (DAT_WALK_CONTINUE | DAT_WALK_ALLOC)
#define DAT_WALK_LEAF_ALLOC (DAT_WALK_LEAF | DAT_WALK_ALLOC)
union pte {
unsigned long val;
union page_table_entry h;
struct {
unsigned long :56; /* Hardware bits */
unsigned long u : 1; /* Page unused */
unsigned long s : 1; /* Special */
unsigned long w : 1; /* Writable */
unsigned long r : 1; /* Readable */
unsigned long d : 1; /* Dirty */
unsigned long y : 1; /* Young */
unsigned long sd: 1; /* Soft dirty */
unsigned long pr: 1; /* Present */
} s;
struct {
unsigned char hwbytes[7];
unsigned char swbyte;
};
union {
struct {
unsigned long type :16; /* Token type */
unsigned long par :16; /* Token parameter */
unsigned long :20;
unsigned long : 1; /* Must be 0 */
unsigned long i : 1; /* Must be 1 */
unsigned long : 2;
unsigned long : 7;
unsigned long pr : 1; /* Must be 0 */
};
struct {
unsigned long token:32; /* Token and parameter */
unsigned long :32;
};
} tok;
};
#define _SEGMENT_FR_MASK (_SEGMENT_MASK >> PAGE_SHIFT)
#define _REGION3_FR_MASK (_REGION3_MASK >> PAGE_SHIFT)
#define _PAGES_PER_SEGMENT _PAGE_ENTRIES
#define _PAGES_PER_REGION3 (_PAGES_PER_SEGMENT * _CRST_ENTRIES)
/* Soft dirty, needed as macro for atomic operations on ptes */
#define _PAGE_SD 0x002
/* Needed as macro to perform atomic operations */
#define PGSTE_PCL_BIT 0x0080000000000000UL /* PCL lock, HW bit */
#define PGSTE_CMMA_D_BIT 0x0000000000008000UL /* CMMA dirty soft-bit */
enum pgste_gps_usage {
PGSTE_GPS_USAGE_STABLE = 0,
PGSTE_GPS_USAGE_UNUSED,
PGSTE_GPS_USAGE_POT_VOLATILE,
PGSTE_GPS_USAGE_VOLATILE,
};
union pgste {
unsigned long val;
struct {
unsigned long acc : 4;
unsigned long fp : 1;
unsigned long : 3;
unsigned long pcl : 1;
unsigned long hr : 1;
unsigned long hc : 1;
unsigned long : 2;
unsigned long gr : 1;
unsigned long gc : 1;
unsigned long : 1;
unsigned long :16; /* val16 */
unsigned long zero : 1;
unsigned long nodat : 1;
unsigned long : 4;
unsigned long usage : 2;
unsigned long : 8;
unsigned long cmma_d : 1; /* Dirty flag for CMMA bits */
unsigned long prefix_notif : 1; /* Guest prefix invalidation notification */
unsigned long vsie_notif : 1; /* Referenced in a shadow table */
unsigned long : 5;
unsigned long : 8;
};
struct {
unsigned short hwbytes0;
unsigned short val16; /* Used to store chunked values, see dat_{s,g}et_ptval() */
unsigned short hwbytes4;
unsigned char flags; /* Maps to the software bits */
unsigned char hwbyte7;
} __packed;
};
union pmd {
unsigned long val;
union segment_table_entry h;
struct {
struct {
unsigned long :44; /* HW */
unsigned long : 3<