3523 lines
108 KiB
Diff
3523 lines
108 KiB
Diff
From 02fb523c7cf1619cb53803a52ca73a56bd43f7e2 Mon Sep 17 00:00:00 2001
|
||
From: Ingo Franzki <ifranzki@linux.ibm.com>
|
||
Date: Tue, 23 Feb 2021 08:52:26 +0100
|
||
Subject: [PATCH 1/7] zkey: Fix build error when the compiler flags are
|
||
overridden
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
When the compiler flags are overridden, the build of zkey may fail with:
|
||
|
||
kms.c:44:2: error: #error KMS_PLUGIN_LOCATION must be defined
|
||
44 | #error KMS_PLUGIN_LOCATION must be defined
|
||
| ^~~~~
|
||
|
||
The Makefile uses CFLAGS variable for defining the KMS_PLUGIN_LOCATION,
|
||
but it should rather use ALL_CFLAGS.
|
||
|
||
Also use ALL_CPPFLAGS for defining HAVE_LUKS2_SUPPORT.
|
||
|
||
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/108
|
||
|
||
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||
Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com>
|
||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||
(cherry picked from commit 3f3f063c98278f53ad3b34e68b70fca62eaea8fb)
|
||
---
|
||
zkey/Makefile | 4 ++--
|
||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||
|
||
diff --git a/zkey/Makefile b/zkey/Makefile
|
||
index 41129bc..f74e209 100644
|
||
--- a/zkey/Makefile
|
||
+++ b/zkey/Makefile
|
||
@@ -18,7 +18,7 @@ ifneq (${HAVE_CRYPTSETUP2},0)
|
||
ifneq (${HAVE_OPENSSL},0)
|
||
BUILD_TARGETS += zkey-cryptsetup
|
||
INSTALL_TARGETS += install-zkey-cryptsetup
|
||
- CPPFLAGS += -DHAVE_LUKS2_SUPPORT
|
||
+ ALL_CPPFLAGS += -DHAVE_LUKS2_SUPPORT
|
||
else
|
||
BUILD_TARGETS += zkey-cryptsetup-skip-openssl
|
||
INSTALL_TARGETS += zkey-cryptsetup-skip-openssl
|
||
@@ -34,7 +34,7 @@ endif
|
||
|
||
libs = $(rootdir)/libutil/libutil.a
|
||
|
||
-CFLAGS += -DKMS_PLUGIN_LOCATION=\"$(ZKEYKMSPLUGINDIR)\"
|
||
+ALL_CFLAGS += -DKMS_PLUGIN_LOCATION=\"$(ZKEYKMSPLUGINDIR)\"
|
||
|
||
detect-libcryptsetup.dep:
|
||
echo "#include <libcryptsetup.h>" > detect-libcryptsetup.dep
|
||
--
|
||
2.31.1
|
||
|
||
|
||
From ea06d53a2d775bd1ad54482202ea021aaa93a1d8 Mon Sep 17 00:00:00 2001
|
||
From: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||
Date: Wed, 24 Feb 2021 15:04:11 +0000
|
||
Subject: [PATCH 2/7] genprotimg: use `pv_` namespace for our Buffer
|
||
implementation
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
Use `pv_` namespace for our Buffer implementation so a symbol clash with other
|
||
libraries is less likely.
|
||
|
||
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/109
|
||
Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com>
|
||
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
|
||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||
(cherry picked from commit b6bdd7744aba06d82f30b0c84012f0b06ccb01de)
|
||
---
|
||
genprotimg/src/pv/pv_comp.c | 26 +++++------
|
||
genprotimg/src/pv/pv_comp.h | 4 +-
|
||
genprotimg/src/pv/pv_comps.c | 10 ++---
|
||
genprotimg/src/pv/pv_comps.h | 4 +-
|
||
genprotimg/src/pv/pv_hdr.c | 24 +++++------
|
||
genprotimg/src/pv/pv_hdr.h | 4 +-
|
||
genprotimg/src/pv/pv_image.c | 70 +++++++++++++++---------------
|
||
genprotimg/src/pv/pv_image.h | 14 +++---
|
||
genprotimg/src/pv/pv_ipib.c | 4 +-
|
||
genprotimg/src/pv/pv_ipib.h | 2 +-
|
||
genprotimg/src/pv/pv_stage3.c | 26 +++++------
|
||
genprotimg/src/pv/pv_stage3.h | 10 ++---
|
||
genprotimg/src/utils/buffer.c | 18 ++++----
|
||
genprotimg/src/utils/buffer.h | 16 +++----
|
||
genprotimg/src/utils/crypto.c | 72 +++++++++++++++----------------
|
||
genprotimg/src/utils/crypto.h | 28 ++++++------
|
||
genprotimg/src/utils/file_utils.c | 4 +-
|
||
genprotimg/src/utils/file_utils.h | 2 +-
|
||
18 files changed, 169 insertions(+), 169 deletions(-)
|
||
|
||
diff --git a/genprotimg/src/pv/pv_comp.c b/genprotimg/src/pv/pv_comp.c
|
||
index 1f64eea..21879ae 100644
|
||
--- a/genprotimg/src/pv/pv_comp.c
|
||
+++ b/genprotimg/src/pv/pv_comp.c
|
||
@@ -73,12 +73,12 @@ PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
||
return pv_component_new(type, size, DATA_FILE, (void **)&file, err);
|
||
}
|
||
|
||
-PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
||
+PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf,
|
||
GError **err)
|
||
{
|
||
g_assert(buf);
|
||
|
||
- g_autoptr(Buffer) dup_buf = buffer_dup(buf, FALSE);
|
||
+ g_autoptr(PvBuffer) dup_buf = pv_buffer_dup(buf, FALSE);
|
||
return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf,
|
||
err);
|
||
}
|
||
@@ -90,7 +90,7 @@ void pv_component_free(PvComponent *component)
|
||
|
||
switch ((PvComponentDataType)component->d_type) {
|
||
case DATA_BUFFER:
|
||
- buffer_clear(&component->buf);
|
||
+ pv_buffer_clear(&component->buf);
|
||
break;
|
||
case DATA_FILE:
|
||
comp_file_free(component->file);
|
||
@@ -162,21 +162,21 @@ gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_pat
|
||
|
||
switch ((PvComponentDataType)component->d_type) {
|
||
case DATA_BUFFER: {
|
||
- g_autoptr(Buffer) enc_buf = NULL;
|
||
+ g_autoptr(PvBuffer) enc_buf = NULL;
|
||
|
||
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
||
- g_autoptr(Buffer) new = NULL;
|
||
+ g_autoptr(PvBuffer) new = NULL;
|
||
|
||
/* create a page aligned copy */
|
||
- new = buffer_dup(component->buf, TRUE);
|
||
- buffer_clear(&component->buf);
|
||
+ new = pv_buffer_dup(component->buf, TRUE);
|
||
+ pv_buffer_clear(&component->buf);
|
||
component->buf = g_steal_pointer(&new);
|
||
}
|
||
enc_buf = encrypt_buf(parms, component->buf, err);
|
||
if (!enc_buf)
|
||
return -1;
|
||
|
||
- buffer_clear(&component->buf);
|
||
+ pv_buffer_clear(&component->buf);
|
||
component->buf = g_steal_pointer(&enc_buf);
|
||
return 0;
|
||
}
|
||
@@ -220,10 +220,10 @@ gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
||
|
||
switch (component->d_type) {
|
||
case DATA_BUFFER: {
|
||
- g_autoptr(Buffer) buf = NULL;
|
||
+ g_autoptr(PvBuffer) buf = NULL;
|
||
|
||
- buf = buffer_dup(component->buf, TRUE);
|
||
- buffer_clear(&component->buf);
|
||
+ buf = pv_buffer_dup(component->buf, TRUE);
|
||
+ pv_buffer_clear(&component->buf);
|
||
component->buf = g_steal_pointer(&buf);
|
||
return 0;
|
||
} break;
|
||
@@ -301,7 +301,7 @@ int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
||
|
||
switch (comp->d_type) {
|
||
case DATA_BUFFER: {
|
||
- const Buffer *buf = comp->buf;
|
||
+ const PvBuffer *buf = comp->buf;
|
||
|
||
g_assert(buf->size <= INT64_MAX);
|
||
g_assert(buf->size == size);
|
||
@@ -425,7 +425,7 @@ gint pv_component_write(const PvComponent *component, FILE *f, GError **err)
|
||
|
||
switch (component->d_type) {
|
||
case DATA_BUFFER: {
|
||
- const Buffer *buf = component->buf;
|
||
+ const PvBuffer *buf = component->buf;
|
||
|
||
if (seek_and_write_buffer(f, buf, offset, err) < 0)
|
||
return -1;
|
||
diff --git a/genprotimg/src/pv/pv_comp.h b/genprotimg/src/pv/pv_comp.h
|
||
index aa1b5ae..a4ffae8 100644
|
||
--- a/genprotimg/src/pv/pv_comp.h
|
||
+++ b/genprotimg/src/pv/pv_comp.h
|
||
@@ -41,7 +41,7 @@ typedef struct {
|
||
gint d_type; /* PvComponentDataType */
|
||
union {
|
||
struct comp_file *file;
|
||
- Buffer *buf;
|
||
+ PvBuffer *buf;
|
||
void *data;
|
||
};
|
||
uint64_t src_addr;
|
||
@@ -51,7 +51,7 @@ typedef struct {
|
||
|
||
PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
||
GError **err);
|
||
-PvComponent *pv_component_new_buf(PvComponentType type, const Buffer *buf,
|
||
+PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf,
|
||
GError **err);
|
||
void pv_component_free(PvComponent *component);
|
||
gint pv_component_type(const PvComponent *component);
|
||
diff --git a/genprotimg/src/pv/pv_comps.c b/genprotimg/src/pv/pv_comps.c
|
||
index 15d32f0..2d364fd 100644
|
||
--- a/genprotimg/src/pv/pv_comps.c
|
||
+++ b/genprotimg/src/pv/pv_comps.c
|
||
@@ -210,13 +210,13 @@ GSList *pv_img_comps_get_comps(const PvImgComps *comps)
|
||
return comps->comps;
|
||
}
|
||
|
||
-gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
||
- Buffer **ald_digest, Buffer **tld_digest,
|
||
+gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest,
|
||
+ PvBuffer **ald_digest, PvBuffer **tld_digest,
|
||
uint64_t *nep, GError **err)
|
||
{
|
||
- g_autoptr(Buffer) tmp_pld_digest = NULL;
|
||
- g_autoptr(Buffer) tmp_ald_digest = NULL;
|
||
- g_autoptr(Buffer) tmp_tld_digest = NULL;
|
||
+ g_autoptr(PvBuffer) tmp_pld_digest = NULL;
|
||
+ g_autoptr(PvBuffer) tmp_ald_digest = NULL;
|
||
+ g_autoptr(PvBuffer) tmp_tld_digest = NULL;
|
||
|
||
comps->finalized = TRUE;
|
||
for (GSList *iterator = comps->comps; iterator; iterator = iterator->next) {
|
||
diff --git a/genprotimg/src/pv/pv_comps.h b/genprotimg/src/pv/pv_comps.h
|
||
index d555e36..891ecdf 100644
|
||
--- a/genprotimg/src/pv/pv_comps.h
|
||
+++ b/genprotimg/src/pv/pv_comps.h
|
||
@@ -32,8 +32,8 @@ gint pv_img_comps_add_component(PvImgComps *comps, PvComponent **comp,
|
||
GError **err);
|
||
PvComponent *pv_img_comps_get_nth_comp(PvImgComps *comps, guint n);
|
||
gint pv_img_comps_set_offset(PvImgComps *comps, gsize offset, GError **err);
|
||
-gint pv_img_comps_finalize(PvImgComps *comps, Buffer **pld_digest,
|
||
- Buffer **ald_digest, Buffer **tld_digest,
|
||
+gint pv_img_comps_finalize(PvImgComps *comps, PvBuffer **pld_digest,
|
||
+ PvBuffer **ald_digest, PvBuffer **tld_digest,
|
||
uint64_t *nep, GError **err);
|
||
void pv_img_comps_free(PvImgComps *comps);
|
||
|
||
diff --git a/genprotimg/src/pv/pv_hdr.c b/genprotimg/src/pv/pv_hdr.c
|
||
index 45e721d..e46e176 100644
|
||
--- a/genprotimg/src/pv/pv_hdr.c
|
||
+++ b/genprotimg/src/pv/pv_hdr.c
|
||
@@ -76,17 +76,17 @@ uint64_t pv_hdr_get_nks(const PvHdr *hdr)
|
||
}
|
||
|
||
/* In-place modification of ``buf`` */
|
||
-static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, Buffer *buf,
|
||
+static gint pv_hdr_encrypt(const PvHdr *hdr, const PvImage *img, PvBuffer *buf,
|
||
GError **err)
|
||
{
|
||
uint32_t hdr_len = pv_hdr_size(hdr);
|
||
uint32_t aad_len = pv_hdr_aad_size(hdr);
|
||
guint tag_len = pv_hdr_tag_size(hdr);
|
||
uint32_t enc_len = pv_hdr_enc_size_casted(hdr);
|
||
- const Buffer aad_part = { .data = buf->data, .size = aad_len };
|
||
- Buffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
||
+ const PvBuffer aad_part = { .data = buf->data, .size = aad_len };
|
||
+ PvBuffer enc_part = { .data = (uint8_t *)buf->data + aad_len,
|
||
.size = enc_len };
|
||
- Buffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len,
|
||
+ PvBuffer tag_part = { .data = (uint8_t *)buf->data + hdr_len - tag_len,
|
||
.size = tag_len };
|
||
struct cipher_parms parms;
|
||
int64_t c_len;
|
||
@@ -119,9 +119,9 @@ static gint pv_hdr_aad_init(PvHdr *hdr, const PvImage *img, GError **err)
|
||
g_autofree union ecdh_pub_key *cust_pub_key = NULL;
|
||
struct pv_hdr_key_slot *hdr_slot = hdr->slots;
|
||
struct pv_hdr_head *head = &hdr->head;
|
||
- g_autoptr(Buffer) pld = NULL;
|
||
- g_autoptr(Buffer) ald = NULL;
|
||
- g_autoptr(Buffer) tld = NULL;
|
||
+ g_autoptr(PvBuffer) pld = NULL;
|
||
+ g_autoptr(PvBuffer) ald = NULL;
|
||
+ g_autoptr(PvBuffer) tld = NULL;
|
||
uint64_t nep = 0;
|
||
|
||
g_assert(sizeof(head->iv) == img->gcm_iv->size);
|
||
@@ -250,7 +250,7 @@ PvHdr *pv_hdr_new(const PvImage *img, GError **err)
|
||
return g_steal_pointer(&ret);
|
||
}
|
||
|
||
-static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst)
|
||
+static void pv_hdr_memcpy(const PvHdr *hdr, const PvBuffer *dst)
|
||
{
|
||
uint64_t nks = pv_hdr_get_nks(hdr);
|
||
uint8_t *data;
|
||
@@ -270,13 +270,13 @@ static void pv_hdr_memcpy(const PvHdr *hdr, const Buffer *dst)
|
||
}
|
||
}
|
||
|
||
-Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||
- enum PvCryptoMode mode, GError **err)
|
||
+PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||
+ enum PvCryptoMode mode, GError **err)
|
||
{
|
||
uint32_t hdr_size = pv_hdr_size(hdr);
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
|
||
- ret = buffer_alloc(hdr_size);
|
||
+ ret = pv_buffer_alloc(hdr_size);
|
||
pv_hdr_memcpy(hdr, ret);
|
||
|
||
if (mode == PV_ENCRYPT) {
|
||
diff --git a/genprotimg/src/pv/pv_hdr.h b/genprotimg/src/pv/pv_hdr.h
|
||
index 8df7a6f..fbcc9e9 100644
|
||
--- a/genprotimg/src/pv/pv_hdr.h
|
||
+++ b/genprotimg/src/pv/pv_hdr.h
|
||
@@ -23,8 +23,8 @@
|
||
PvHdr *pv_hdr_new(const PvImage *img, GError **err);
|
||
void pv_hdr_free(PvHdr *hdr);
|
||
G_GNUC_UNUSED gboolean pv_hdr_uses_encryption(const PvHdr *hdr);
|
||
-Buffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||
- enum PvCryptoMode mode, GError **err);
|
||
+PvBuffer *pv_hdr_serialize(const PvHdr *hdr, const PvImage *img,
|
||
+ enum PvCryptoMode mode, GError **err);
|
||
uint32_t pv_hdr_size(const PvHdr *hdr);
|
||
uint32_t pv_hdr_aad_size(const PvHdr *hdr);
|
||
uint64_t pv_hdr_enc_size(const PvHdr *hdr);
|
||
diff --git a/genprotimg/src/pv/pv_image.c b/genprotimg/src/pv/pv_image.c
|
||
index 59eca5e..375e40f 100644
|
||
--- a/genprotimg/src/pv/pv_image.c
|
||
+++ b/genprotimg/src/pv/pv_image.c
|
||
@@ -56,7 +56,7 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
||
GError **err)
|
||
{
|
||
struct cipher_parms parms = { 0 };
|
||
- g_autoptr(Buffer) tweak = NULL;
|
||
+ g_autoptr(PvBuffer) tweak = NULL;
|
||
prepare_func func = NULL;
|
||
void *opaque = NULL;
|
||
gint rc;
|
||
@@ -76,7 +76,7 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
||
EVP_CIPHER_iv_length(cipher));
|
||
g_assert(img->xts_key->size <= UINT_MAX);
|
||
|
||
- tweak = buffer_alloc(sizeof(comp->tweak.data));
|
||
+ tweak = pv_buffer_alloc(sizeof(comp->tweak.data));
|
||
memcpy(tweak->data, comp->tweak.data, tweak->size);
|
||
func = pv_component_align_and_encrypt;
|
||
parms.cipher = cipher;
|
||
@@ -93,11 +93,11 @@ static gint pv_img_prepare_component(const PvImage *img, PvComponent *comp,
|
||
return 0;
|
||
}
|
||
|
||
-static Buffer *pv_img_read_key(const gchar *path, guint key_size,
|
||
- GError **err)
|
||
+static PvBuffer *pv_img_read_key(const gchar *path, guint key_size,
|
||
+ GError **err)
|
||
{
|
||
- g_autoptr(Buffer) tmp_ret = NULL;
|
||
- Buffer *ret = NULL;
|
||
+ g_autoptr(PvBuffer) tmp_ret = NULL;
|
||
+ PvBuffer *ret = NULL;
|
||
gsize bytes_read;
|
||
FILE *f = NULL;
|
||
gsize size;
|
||
@@ -116,7 +116,7 @@ static Buffer *pv_img_read_key(const gchar *path, guint key_size,
|
||
if (!f)
|
||
return NULL;
|
||
|
||
- tmp_ret = buffer_alloc(size);
|
||
+ tmp_ret = pv_buffer_alloc(size);
|
||
if (file_read(f, tmp_ret->data, 1, tmp_ret->size, &bytes_read, err) < 0)
|
||
goto err;
|
||
|
||
@@ -160,8 +160,8 @@ static HostKeyList *pv_img_get_host_keys(GSList *host_keys_with_path, gint nid,
|
||
return g_steal_pointer(&ret);
|
||
}
|
||
|
||
-static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||
- GError **err)
|
||
+static PvBuffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||
+ GError **err)
|
||
{
|
||
gint key_len = EVP_CIPHER_key_length(cipher);
|
||
|
||
@@ -173,8 +173,8 @@ static Buffer *pv_img_get_key(const EVP_CIPHER *cipher, const gchar *path,
|
||
return generate_aes_key((guint)key_len, err);
|
||
}
|
||
|
||
-static Buffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path,
|
||
- GError **err)
|
||
+static PvBuffer *pv_img_get_iv(const EVP_CIPHER *cipher, const gchar *path,
|
||
+ GError **err)
|
||
{
|
||
gint iv_len = EVP_CIPHER_iv_length(cipher);
|
||
|
||
@@ -485,23 +485,23 @@ static void pv_hdr_key_slot_free(PvHdrKeySlot *slot)
|
||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvHdrKeySlot, pv_hdr_key_slot_free)
|
||
|
||
static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
||
- const Buffer *cust_root_key,
|
||
+ const PvBuffer *cust_root_key,
|
||
EVP_PKEY *cust_key, EVP_PKEY *host_key,
|
||
GError **err)
|
||
{
|
||
g_autoptr(PvHdrKeySlot) ret = g_new0(PvHdrKeySlot, 1);
|
||
g_autofree union ecdh_pub_key *pub = NULL;
|
||
- g_autoptr(Buffer) exchange_key = NULL;
|
||
- g_autoptr(Buffer) digest_key = NULL;
|
||
- g_autoptr(Buffer) iv = NULL;
|
||
- Buffer pub_buf;
|
||
+ g_autoptr(PvBuffer) exchange_key = NULL;
|
||
+ g_autoptr(PvBuffer) digest_key = NULL;
|
||
+ g_autoptr(PvBuffer) iv = NULL;
|
||
+ PvBuffer pub_buf;
|
||
/* No AAD data is used */
|
||
- Buffer aad = { .data = NULL, .size = 0 };
|
||
+ PvBuffer aad = { .data = NULL, .size = 0 };
|
||
/* Set the output buffers for the encrypted data and the
|
||
* generated GCM tag
|
||
*/
|
||
- Buffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) };
|
||
- Buffer tag = { .data = ret->tag, .size = sizeof(ret->tag) };
|
||
+ PvBuffer enc = { .data = ret->wrapped_key, .size = sizeof(ret->wrapped_key) };
|
||
+ PvBuffer tag = { .data = ret->tag, .size = sizeof(ret->tag) };
|
||
struct cipher_parms parms;
|
||
int64_t c_len = 0;
|
||
|
||
@@ -530,7 +530,7 @@ static PvHdrKeySlot *pv_hdr_key_slot_new(const EVP_CIPHER *gcm_cipher,
|
||
g_assert(exchange_key->size == (guint)EVP_CIPHER_key_length(gcm_cipher));
|
||
|
||
/* create zero IV */
|
||
- iv = buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher));
|
||
+ iv = pv_buffer_alloc((guint)EVP_CIPHER_iv_length(gcm_cipher));
|
||
parms.iv_or_tweak = iv;
|
||
parms.key = exchange_key;
|
||
parms.cipher = gcm_cipher;
|
||
@@ -637,13 +637,13 @@ void pv_img_free(PvImage *img)
|
||
g_slist_free_full(img->key_slots, (GDestroyNotify)pv_hdr_key_slot_free);
|
||
g_slist_free_full(img->host_pub_keys, (GDestroyNotify)EVP_PKEY_free);
|
||
EVP_PKEY_free(img->cust_pub_priv_key);
|
||
- buffer_clear(&img->stage3a);
|
||
+ pv_buffer_clear(&img->stage3a);
|
||
pv_img_comps_free(img->comps);
|
||
g_free(img->tmp_dir);
|
||
- buffer_free(img->xts_key);
|
||
- buffer_free(img->cust_root_key);
|
||
- buffer_free(img->gcm_iv);
|
||
- buffer_free(img->cust_comm_key);
|
||
+ pv_buffer_free(img->xts_key);
|
||
+ pv_buffer_free(img->cust_root_key);
|
||
+ pv_buffer_free(img->gcm_iv);
|
||
+ pv_buffer_free(img->cust_comm_key);
|
||
g_free(img);
|
||
}
|
||
|
||
@@ -684,13 +684,13 @@ gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err)
|
||
return 0;
|
||
}
|
||
|
||
-gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
||
- Buffer **tld, uint64_t *nep, GError **err)
|
||
+gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald,
|
||
+ PvBuffer **tld, uint64_t *nep, GError **err)
|
||
{
|
||
return pv_img_comps_finalize(img->comps, pld, ald, tld, nep, err);
|
||
}
|
||
|
||
-static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err)
|
||
+static gint pv_img_build_stage3b(PvImage *img, PvBuffer *stage3b, GError **err)
|
||
{
|
||
g_autofree struct stage3b_args *args = NULL;
|
||
|
||
@@ -708,7 +708,7 @@ static gint pv_img_build_stage3b(PvImage *img, Buffer *stage3b, GError **err)
|
||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err)
|
||
{
|
||
g_autoptr(PvComponent) comp = NULL;
|
||
- g_autoptr(Buffer) stage3b = NULL;
|
||
+ g_autoptr(PvBuffer) stage3b = NULL;
|
||
|
||
stage3b = stage3b_getblob(path, err);
|
||
if (!stage3b)
|
||
@@ -825,7 +825,7 @@ static gint get_stage3a_data_size(const PvImage *img, gsize *data_size,
|
||
|
||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
||
{
|
||
- g_autoptr(Buffer) stage3a = NULL;
|
||
+ g_autoptr(PvBuffer) stage3a = NULL;
|
||
gsize bin_size, data_size = 0;
|
||
|
||
if (get_stage3a_data_size(img, &data_size, err) < 0)
|
||
@@ -845,8 +845,8 @@ gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err)
|
||
}
|
||
|
||
/* Creates the PV IPIB and sets the stage3a arguments */
|
||
-static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size,
|
||
- GSList *comps, const Buffer *hdr, GError **err)
|
||
+static gint pv_img_build_stage3a(PvBuffer *stage3a, gsize stage3a_bin_size,
|
||
+ GSList *comps, const PvBuffer *hdr, GError **err)
|
||
{
|
||
g_autofree struct ipl_parameter_block *ipib = NULL;
|
||
|
||
@@ -866,9 +866,9 @@ static gint pv_img_build_stage3a(Buffer *stage3a, gsize stage3a_bin_size,
|
||
}
|
||
|
||
/* Creates the actual PV header (serialized and AES-GCM encrypted) */
|
||
-static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||
+static PvBuffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||
{
|
||
- g_autoptr(Buffer) hdr_buf = NULL;
|
||
+ g_autoptr(PvBuffer) hdr_buf = NULL;
|
||
g_autoptr(PvHdr) hdr = NULL;
|
||
|
||
hdr = pv_hdr_new(img, err);
|
||
@@ -887,7 +887,7 @@ static Buffer *pv_img_create_pv_hdr(PvImage *img, GError **err)
|
||
*/
|
||
gint pv_img_finalize(PvImage *pv, const gchar *stage3b_path, GError **err)
|
||
{
|
||
- g_autoptr(Buffer) hdr = NULL;
|
||
+ g_autoptr(PvBuffer) hdr = NULL;
|
||
|
||
/* load stage3b template into memory and add it to the list of
|
||
* components. This must be done before calling
|
||
diff --git a/genprotimg/src/pv/pv_image.h b/genprotimg/src/pv/pv_image.h
|
||
index 7c624e2..116fb1a 100644
|
||
--- a/genprotimg/src/pv/pv_image.h
|
||
+++ b/genprotimg/src/pv/pv_image.h
|
||
@@ -25,7 +25,7 @@
|
||
|
||
typedef struct {
|
||
gchar *tmp_dir; /* directory used for temporary files */
|
||
- Buffer *stage3a; /* stage3a containing IPIB and PV header */
|
||
+ PvBuffer *stage3a; /* stage3a containing IPIB and PV header */
|
||
gsize stage3a_bin_size; /* size of stage3a.bin */
|
||
struct psw_t stage3a_psw; /* (short) PSW that is written to
|
||
* location 0 of the created image
|
||
@@ -35,15 +35,15 @@ typedef struct {
|
||
GSList *host_pub_keys; /* public host keys */
|
||
gint nid; /* Elliptic Curve used for the key derivation */
|
||
/* keys and cipher used for the AES-GCM encryption */
|
||
- Buffer *cust_root_key;
|
||
- Buffer *gcm_iv;
|
||
+ PvBuffer *cust_root_key;
|
||
+ PvBuffer *gcm_iv;
|
||
const EVP_CIPHER *gcm_cipher;
|
||
/* Information for the IPIB and PV header */
|
||
uint64_t pcf;
|
||
uint64_t scf;
|
||
- Buffer *cust_comm_key;
|
||
+ PvBuffer *cust_comm_key;
|
||
const EVP_CIPHER *cust_comm_cipher;
|
||
- Buffer *xts_key;
|
||
+ PvBuffer *xts_key;
|
||
const EVP_CIPHER *xts_cipher;
|
||
GSList *key_slots;
|
||
GSList *optional_items;
|
||
@@ -54,8 +54,8 @@ PvImage *pv_img_new(PvArgs *args, const gchar *stage3a_path, GError **err);
|
||
void pv_img_free(PvImage *img);
|
||
gint pv_img_add_component(PvImage *img, const PvArg *arg, GError **err);
|
||
gint pv_img_finalize(PvImage *img, const gchar *stage3b_path, GError **err);
|
||
-gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, Buffer **pld, Buffer **ald,
|
||
- Buffer **tld, uint64_t *nep, GError **err);
|
||
+gint pv_img_calc_pld_ald_tld_nep(const PvImage *img, PvBuffer **pld, PvBuffer **ald,
|
||
+ PvBuffer **tld, uint64_t *nep, GError **err);
|
||
gint pv_img_load_and_set_stage3a(PvImage *img, const gchar *path, GError **err);
|
||
const PvComponent *pv_img_get_stage3b_comp(const PvImage *img, GError **err);
|
||
gint pv_img_add_stage3b_comp(PvImage *img, const gchar *path, GError **err);
|
||
diff --git a/genprotimg/src/pv/pv_ipib.c b/genprotimg/src/pv/pv_ipib.c
|
||
index 2517e54..59fe008 100644
|
||
--- a/genprotimg/src/pv/pv_ipib.c
|
||
+++ b/genprotimg/src/pv/pv_ipib.c
|
||
@@ -35,7 +35,7 @@ uint64_t pv_ipib_get_size(uint32_t num_comp)
|
||
}
|
||
|
||
static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
||
- const Buffer *hdr)
|
||
+ const PvBuffer *hdr)
|
||
{
|
||
g_assert(sizeof(struct ipl_pl_hdr) <= UINT32_MAX);
|
||
g_assert(sizeof(struct ipl_pb0_pv_comp) <= UINT32_MAX);
|
||
@@ -100,7 +100,7 @@ static gint pv_ipib_init(IplParameterBlock *ipib, GSList *comps,
|
||
return 0;
|
||
}
|
||
|
||
-IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err)
|
||
+IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err)
|
||
{
|
||
uint64_t ipib_size = pv_ipib_get_size(g_slist_length(comps));
|
||
g_autoptr(IplParameterBlock) ret = NULL;
|
||
diff --git a/genprotimg/src/pv/pv_ipib.h b/genprotimg/src/pv/pv_ipib.h
|
||
index 9331790..4b66643 100644
|
||
--- a/genprotimg/src/pv/pv_ipib.h
|
||
+++ b/genprotimg/src/pv/pv_ipib.h
|
||
@@ -19,7 +19,7 @@
|
||
typedef struct ipl_parameter_block IplParameterBlock;
|
||
|
||
uint64_t pv_ipib_get_size(uint32_t num_comp);
|
||
-IplParameterBlock *pv_ipib_new(GSList *comps, const Buffer *hdr, GError **err);
|
||
+IplParameterBlock *pv_ipib_new(GSList *comps, const PvBuffer *hdr, GError **err);
|
||
void pv_ipib_free(IplParameterBlock *ipib);
|
||
|
||
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(IplParameterBlock, pv_ipib_free)
|
||
diff --git a/genprotimg/src/pv/pv_stage3.c b/genprotimg/src/pv/pv_stage3.c
|
||
index a1e5b16..bff9db7 100644
|
||
--- a/genprotimg/src/pv/pv_stage3.c
|
||
+++ b/genprotimg/src/pv/pv_stage3.c
|
||
@@ -24,12 +24,12 @@
|
||
((struct stage3a_args *)((uint64_t)data_ptr + loader_size - \
|
||
sizeof(struct stage3a_args)))
|
||
|
||
-static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||
- gsize args_size, gsize data_size,
|
||
- gboolean data_aligned, GError **err)
|
||
+static PvBuffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||
+ gsize args_size, gsize data_size,
|
||
+ gboolean data_aligned, GError **err)
|
||
{
|
||
g_autoptr(GMappedFile) mapped_file = NULL;
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
gsize size, tmp_loader_size;
|
||
gchar *loader_data;
|
||
|
||
@@ -60,7 +60,7 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||
size = (data_aligned ? PAGE_ALIGN(tmp_loader_size) : tmp_loader_size) +
|
||
data_size;
|
||
|
||
- ret = buffer_alloc(size);
|
||
+ ret = pv_buffer_alloc(size);
|
||
|
||
/* copy the loader "template" */
|
||
memcpy(ret->data, loader_data, tmp_loader_size);
|
||
@@ -71,8 +71,8 @@ static Buffer *loader_getblob(const gchar *filename, gsize *loader_size,
|
||
return g_steal_pointer(&ret);
|
||
}
|
||
|
||
-Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||
- gsize data_size, GError **err)
|
||
+PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||
+ gsize data_size, GError **err)
|
||
{
|
||
return loader_getblob(filename, loader_size,
|
||
sizeof(struct stage3a_args), data_size, TRUE,
|
||
@@ -83,8 +83,8 @@ Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||
/* Set the right offsets and sizes in the stage3a template + add
|
||
* the IPIB block with the PV header
|
||
*/
|
||
-static gint stage3a_set_data(Buffer *loader, gsize loader_size,
|
||
- const Buffer *hdr, struct ipl_parameter_block *ipib,
|
||
+static gint stage3a_set_data(PvBuffer *loader, gsize loader_size,
|
||
+ const PvBuffer *hdr, struct ipl_parameter_block *ipib,
|
||
GError **err)
|
||
{
|
||
uint32_t ipib_size = GUINT32_FROM_BE(ipib->hdr.len);
|
||
@@ -126,15 +126,15 @@ static gint stage3a_set_data(Buffer *loader, gsize loader_size,
|
||
return 0;
|
||
}
|
||
|
||
-gint build_stage3a(Buffer *loader, gsize loader_size, const Buffer *hdr,
|
||
+gint build_stage3a(PvBuffer *loader, gsize loader_size, const PvBuffer *hdr,
|
||
struct ipl_parameter_block *ipib, GError **err)
|
||
{
|
||
return stage3a_set_data(loader, loader_size, hdr, ipib, err);
|
||
}
|
||
|
||
-Buffer *stage3b_getblob(const gchar *filename, GError **err)
|
||
+PvBuffer *stage3b_getblob(const gchar *filename, GError **err)
|
||
{
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
gsize rb_size;
|
||
|
||
ret = loader_getblob(filename, &rb_size, sizeof(struct stage3b_args), 0,
|
||
@@ -146,7 +146,7 @@ Buffer *stage3b_getblob(const gchar *filename, GError **err)
|
||
return g_steal_pointer(&ret);
|
||
}
|
||
|
||
-void build_stage3b(Buffer *stage3b, const struct stage3b_args *args)
|
||
+void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args)
|
||
{
|
||
g_assert(stage3b->size > sizeof(*args));
|
||
|
||
diff --git a/genprotimg/src/pv/pv_stage3.h b/genprotimg/src/pv/pv_stage3.h
|
||
index baaf921..364408e 100644
|
||
--- a/genprotimg/src/pv/pv_stage3.h
|
||
+++ b/genprotimg/src/pv/pv_stage3.h
|
||
@@ -19,12 +19,12 @@
|
||
#include "boot/stage3b.h"
|
||
#include "utils/buffer.h"
|
||
|
||
-Buffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||
- gsize data_size, GError **err);
|
||
-gint build_stage3a(Buffer *dc, gsize dc_size, const Buffer *hdr,
|
||
+PvBuffer *stage3a_getblob(const gchar *filename, gsize *loader_size,
|
||
+ gsize data_size, GError **err);
|
||
+gint build_stage3a(PvBuffer *dc, gsize dc_size, const PvBuffer *hdr,
|
||
struct ipl_parameter_block *ipib, GError **err);
|
||
-Buffer *stage3b_getblob(const gchar *filename, GError **err);
|
||
-void build_stage3b(Buffer *stage3b, const struct stage3b_args *args);
|
||
+PvBuffer *stage3b_getblob(const gchar *filename, GError **err);
|
||
+void build_stage3b(PvBuffer *stage3b, const struct stage3b_args *args);
|
||
void memblob_init(struct memblob *arg, uint64_t src, uint64_t size);
|
||
|
||
#endif
|
||
diff --git a/genprotimg/src/utils/buffer.c b/genprotimg/src/utils/buffer.c
|
||
index 35aed74..509dc0d 100644
|
||
--- a/genprotimg/src/utils/buffer.c
|
||
+++ b/genprotimg/src/utils/buffer.c
|
||
@@ -17,18 +17,18 @@
|
||
#include "common.h"
|
||
#include "file_utils.h"
|
||
|
||
-Buffer *buffer_alloc(gsize size)
|
||
+PvBuffer *pv_buffer_alloc(gsize size)
|
||
{
|
||
- Buffer *ret = g_new0(Buffer, 1);
|
||
+ PvBuffer *ret = g_new0(PvBuffer, 1);
|
||
|
||
ret->data = g_malloc0(size);
|
||
ret->size = size;
|
||
return ret;
|
||
}
|
||
|
||
-Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned)
|
||
+PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned)
|
||
{
|
||
- Buffer *ret;
|
||
+ PvBuffer *ret;
|
||
gsize size;
|
||
|
||
if (!buf)
|
||
@@ -38,19 +38,19 @@ Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned)
|
||
if (page_aligned)
|
||
size = PAGE_ALIGN(size);
|
||
|
||
- ret = buffer_alloc(size);
|
||
+ ret = pv_buffer_alloc(size);
|
||
|
||
/* content will be 0-right-padded */
|
||
memcpy(ret->data, buf->data, buf->size);
|
||
return ret;
|
||
}
|
||
|
||
-gint buffer_write(const Buffer *buf, FILE *file, GError **err)
|
||
+gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err)
|
||
{
|
||
return file_write(file, buf->data, buf->size, 1, NULL, err);
|
||
}
|
||
|
||
-void buffer_free(Buffer *buf)
|
||
+void pv_buffer_free(PvBuffer *buf)
|
||
{
|
||
if (!buf)
|
||
return;
|
||
@@ -59,11 +59,11 @@ void buffer_free(Buffer *buf)
|
||
g_free(buf);
|
||
}
|
||
|
||
-void buffer_clear(Buffer **buf)
|
||
+void pv_buffer_clear(PvBuffer **buf)
|
||
{
|
||
if (!buf || !*buf)
|
||
return;
|
||
|
||
- buffer_free(*buf);
|
||
+ pv_buffer_free(*buf);
|
||
*buf = NULL;
|
||
}
|
||
diff --git a/genprotimg/src/utils/buffer.h b/genprotimg/src/utils/buffer.h
|
||
index 7239d5c..824b72c 100644
|
||
--- a/genprotimg/src/utils/buffer.h
|
||
+++ b/genprotimg/src/utils/buffer.h
|
||
@@ -15,17 +15,17 @@
|
||
|
||
#include "common.h"
|
||
|
||
-typedef struct Buffer {
|
||
+typedef struct PvBuffer {
|
||
void *data;
|
||
gsize size; /* in bytes */
|
||
-} Buffer;
|
||
+} PvBuffer;
|
||
|
||
-Buffer *buffer_alloc(gsize size);
|
||
-void buffer_free(Buffer *buf);
|
||
-void buffer_clear(Buffer **buf);
|
||
-gint buffer_write(const Buffer *buf, FILE *file, GError **err);
|
||
-Buffer *buffer_dup(const Buffer *buf, gboolean page_aligned);
|
||
+PvBuffer *pv_buffer_alloc(gsize size);
|
||
+void pv_buffer_free(PvBuffer *buf);
|
||
+void pv_buffer_clear(PvBuffer **buf);
|
||
+gint pv_buffer_write(const PvBuffer *buf, FILE *file, GError **err);
|
||
+PvBuffer *pv_buffer_dup(const PvBuffer *buf, gboolean page_aligned);
|
||
|
||
-WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(Buffer, buffer_free)
|
||
+WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(PvBuffer, pv_buffer_free)
|
||
|
||
#endif
|
||
diff --git a/genprotimg/src/utils/crypto.c b/genprotimg/src/utils/crypto.c
|
||
index 44facc2..05c3e83 100644
|
||
--- a/genprotimg/src/utils/crypto.c
|
||
+++ b/genprotimg/src/utils/crypto.c
|
||
@@ -89,15 +89,15 @@ EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err)
|
||
return g_steal_pointer(&ctx);
|
||
}
|
||
|
||
-Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||
+PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||
{
|
||
gint md_size = EVP_MD_size(EVP_MD_CTX_md(ctx));
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
guint digest_size;
|
||
|
||
g_assert(md_size > 0);
|
||
|
||
- ret = buffer_alloc((guint)md_size);
|
||
+ ret = pv_buffer_alloc((guint)md_size);
|
||
if (EVP_DigestFinal_ex(ctx, ret->data, &digest_size) != 1) {
|
||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_INTERNAL,
|
||
_("EVP_DigestFinal_ex failed"));
|
||
@@ -110,10 +110,10 @@ Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err)
|
||
}
|
||
|
||
/* Returns the digest of @buf using the hash algorithm @md */
|
||
-static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err)
|
||
+static PvBuffer *digest_buffer(const EVP_MD *md, const PvBuffer *buf, GError **err)
|
||
{
|
||
g_autoptr(EVP_MD_CTX) md_ctx = NULL;
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
g_assert(buf);
|
||
|
||
md_ctx = digest_ctx_new(md, err);
|
||
@@ -134,9 +134,9 @@ static Buffer *digest_buffer(const EVP_MD *md, const Buffer *buf, GError **err)
|
||
}
|
||
|
||
/* Returns the SHA256 digest of @buf */
|
||
-Buffer *sha256_buffer(const Buffer *buf, GError **err)
|
||
+PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err)
|
||
{
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
|
||
ret = digest_buffer(EVP_sha256(), buf, err);
|
||
if (!ret)
|
||
@@ -207,10 +207,10 @@ union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err)
|
||
return g_steal_pointer(&ret);
|
||
}
|
||
|
||
-static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||
+static PvBuffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||
{
|
||
g_autoptr(EVP_PKEY_CTX) ctx = NULL;
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
gsize key_size;
|
||
|
||
ctx = EVP_PKEY_CTX_new(cust, NULL);
|
||
@@ -236,7 +236,7 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||
return NULL;
|
||
}
|
||
|
||
- ret = buffer_alloc(key_size);
|
||
+ ret = pv_buffer_alloc(key_size);
|
||
if (EVP_PKEY_derive(ctx, ret->data, &key_size) != 1) {
|
||
g_set_error(err, PV_CRYPTO_ERROR, PV_CRYPTO_ERROR_DERIVE,
|
||
_("Key derivation failed"));
|
||
@@ -247,11 +247,11 @@ static Buffer *derive_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||
return g_steal_pointer(&ret);
|
||
}
|
||
|
||
-Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||
+PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err)
|
||
{
|
||
- g_autoptr(Buffer) raw = buffer_alloc(70);
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
- g_autoptr(Buffer) key = NULL;
|
||
+ g_autoptr(PvBuffer) raw = pv_buffer_alloc(70);
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) key = NULL;
|
||
guchar *data;
|
||
|
||
key = derive_key(cust, host, err);
|
||
@@ -290,10 +290,10 @@ gint generate_tweak(union tweak *tweak, uint16_t i, GError **err)
|
||
return 0;
|
||
}
|
||
|
||
-static Buffer *generate_rand_data(guint size, const gchar *err_msg,
|
||
- GError **err)
|
||
+static PvBuffer *generate_rand_data(guint size, const gchar *err_msg,
|
||
+ GError **err)
|
||
{
|
||
- g_autoptr(Buffer) buf = buffer_alloc(size);
|
||
+ g_autoptr(PvBuffer) buf = pv_buffer_alloc(size);
|
||
|
||
g_assert(size <= INT_MAX);
|
||
|
||
@@ -307,14 +307,14 @@ static Buffer *generate_rand_data(guint size, const gchar *err_msg,
|
||
return g_steal_pointer(&buf);
|
||
}
|
||
|
||
-Buffer *generate_aes_iv(guint size, GError **err)
|
||
+PvBuffer *generate_aes_iv(guint size, GError **err)
|
||
{
|
||
return generate_rand_data(size,
|
||
_("Generating a IV failed because the required amount of random data is not available"),
|
||
err);
|
||
}
|
||
|
||
-Buffer *generate_aes_key(guint size, GError **err)
|
||
+PvBuffer *generate_aes_key(guint size, GError **err)
|
||
{
|
||
return generate_rand_data(size,
|
||
_("Generating a key failed because the required amount of random data is not available"),
|
||
@@ -1756,8 +1756,8 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||
gint cipher_block_size = EVP_CIPHER_block_size(cipher);
|
||
guchar in_buf[PAGE_SIZE],
|
||
out_buf[PAGE_SIZE + (guint)cipher_block_size];
|
||
- const Buffer *key = parms->key;
|
||
- const Buffer *tweak = parms->iv_or_tweak;
|
||
+ const PvBuffer *key = parms->key;
|
||
+ const PvBuffer *tweak = parms->iv_or_tweak;
|
||
g_autofree guchar *tmp_tweak = NULL;
|
||
gint out_len, tweak_size;
|
||
gsize tmp_size_in = 0, tmp_size_out = 0;
|
||
@@ -1895,11 +1895,11 @@ static gint __encrypt_decrypt_bio(const struct cipher_parms *parms, BIO *b_in,
|
||
return 0;
|
||
}
|
||
|
||
-static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||
- const Buffer *in, gboolean encrypt,
|
||
- GError **err)
|
||
+static PvBuffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||
+ const PvBuffer *in, gboolean encrypt,
|
||
+ GError **err)
|
||
{
|
||
- g_autoptr(Buffer) ret = NULL;
|
||
+ g_autoptr(PvBuffer) ret = NULL;
|
||
g_autoptr(BIO) b_out = NULL;
|
||
g_autoptr(BIO) b_in = NULL;
|
||
gsize in_size, out_size;
|
||
@@ -1927,19 +1927,19 @@ static Buffer *__encrypt_decrypt_buffer(const struct cipher_parms *parms,
|
||
return NULL;
|
||
}
|
||
|
||
- ret = buffer_alloc((unsigned long)data_size);
|
||
+ ret = pv_buffer_alloc((unsigned long)data_size);
|
||
memcpy(ret->data, data, ret->size);
|
||
return g_steal_pointer(&ret);
|
||
}
|
||
|
||
-Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||
- GError **err)
|
||
+PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||
+ GError **err)
|
||
{
|
||
return __encrypt_decrypt_buffer(parms, in, TRUE, err);
|
||
}
|
||
|
||
-Buffer *decrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||
- GError **err)
|
||
+PvBuffer *decrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||
+ GError **err)
|
||
{
|
||
return __encrypt_decrypt_buffer(parms, in, FALSE, err);
|
||
}
|
||
@@ -1993,16 +1993,16 @@ G_GNUC_UNUSED static gint decrypt_file(const struct cipher_parms *parms,
|
||
}
|
||
|
||
/* GCM mode uses (zero-)padding */
|
||
-static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad,
|
||
+static int64_t gcm_encrypt_decrypt(const PvBuffer *in, const PvBuffer *aad,
|
||
const struct cipher_parms *parms,
|
||
- Buffer *out, Buffer *tag,
|
||
+ PvBuffer *out, PvBuffer *tag,
|
||
enum PvCryptoMode mode, GError **err)
|
||
{
|
||
g_autoptr(EVP_CIPHER_CTX) ctx = NULL;
|
||
const EVP_CIPHER *cipher = parms->cipher;
|
||
- const Buffer *iv = parms->iv_or_tweak;
|
||
+ const PvBuffer *iv = parms->iv_or_tweak;
|
||
gboolean encrypt = mode == PV_ENCRYPT;
|
||
- const Buffer *key = parms->key;
|
||
+ const PvBuffer *key = parms->key;
|
||
int64_t ret = -1;
|
||
gint len = -1;
|
||
|
||
@@ -2097,8 +2097,8 @@ static int64_t gcm_encrypt_decrypt(const Buffer *in, const Buffer *aad,
|
||
return ret;
|
||
}
|
||
|
||
-int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
||
- const struct cipher_parms *parms, Buffer *out, Buffer *tag,
|
||
+int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
||
+ const struct cipher_parms *parms, PvBuffer *out, PvBuffer *tag,
|
||
GError **err)
|
||
{
|
||
return gcm_encrypt_decrypt(in, aad, parms, out, tag, PV_ENCRYPT, err);
|
||
diff --git a/genprotimg/src/utils/crypto.h b/genprotimg/src/utils/crypto.h
|
||
index 286cf45..3cda450 100644
|
||
--- a/genprotimg/src/utils/crypto.h
|
||
+++ b/genprotimg/src/utils/crypto.h
|
||
@@ -117,8 +117,8 @@ union tweak {
|
||
|
||
struct cipher_parms {
|
||
const EVP_CIPHER *cipher;
|
||
- const Buffer *key;
|
||
- const Buffer *iv_or_tweak;
|
||
+ const PvBuffer *key;
|
||
+ const PvBuffer *iv_or_tweak;
|
||
};
|
||
|
||
int check_crl_valid_for_cert(X509_CRL *crl, X509 *cert,
|
||
@@ -152,24 +152,24 @@ X509_CRL *get_first_valid_crl(X509_STORE_CTX *ctx, X509 *cert, GError **err);
|
||
void store_setup_crl_download(X509_STORE *st);
|
||
EVP_PKEY *read_ec_pubkey_cert(X509 *cert, gint nid, GError **err);
|
||
|
||
-Buffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
||
-Buffer *generate_aes_key(guint size, GError **err);
|
||
-Buffer *generate_aes_iv(guint size, GError **err);
|
||
+PvBuffer *compute_exchange_key(EVP_PKEY *cust, EVP_PKEY *host, GError **err);
|
||
+PvBuffer *generate_aes_key(guint size, GError **err);
|
||
+PvBuffer *generate_aes_iv(guint size, GError **err);
|
||
EVP_PKEY *generate_ec_key(gint nid, GError **err);
|
||
gint generate_tweak(union tweak *tweak, uint16_t i, GError **err);
|
||
union ecdh_pub_key *evp_pkey_to_ecdh_pub_key(EVP_PKEY *key, GError **err);
|
||
EVP_MD_CTX *digest_ctx_new(const EVP_MD *md, GError **err);
|
||
-Buffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
||
-Buffer *sha256_buffer(const Buffer *buf, GError **err);
|
||
-int64_t gcm_encrypt(const Buffer *in, const Buffer *aad,
|
||
- const struct cipher_parms *parms, Buffer *out,
|
||
- Buffer *tag, GError **err);
|
||
+PvBuffer *digest_ctx_finalize(EVP_MD_CTX *ctx, GError **err);
|
||
+PvBuffer *sha256_buffer(const PvBuffer *buf, GError **err);
|
||
+int64_t gcm_encrypt(const PvBuffer *in, const PvBuffer *aad,
|
||
+ const struct cipher_parms *parms, PvBuffer *out,
|
||
+ PvBuffer *tag, GError **err);
|
||
gint encrypt_file(const struct cipher_parms *parms, const gchar *in_path,
|
||
const gchar *path_out, gsize *in_size, gsize *out_size,
|
||
GError **err);
|
||
-Buffer *encrypt_buf(const struct cipher_parms *parms, const Buffer *in,
|
||
- GError **err);
|
||
-G_GNUC_UNUSED Buffer *decrypt_buf(const struct cipher_parms *parms,
|
||
- const Buffer *in, GError **err);
|
||
+PvBuffer *encrypt_buf(const struct cipher_parms *parms, const PvBuffer *in,
|
||
+ GError **err);
|
||
+G_GNUC_UNUSED PvBuffer *decrypt_buf(const struct cipher_parms *parms,
|
||
+ const PvBuffer *in, GError **err);
|
||
|
||
#endif
|
||
diff --git a/genprotimg/src/utils/file_utils.c b/genprotimg/src/utils/file_utils.c
|
||
index 1d6fc37..ba33400 100644
|
||
--- a/genprotimg/src/utils/file_utils.c
|
||
+++ b/genprotimg/src/utils/file_utils.c
|
||
@@ -171,13 +171,13 @@ err:
|
||
return ret;
|
||
}
|
||
|
||
-gint seek_and_write_buffer(FILE *o, const Buffer *buf, uint64_t offset,
|
||
+gint seek_and_write_buffer(FILE *o, const PvBuffer *buf, uint64_t offset,
|
||
GError **err)
|
||
{
|
||
if (file_seek(o, offset, err) < 0)
|
||
return -1;
|
||
|
||
- if (buffer_write(buf, o, err) < 0)
|
||
+ if (pv_buffer_write(buf, o, err) < 0)
|
||
return -1;
|
||
|
||
return 0;
|
||
diff --git a/genprotimg/src/utils/file_utils.h b/genprotimg/src/utils/file_utils.h
|
||
index 47df114..456e7ac 100644
|
||
--- a/genprotimg/src/utils/file_utils.h
|
||
+++ b/genprotimg/src/utils/file_utils.h
|
||
@@ -26,7 +26,7 @@ gint file_write(FILE *out, const void *ptr, gsize size, gsize count,
|
||
gsize *count_written, GError **err);
|
||
gint pad_file_right(const gchar *path_out, const gchar *path_in,
|
||
gsize *size_out, guint padding, GError **err);
|
||
-gint seek_and_write_buffer(FILE *out, const Buffer *buf, uint64_t offset,
|
||
+gint seek_and_write_buffer(FILE *out, const PvBuffer *buf, uint64_t offset,
|
||
GError **err);
|
||
gint seek_and_write_file(FILE *o, const CompFile *ifile, uint64_t offset,
|
||
GError **err);
|
||
--
|
||
2.31.1
|
||
|
||
|
||
From 5997d2b96e05fd8d228bd05a8b56850822747944 Mon Sep 17 00:00:00 2001
|
||
From: =?UTF-8?q?Jan=20H=C3=B6ppner?= <hoeppner@linux.ibm.com>
|
||
Date: Fri, 26 Feb 2021 16:47:23 +0100
|
||
Subject: [PATCH 3/7] ttyrun-getty: Avoid conflicts with serial-getty@
|
||
(#1907781)
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
Starting ttyrun-getty@ will fail as it conflicts with the serial-getty@
|
||
service. Add Conflicts= option to avoid any conflicts.
|
||
|
||
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/105
|
||
Suggested-by: Dan Horák <dan@danny.cz>
|
||
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
|
||
(cherry picked from commit d23558f1d1b8268f1ebcb4c05c692a89f2a433c0)
|
||
---
|
||
systemd/ttyrun-getty@.service.in | 2 +-
|
||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||
|
||
diff --git a/systemd/ttyrun-getty@.service.in b/systemd/ttyrun-getty@.service.in
|
||
index eb4299b..37554b4 100644
|
||
--- a/systemd/ttyrun-getty@.service.in
|
||
+++ b/systemd/ttyrun-getty@.service.in
|
||
@@ -16,7 +16,7 @@ After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service
|
||
After=rc-local.service
|
||
Before=getty.target
|
||
IgnoreOnIsolate=yes
|
||
-
|
||
+Conflicts=serial-getty@%i.service
|
||
|
||
[Service]
|
||
Environment=TERM=linux
|
||
--
|
||
2.31.1
|
||
|
||
|
||
From 3040c1e6c8962b1aca1291586de304ba3ee6d67f Mon Sep 17 00:00:00 2001
|
||
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
||
Date: Fri, 2 Jul 2021 11:46:33 +0200
|
||
Subject: [PATCH 4/7] zdsfs: transparent dataset conversion (#1919238)
|
||
|
||
Summary: zdsfs: transparent dataset conversion
|
||
Description: The zdsfs tool is used to access z/OS data sets from
|
||
Linux. Many text based data sets in z/OS are EBCDIC
|
||
encoded. To perform actual work in Linux those data sets
|
||
usually have to be converted to ASCII. This is currently
|
||
done by first copying the data set to Linux using zdsfs
|
||
and run a separate conversion tool afterwards.
|
||
This item aims to improve the user experience by allowing
|
||
the user to do the conversion while the data is
|
||
transferred using zdsfs.
|
||
Upstream-ID: 4b0403a9630dd64d93efcf443d2e5231458a812e
|
||
Upstream-ID: cdf716a7a92b33c6bb64e66da2a4914fc2bd80f9
|
||
Upstream-ID: 7244785279ddcf66cf1da92f6167a824e2ee119c
|
||
Upstream-ID: 0fafbcf3bb44a49a7eea41011d60049f61063c00
|
||
Upstream-ID: 2ece47ee1ac3d5b448680264d948f6c50a76af56
|
||
|
||
Problem-ID: IO1811
|
||
---
|
||
include/lib/libzds.h | 7 +-
|
||
libzds/libzds.c | 186 +++++++++++++++++++-
|
||
zdsfs/zdsfs.1 | 167 ++++++++++++------
|
||
zdsfs/zdsfs.c | 399 ++++++++++++++++++++++++++++++++++++++++++-
|
||
4 files changed, 697 insertions(+), 62 deletions(-)
|
||
|
||
diff --git a/include/lib/libzds.h b/include/lib/libzds.h
|
||
index 9135485..2647107 100644
|
||
--- a/include/lib/libzds.h
|
||
+++ b/include/lib/libzds.h
|
||
@@ -115,7 +115,7 @@
|
||
#include "lib/util_base.h"
|
||
#include "lib/util_list.h"
|
||
#include "vtoc.h"
|
||
-
|
||
+#include <iconv.h>
|
||
|
||
|
||
/**
|
||
@@ -834,6 +834,11 @@ void lzds_dshandle_get_errorlog(struct dshandle *dsh, struct errorlog **log);
|
||
int lzds_dshandle_set_seekbuffer(struct dshandle *dsh,
|
||
unsigned long long seek_buffer_size);
|
||
|
||
+/**
|
||
+ * @brief Set iconv handle for codepage conversion.
|
||
+ */
|
||
+int lzds_dshandle_set_iconv(struct dshandle *dsh, iconv_t *iconv);
|
||
+
|
||
/**
|
||
* @brief Get the size of the data set in number of tracks (sum of all extents).
|
||
*/
|
||
diff --git a/libzds/libzds.c b/libzds/libzds.c
|
||
index a6c6912..dbf810d 100644
|
||
--- a/libzds/libzds.c
|
||
+++ b/libzds/libzds.c
|
||
@@ -61,6 +61,9 @@ struct errorlog {
|
||
|
||
#define BUSIDSIZE 8
|
||
|
||
+#define EBCDIC_SP 0x40
|
||
+#define EBCDIC_LF 0x25
|
||
+
|
||
/**
|
||
* @brief An internal structure that represents an entry in the error log.
|
||
*/
|
||
@@ -307,6 +310,8 @@ struct dshandle {
|
||
struct errorlog *log;
|
||
|
||
char *session_ref;
|
||
+ iconv_t *iconv;
|
||
+ char *convbuffer;
|
||
};
|
||
|
||
/** @endcond */
|
||
@@ -2812,6 +2817,38 @@ int lzds_dshandle_set_keepRDW(struct dshandle *dsh, int keepRDW)
|
||
return 0;
|
||
}
|
||
|
||
+/**
|
||
+ * @pre The dsh must not be open when this function is called.
|
||
+ *
|
||
+ * @param[in] dsh The dshandle we want to modify.
|
||
+ * @param[in] iconv_t The iconv handle for codepage conversion.
|
||
+ *
|
||
+ * @return 0 on success, otherwise one of the following error codes:
|
||
+ * - EBUSY The handle is already open.
|
||
+ */
|
||
+int lzds_dshandle_set_iconv(struct dshandle *dsh, iconv_t *iconv)
|
||
+{
|
||
+ errorlog_clear(dsh->log);
|
||
+ if (dsh->is_open)
|
||
+ return errorlog_add_message(
|
||
+ &dsh->log, NULL, EBUSY,
|
||
+ "dshandle: cannot set iconv while handle is open\n");
|
||
+
|
||
+ /*
|
||
+ * if conversion is enabled the returned data might in worst case
|
||
+ * be 4 times the size of the input buffer. So realloc the buffer.
|
||
+ * If for whatever very unlikely reason the converted size is still
|
||
+ * larger the conversion will fail.
|
||
+ */
|
||
+ if (iconv) {
|
||
+ dsh->databufmax *= 4;
|
||
+ dsh->databuffer = util_realloc(dsh->databuffer,
|
||
+ dsh->databufmax);
|
||
+ }
|
||
+ dsh->iconv = iconv;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
/**
|
||
* @param[in] dsh The dshandle that we want to know the member of.
|
||
* @param[out] keepRDW Reference to a variable in which the previously
|
||
@@ -2946,6 +2983,8 @@ void lzds_dshandle_close(struct dshandle *dsh)
|
||
for (i = 0; i < MAXVOLUMESPERDS; ++i)
|
||
if (dsh->dasdhandle[i])
|
||
lzds_dasdhandle_close(dsh->dasdhandle[i]);
|
||
+ free(dsh->convbuffer);
|
||
+ free(dsh->iconv);
|
||
dsh->is_open = 0;
|
||
}
|
||
|
||
@@ -3280,10 +3319,130 @@ int lzds_dshandle_open(struct dshandle *dsh)
|
||
return rc;
|
||
}
|
||
}
|
||
+ if (dsh->iconv)
|
||
+ dsh->convbuffer = util_zalloc(dsh->databufmax);
|
||
+
|
||
dsh->is_open = 1;
|
||
return 0;
|
||
}
|
||
|
||
+/**
|
||
+ * @brief subroutine of parse_fixed_record for codepage conversion
|
||
+ *
|
||
+ * Converts the provided data from one codepage to another using iconv.
|
||
+ * Stores converted data directly in the target buffer.
|
||
+ * Adds a linebreak at the end of each record to end the line.
|
||
+ * Also remove trailing spaces.
|
||
+ *
|
||
+ * @param[in] dsh The dshandle that keeps track of the I/O operations.
|
||
+ * @param[in] rec Pointer to the record buffer.
|
||
+ * @param[in] targetdata Pointer to the data buffer.
|
||
+ * @return Number of copied data bytes on success,
|
||
+ * otherwise one of the following (negative) error codes:
|
||
+ * - -EPROTO The record is malformed.
|
||
+ */
|
||
+static ssize_t convert_fixed_record(struct dshandle *dsh,
|
||
+ char *rec, char *targetdata)
|
||
+{
|
||
+ struct eckd_count *ecount = (struct eckd_count *)rec;
|
||
+ size_t in_count, out_count, max_count;
|
||
+ int reclen, blocksize, reccount;
|
||
+ char *inbuf, *outbuf;
|
||
+ char *src, *target;
|
||
+ size_t rc;
|
||
+ int i;
|
||
+
|
||
+ blocksize = ecount->dl;
|
||
+ reclen = dsh->ds->dsp[0]->f1->DS1LRECL;
|
||
+ reccount = blocksize / reclen;
|
||
+
|
||
+ outbuf = targetdata;
|
||
+ out_count = max_count =
|
||
+ (unsigned long)dsh->databuffer + dsh->databufmax -
|
||
+ (unsigned long)targetdata;
|
||
+ in_count = 0;
|
||
+ inbuf = dsh->convbuffer;
|
||
+
|
||
+ /* skip block header */
|
||
+ src = (rec + sizeof(*ecount) + ecount->kl);
|
||
+ target = inbuf;
|
||
+ /* for each record aka line */
|
||
+ for (i = 0; i < reccount; i++) {
|
||
+ /* remove trailing spaces */
|
||
+ while (reclen && (*(src + reclen - 1) == EBCDIC_SP))
|
||
+ reclen--;
|
||
+ /* move remaining data and add linebreak at end of record */
|
||
+ memcpy(target, src, reclen);
|
||
+ target += reclen;
|
||
+ *target = EBCDIC_LF;
|
||
+ target++;
|
||
+ /* count how much chars remain after whitespace cleanup */
|
||
+ in_count += reclen + 1;
|
||
+
|
||
+ /* reset for next line */
|
||
+ reclen = dsh->ds->dsp[0]->f1->DS1LRECL;
|
||
+ src += reclen;
|
||
+ }
|
||
+ /* convert directly into target buffer */
|
||
+ rc = iconv(*(dsh->iconv), &inbuf, &in_count, &outbuf, &out_count);
|
||
+ if ((rc == (size_t) -1) || (in_count != 0))
|
||
+ return -errorlog_add_message(
|
||
+ &dsh->log, NULL, EPROTO,
|
||
+ "fixed record parser: codepage conversion failed\n");
|
||
+ /* return how much was written in the target buffer */
|
||
+ return max_count - out_count;
|
||
+}
|
||
+
|
||
+/**
|
||
+ * @brief subroutine of parse_variable_record for codepage conversion
|
||
+ *
|
||
+ * Converts the record data from one codepage to another using iconv.
|
||
+ * Stores converted data directly in the target buffer
|
||
+ *
|
||
+ * @param[in] dsh The dshandle that keeps track of the I/O operations.
|
||
+ * @param[in] reclen Length of the record.
|
||
+ * @param[in] rec Pointer to the record buffer.
|
||
+ * @param[in] targetdata Pointer to the data buffer.
|
||
+ * @return Number of copied data bytes on success,
|
||
+ * otherwise one of the following (negative) error codes:
|
||
+ * - -EPROTO The record is malformed.
|
||
+ */
|
||
+static ssize_t convert_variable_record(struct dshandle *dsh, int reclen,
|
||
+ char *rec, char *targetdata)
|
||
+{
|
||
+ size_t in_count, out_count, max_count;
|
||
+ char *inbuf, *outbuf;
|
||
+ size_t rc;
|
||
+
|
||
+ inbuf = rec;
|
||
+ outbuf = targetdata;
|
||
+ in_count = reclen + 1;
|
||
+ out_count = max_count =
|
||
+ (unsigned long)dsh->databuffer + dsh->databufmax -
|
||
+ (unsigned long)targetdata;
|
||
+ /*
|
||
+ * we can not overwrite the track end marker since it is still used
|
||
+ * for this case we have to make a copy of the source data to add the
|
||
+ * linebreak
|
||
+ */
|
||
+ if (inbuf[reclen] == 0xFF) {
|
||
+ inbuf = dsh->convbuffer;
|
||
+ memcpy(inbuf, rec, reclen);
|
||
+ }
|
||
+
|
||
+ /* add linebreak */
|
||
+ inbuf[reclen] = 0x25;
|
||
+
|
||
+ rc = iconv(*(dsh->iconv), &inbuf, &in_count, &outbuf, &out_count);
|
||
+ if ((rc == (size_t) -1) || (in_count != 0))
|
||
+ return -errorlog_add_message(
|
||
+ &dsh->log, NULL, EPROTO,
|
||
+ "variable record parser: codepage conversion failed\n");
|
||
+
|
||
+ /* return how much was written in the target buffer */
|
||
+ return max_count - out_count;
|
||
+}
|
||
+
|
||
/**
|
||
* @brief subroutine of dshandle_extract_data_from_trackbuffer
|
||
*
|
||
@@ -3298,6 +3457,7 @@ static ssize_t parse_fixed_record(struct dshandle *dsh,
|
||
char *rec, char *targetdata)
|
||
{
|
||
struct eckd_count *ecount;
|
||
+ int count;
|
||
|
||
ecount = (struct eckd_count *)rec;
|
||
/* Make sure that we do not copy data beyond the end of
|
||
@@ -3308,8 +3468,13 @@ static ssize_t parse_fixed_record(struct dshandle *dsh,
|
||
return - errorlog_add_message(
|
||
&dsh->log, NULL, EPROTO,
|
||
"fixed record to long for target buffer\n");
|
||
- memcpy(targetdata, (rec + sizeof(*ecount) + ecount->kl), ecount->dl);
|
||
- return ecount->dl;
|
||
+ if (dsh->iconv) {
|
||
+ count = convert_fixed_record(dsh, rec, targetdata);
|
||
+ } else {
|
||
+ memcpy(targetdata, (rec + sizeof(*ecount) + ecount->kl), ecount->dl);
|
||
+ count = ecount->dl;
|
||
+ }
|
||
+ return count;
|
||
}
|
||
|
||
/**
|
||
@@ -3333,6 +3498,7 @@ static ssize_t parse_variable_record(struct dshandle *dsh, char *rec,
|
||
struct segment_header *blockhead;
|
||
struct segment_header *seghead;
|
||
size_t totaldatalength;
|
||
+ int count;
|
||
|
||
/* We must not rely on the data in rec, as it was read from disk and
|
||
* may be broken. Wherever we interprete the data we must have sanity
|
||
@@ -3398,9 +3564,19 @@ static ssize_t parse_variable_record(struct dshandle *dsh, char *rec,
|
||
&dsh->log, NULL, EPROTO,
|
||
"variable record parser: "
|
||
"record to long for target buffer\n");
|
||
- memcpy(targetdata, data, segmentlength);
|
||
- targetdata += segmentlength;
|
||
- totaldatalength += segmentlength;
|
||
+ if (dsh->iconv) {
|
||
+ count = convert_variable_record(dsh, segmentlength,
|
||
+ data, targetdata);
|
||
+ if (count < 0)
|
||
+ return count;
|
||
+
|
||
+ totaldatalength += count;
|
||
+ targetdata += count;
|
||
+ } else {
|
||
+ memcpy(targetdata, data, segmentlength);
|
||
+ totaldatalength += segmentlength;
|
||
+ targetdata += segmentlength;
|
||
+ }
|
||
data += segmentlength;
|
||
}
|
||
return totaldatalength;
|
||
diff --git a/zdsfs/zdsfs.1 b/zdsfs/zdsfs.1
|
||
index 677bea5..21acb70 100644
|
||
--- a/zdsfs/zdsfs.1
|
||
+++ b/zdsfs/zdsfs.1
|
||
@@ -32,29 +32,23 @@ Only read access is supported.
|
||
|
||
Data sets on tape devices are not supported.
|
||
|
||
-To maintain data consistency, a DASD must not be modified while it is
|
||
-in use by zdsfs. This can be assured by varying the device offline
|
||
-in z/OS before setting it online in Linux or by using z/OSMF REST
|
||
-services to notify z/OS about access to data sets.
|
||
-
|
||
-Device access by Linux is not subject to RACF or other
|
||
-z/OS auditing mechanisms unless the z/OSMF REST services are used for
|
||
-a coordinated read access.
|
||
-The safety of the data on the device must be established by
|
||
-therespective Linux mechanisms. By default, zdsfs grants access to the
|
||
-files in the fuse file system only to the user who started the
|
||
-tool. This behavior can be changed by using the options `allow_other',
|
||
-`default_permissions', `umask', `uid', and `gid'.
|
||
-
|
||
-When using the z/OSMF REST services for coordinated read access, a
|
||
-connection is established for every opened file. The z/OSMF REST
|
||
-services confirm that the z/OS userid that is specified in the .netrc
|
||
-configuration file has the required access rights for the data set.
|
||
-Using this mechanism also an exclusive ENQ is obtained to mark the
|
||
-data set as in use to z/OS. The ENQ prevents z/OS applications from
|
||
-modifying the data set during zdsfs access.
|
||
-If the ENQ cannot be obtained, the access from Linux fails with an
|
||
-error.
|
||
+If the z/OSMF REST services are not used for a coordinated-read
|
||
+access, access to the device by Linux is not subject to RACF or any
|
||
+other z/OS auditing mechanism. Ensure the security of the data on the
|
||
+device by using the well-known Linux methods. By default, zdsfs grants
|
||
+access to the files in the fuse file system only to the user who
|
||
+started the tool. Configure file access behavior by using the
|
||
+‘allow_other’, ‘default_permissions’, ‘umask’, ‘uid’, and ‘gid’
|
||
+options.
|
||
+
|
||
+When using the z/OSMF REST services for coordinated read access, every
|
||
+file open establishes a connection to the z/OSMF REST server. This
|
||
+checks that the accessing z/OS user ID has the appropriate access
|
||
+rights for the data set. Using this mechanism, an exclusive ENQ is
|
||
+obtained that marks the data set as in-use to z/OS. An ENQ is a z/OS
|
||
+method to control a serially reusable resource. While the ENQ is held,
|
||
+no z/OS application can modify the data set. If the ENQ cannot be
|
||
+obtained, the access attempt from Linux fails.
|
||
|
||
Only physical sequential (PS) and partitioned data sets (PDS) are
|
||
supported. Supported record formats are: V, F, U, B, S, A, and M.
|
||
@@ -171,20 +165,50 @@ instance.
|
||
|
||
.TP
|
||
\fB\-c\fR \fI<config_file>\fR
|
||
-zdsfs configuration file. The default is /etc/zdsfs.conf.
|
||
+Provide a configuration file for zdsfs. The default is /etc/zdsfs.conf.
|
||
|
||
.TP
|
||
\fB\-o\fR restapi
|
||
-Make zdsfs use z/OSMF REST services for coordinated read access to
|
||
-data sets. The user credentials are taken from .netrc file in the
|
||
-user's home directory or where the NETRC environment variable points
|
||
+Use z/OSMF REST services for coordinated read-access to data sets. The
|
||
+user credentials are read from the .netrc file in the user's home
|
||
+directory, or from the location the NETRC environment variable points
|
||
to.
|
||
|
||
.TP
|
||
\fB\-o\fR restserver=<server_URL>
|
||
-Specify up to 3 server URLs to z/OSMF REST services.
|
||
-For multiple specifications, the URLs are tried sequentially, and the
|
||
-first functioning URL is used.
|
||
+Specify up to three server URLs to z/OSMF REST services. If more than
|
||
+one server is specified, the first that responds is used. If a server
|
||
+does not respond any longer during operation all specified server are probed again.
|
||
+
|
||
+.TP
|
||
+\fB\-x\fR \fI<dataset_file>\fR
|
||
+Use a config file with code-page conversion options for
|
||
+specific data sets.
|
||
+
|
||
+.TP
|
||
+\fB\-o\fR codepage_convert
|
||
+Enable code-page conversion for all data sets. Unless specified
|
||
+otherwise, the source code-page is EBCDIC CP1047 and the target
|
||
+code-page is UTF-8.
|
||
+
|
||
+You can change the default for the source code-page with the -o
|
||
+codepage_from option and for the target code-page with the -o
|
||
+codepage_to option. To specify source and target code-pages for
|
||
+individual data sets use a data set config file.
|
||
+
|
||
+.TP
|
||
+\fB\-o\fR codepage_from=\fI<n>\fR
|
||
+Override EBCDIC CP1047 as the default code-page for the source. Must
|
||
+be combined with \fB\-o\fR codepage_to=\fI<n>\fR. Overrides settings
|
||
+in a data set config file. Issue iconv -l for a list of valid
|
||
+specifications for <n>.
|
||
+
|
||
+.TP
|
||
+\fB\-o\fR codepage_to=\fI<n>\fR
|
||
+Override UTF-8 as the default code-page for the target. Must be
|
||
+combined with \fB\-o\fR codepage_from=\fI<n>\fR. Overrides settings
|
||
+in a data set configu file. Issue iconv -l for a list of valid
|
||
+specifications for <n>.
|
||
|
||
.SS "Applicable FUSE options (version 2.8):"
|
||
This is a selected subset of all FUSE options. Use the zdsfs
|
||
@@ -273,44 +297,81 @@ directory:
|
||
|
||
\fBuser.dsorg\fR: The data set organization of a file.
|
||
|
||
-.SH zdsfs configuration file
|
||
-
|
||
-The default search path is /etc/zdsfs.conf.
|
||
-Use the \fB\-c\fR \fI<config_file>\fR option to specify other zdsfs
|
||
-configuration file locations.
|
||
+.SH zdsfs config file
|
||
|
||
+The default path is /etc/zdsfs.conf. Specify a different zdsfs config
|
||
+file location with the \fB\-c\fR \fI<config_file>\fR option.
|
||
.br
|
||
-
|
||
-The configuration file can contain the following options:
|
||
+The config file may contain the following options:
|
||
.PP
|
||
|
||
.B restapi
|
||
=
|
||
.IR 0 / 1
|
||
.IP
|
||
-Setting this option to 1 enables the z/OSMF REST services.
|
||
-The z/OSMF REST services require a valid URL specification for a REST
|
||
-server, and a .netrc file with a valid z/OS user ID and password.
|
||
+Determines whether the z/OSMF REST services should be used. If
|
||
+enabled, a valid REST server must be specified, as well as a .netrc
|
||
+file with a valid z/OS user ID and password.
|
||
.PP
|
||
|
||
.B restserver
|
||
=
|
||
.IR URL
|
||
.IP
|
||
-Specifies the URL of the z/OSMF REST server that is
|
||
-used for coordinated read access. For failover, up to 3 different
|
||
-server addresses can be provided.
|
||
+Specifies the address of the z/OSMF REST server that is used for
|
||
+coordinating read-access. For failover scenarios, provide up to three
|
||
+different server addresses. These will be tried in the specified order
|
||
+when one of the servers cannot be reached during mount or operation.
|
||
.PP
|
||
|
||
.B keepalive
|
||
=
|
||
-.I timeout
|
||
-(in seconds)
|
||
+.I timeout (in seconds)
|
||
+.IP
|
||
+Optionally change the keepalive timer for ENQs. By default the
|
||
+keepalive refreshes the access after 540 seconds (9 minutes).The 9
|
||
+minutes are chosen to prevent a timeout by z/OS after 10 minutes.
|
||
+
|
||
+.SH data-set config file <dataset_file>
|
||
+Provides code-page conversion settings for individual data sets.
|
||
+The default config file is /etc/zdsfs-dataset.conf. Use the \fB\-x\fR
|
||
+\fI<dataset_file>\fR option to specify a different file.
|
||
+.br
|
||
+Each config-file entry must contain the following options:
|
||
+.PP
|
||
+
|
||
+.B
|
||
+DATASET.TITLE
|
||
+.IP
|
||
+Specifies the data-set title or a pattern of titles to which the entry
|
||
+applies. The title can have a trailing asterisk to match all titles
|
||
+that begin with the leading characters.
|
||
+.PP
|
||
+
|
||
+.B conv
|
||
+=
|
||
+.IR 0 / 1 / <codepage_from>,<codepage_to>
|
||
+.IP
|
||
+\fI0\fR disables code-page conversion.
|
||
+.br
|
||
+\fI1\fR performs conversion with the default conversion table.
|
||
+.br
|
||
+An explicit specification of source and target code-page overrides the default conversion tables
|
||
+The code-page specifications must be separated by a comma.
|
||
+Issue iconv -l for a list of valid code-page specifications.
|
||
+
|
||
+.PP
|
||
+
|
||
+.B rdw
|
||
+=
|
||
+.I 0 / 1
|
||
.IP
|
||
-Specifies the keepalive timer for ENQs.
|
||
-By default the timer is set to 540 seconds to prevent the ENQ from a
|
||
-timeout after 10 minutes in case access to the data set takes longer
|
||
-than this.
|
||
+\fI0\fR omits the record descriptor word from the data stream.
|
||
+.br
|
||
+\fI1\fR keeps the record descriptor word from the data stream.
|
||
+.br
|
||
+Code-page conversion can render data unreadable if the record descriptor word is kept.
|
||
+
|
||
|
||
.SH EXAMPLES
|
||
To mount the z/OS disk with the name dasde enter:
|
||
@@ -341,9 +402,15 @@ assuming the z/OS disk was mounted on /mnt:
|
||
|
||
To mount the z/OS disk using the z/OSMF REST services for coordinated
|
||
read access:
|
||
+
|
||
+ # ./zdsfs -o restapi -o restserver=zos1.server.tld/zosmf /dev/dasde /mnt/
|
||
+
|
||
.br
|
||
+To mount the z/OS disk and enable code-page conversion for all data
|
||
+sets using a custom source and target code page:
|
||
+
|
||
+ # ./zdsfs -o codepage_from=CP037 -o codepage_to=ISO-8859-1 /dev/dasde /mnt/
|
||
|
||
- # ./zdsfs -o restapi -o restserver=zos1.server.tld/zosmf /dev/dasde /mnt/
|
||
|
||
.SH SEE ALSO
|
||
getfattr(1), fuse(8), z/OS DFSMS Using Data Sets,
|
||
diff --git a/zdsfs/zdsfs.c b/zdsfs/zdsfs.c
|
||
index 87c1bea..ceadd34 100644
|
||
--- a/zdsfs/zdsfs.c
|
||
+++ b/zdsfs/zdsfs.c
|
||
@@ -25,6 +25,8 @@
|
||
#include <time.h>
|
||
#include <signal.h>
|
||
|
||
+#include <iconv.h>
|
||
+
|
||
#ifdef HAVE_SETXATTR
|
||
#include <linux/xattr.h>
|
||
#endif
|
||
@@ -34,6 +36,7 @@
|
||
#include "lib/zt_common.h"
|
||
|
||
#define COMP "zdsfs: "
|
||
+
|
||
#define METADATAFILE "metadata.txt"
|
||
|
||
/* defaults for file and directory permissions (octal) */
|
||
@@ -42,6 +45,10 @@
|
||
/* default timer interval 9 minutes, enq times out after 10 minutes */
|
||
#define DEFAULT_KEEPALIVE_SEC 540
|
||
|
||
+#define SECTION_ENTRIES 3
|
||
+static char CODEPAGE_EDF[] = "CP1047";
|
||
+static char CODEPAGE_LINUX[] = "UTF-8";
|
||
+
|
||
struct zdsfs_info {
|
||
int devcount;
|
||
int allow_inclomplete_multi_volume;
|
||
@@ -61,6 +68,12 @@ struct zdsfs_info {
|
||
int active_server;
|
||
char *server[MAX_SERVER];
|
||
long keepalive;
|
||
+ int codepage_convert;
|
||
+ char *codepage_from;
|
||
+ char *codepage_to;
|
||
+ iconv_t iconv;
|
||
+ struct util_list *dsclist;
|
||
+ char *dsfile;
|
||
};
|
||
|
||
static struct zdsfs_info zdsfsinfo;
|
||
@@ -82,6 +95,18 @@ struct zdsfs_file_info {
|
||
size_t metaread; /* how many bytes have already been read */
|
||
};
|
||
|
||
+struct dsconvert {
|
||
+ char *name;
|
||
+ char *codepage_from;
|
||
+ char *codepage_to;
|
||
+ bool keeprdw;
|
||
+};
|
||
+
|
||
+struct dsc_node {
|
||
+ struct util_list_node node;
|
||
+ struct dsconvert *dsc;
|
||
+};
|
||
+
|
||
/* Allocate and initialize a new list of struct dsh_node. */
|
||
static struct util_list *dshlist_alloc(void)
|
||
{
|
||
@@ -144,6 +169,65 @@ void dshlist_remove(struct util_list *list, struct dshandle *dsh)
|
||
}
|
||
}
|
||
|
||
+/* Allocate and initialize a new list of struct dsc_node. */
|
||
+static struct util_list *dsclist_alloc(void)
|
||
+{
|
||
+ struct util_list *list;
|
||
+
|
||
+ list = util_malloc(sizeof(struct util_list));
|
||
+ util_list_init(list, struct dsc_node, node);
|
||
+
|
||
+ return list;
|
||
+}
|
||
+
|
||
+/* free struct dsconvert. */
|
||
+static void dsc_free(struct dsconvert *dsc)
|
||
+{
|
||
+ free(dsc->name);
|
||
+ free(dsc->codepage_from);
|
||
+ free(dsc->codepage_to);
|
||
+ free(dsc);
|
||
+}
|
||
+
|
||
+/* free list of struct dsc_node. */
|
||
+static void dsclist_free(struct util_list *list)
|
||
+{
|
||
+ struct dsc_node *s, *n;
|
||
+
|
||
+ if (!list)
|
||
+ return;
|
||
+
|
||
+ util_list_iterate_safe(list, s, n) {
|
||
+ util_list_remove(list, s);
|
||
+ dsc_free(s->dsc);
|
||
+ free(s);
|
||
+ }
|
||
+
|
||
+ free(list);
|
||
+}
|
||
+
|
||
+/* add dsc to list */
|
||
+static void dsclist_add(struct util_list *list, struct dsconvert *dsc)
|
||
+{
|
||
+ struct dsc_node *s;
|
||
+
|
||
+ s = util_malloc(sizeof(struct dsc_node));
|
||
+ s->dsc = dsc;
|
||
+ util_list_add_tail(list, s);
|
||
+}
|
||
+
|
||
+/* Find a dsc_node by name. */
|
||
+static struct dsc_node *dsclist_find_by_name(struct util_list *list, char *name)
|
||
+{
|
||
+ struct dsc_node *s;
|
||
+
|
||
+ util_list_iterate(list, s) {
|
||
+ if (strcmp(s->dsc->name, name) == 0)
|
||
+ return s;
|
||
+ }
|
||
+
|
||
+ return NULL;
|
||
+}
|
||
|
||
/* normalize the given path name to a dataset name
|
||
* so that we can compare it to the names in the vtoc. This means:
|
||
@@ -176,6 +260,16 @@ static void path_to_member_name(const char *path, char *normds, size_t size)
|
||
}
|
||
}
|
||
|
||
+static int setup_iconv(iconv_t *conv, const char *from, const char *to)
|
||
+{
|
||
+ *conv = iconv_open(to, from);
|
||
+ if (*conv == ((iconv_t) -1)) {
|
||
+ fprintf(stderr, "error when setting up iconv\n");
|
||
+ return -1;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
static void setup_timer(long sec)
|
||
{
|
||
static struct itimerval timer;
|
||
@@ -531,6 +625,102 @@ static int zdsfs_test_restserver(void)
|
||
return 0;
|
||
}
|
||
|
||
+/*
|
||
+ * check if a dsconvert entry exists that match the given DS name
|
||
+ * if the dsconvert entry ends in an asterisk only match the prefix
|
||
+ * otherwise look for an exact match
|
||
+ */
|
||
+static struct dsconvert *zdsfs_get_matching_dsc(char *name)
|
||
+{
|
||
+ struct dsconvert *dsc;
|
||
+ unsigned int length;
|
||
+ struct dsc_node *n;
|
||
+ char *match;
|
||
+
|
||
+ util_list_iterate(zdsfsinfo.dsclist, n) {
|
||
+ dsc = n->dsc;
|
||
+ match = dsc->name;
|
||
+ length = strlen(match);
|
||
+ if (strcmp(&match[length - 1], "*") == 0)
|
||
+ length--;
|
||
+ else if (strlen(name) != length)
|
||
+ continue;
|
||
+
|
||
+ if (strncmp(name, match, length) == 0)
|
||
+ return dsc;
|
||
+ }
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Setup iconv conversion for a given dataset.
|
||
+ * The codepage settings can be obtained from (in descending priority)
|
||
+ * - globally set codepage settings
|
||
+ * - a dsconvert entry matching the DS name
|
||
+ * - default codepage settings.
|
||
+ */
|
||
+static int zdsfs_setup_conversion(struct dshandle *dsh, struct dataset *ds)
|
||
+{
|
||
+ struct dsconvert *dsc;
|
||
+ const char *from, *to;
|
||
+ struct errorlog *log;
|
||
+ iconv_t *iconv;
|
||
+ char *dsname;
|
||
+ int rc;
|
||
+
|
||
+ from = to = NULL;
|
||
+ lzds_dataset_get_name(ds, &dsname);
|
||
+ dsc = zdsfs_get_matching_dsc(dsname);
|
||
+ /* the DS matches a dsconvert entry */
|
||
+ if (dsc) {
|
||
+ from = dsc->codepage_from;
|
||
+ to = dsc->codepage_to;
|
||
+ }
|
||
+
|
||
+ /* globally set conversion overwriting possible config file settings */
|
||
+ if (zdsfsinfo.codepage_from && zdsfsinfo.codepage_to) {
|
||
+ from = zdsfsinfo.codepage_from;
|
||
+ to = zdsfsinfo.codepage_to;
|
||
+ }
|
||
+
|
||
+ /*
|
||
+ * globally set conversion using defaults
|
||
+ * if not specified otherwise already
|
||
+ */
|
||
+ if (zdsfsinfo.codepage_convert) {
|
||
+ if (!from)
|
||
+ from = CODEPAGE_EDF;
|
||
+ if (!to)
|
||
+ to = CODEPAGE_LINUX;
|
||
+ }
|
||
+
|
||
+ /* no conversion */
|
||
+ if (!from || !to)
|
||
+ return 0;
|
||
+
|
||
+ iconv = util_malloc(sizeof(*iconv));
|
||
+ rc = setup_iconv(iconv, from, to);
|
||
+ if (rc) {
|
||
+ fprintf(stderr, "Error when preparing iconv setting:\n");
|
||
+ lzds_dshandle_get_errorlog(dsh, &log);
|
||
+ lzds_errorlog_fprint(log, stderr);
|
||
+ rc = -rc;
|
||
+ goto out;
|
||
+ }
|
||
+ rc = lzds_dshandle_set_iconv(dsh, iconv);
|
||
+ if (rc) {
|
||
+ fprintf(stderr, "Error when setting iconv handle:\n");
|
||
+ lzds_dshandle_get_errorlog(dsh, &log);
|
||
+ lzds_errorlog_fprint(log, stderr);
|
||
+ rc = -rc;
|
||
+ goto out;
|
||
+ }
|
||
+ return 0;
|
||
+out:
|
||
+ free(iconv);
|
||
+ return rc;
|
||
+}
|
||
+
|
||
|
||
static int zdsfs_open(const char *path, struct fuse_file_info *fi)
|
||
{
|
||
@@ -620,6 +810,7 @@ static int zdsfs_open(const char *path, struct fuse_file_info *fi)
|
||
rc = -rc;
|
||
goto error2;
|
||
}
|
||
+ zdsfs_setup_conversion(dsh, ds);
|
||
|
||
retry:
|
||
if (zdsfsinfo.restapi && zdsfsinfo.active_server >= 0) {
|
||
@@ -660,7 +851,6 @@ error2:
|
||
error1:
|
||
free(zfi);
|
||
return rc;
|
||
-
|
||
}
|
||
|
||
static int zdsfs_release(const char *UNUSED(path), struct fuse_file_info *fi)
|
||
@@ -1012,7 +1202,10 @@ enum {
|
||
KEY_TRACKS,
|
||
KEY_SEEKBUFFER,
|
||
KEY_CONFIG,
|
||
+ KEY_DSCONFIG,
|
||
KEY_SERVER,
|
||
+ KEY_CODE_FROM,
|
||
+ KEY_CODE_TO,
|
||
};
|
||
|
||
#define ZDSFS_OPT(t, p, v) { t, offsetof(struct zdsfs_info, p), v }
|
||
@@ -1026,11 +1219,15 @@ static const struct fuse_opt zdsfs_opts[] = {
|
||
FUSE_OPT_KEY("tracks=", KEY_TRACKS),
|
||
FUSE_OPT_KEY("seekbuffer=", KEY_SEEKBUFFER),
|
||
FUSE_OPT_KEY("-c %s", KEY_CONFIG),
|
||
+ FUSE_OPT_KEY("-x %s", KEY_DSCONFIG),
|
||
FUSE_OPT_KEY("restserver=", KEY_SERVER),
|
||
+ FUSE_OPT_KEY("codepage_from=", KEY_CODE_FROM),
|
||
+ FUSE_OPT_KEY("codepage_to=", KEY_CODE_TO),
|
||
ZDSFS_OPT("rdw", keepRDW, 1),
|
||
ZDSFS_OPT("ignore_incomplete", allow_inclomplete_multi_volume, 1),
|
||
ZDSFS_OPT("check_host_count", host_count, 1),
|
||
ZDSFS_OPT("restapi", restapi, 1),
|
||
+ ZDSFS_OPT("codepage_convert", codepage_convert, 1),
|
||
FUSE_OPT_END
|
||
};
|
||
|
||
@@ -1052,6 +1249,8 @@ static void usage(const char *progname)
|
||
" nodes\n"
|
||
" -c config_file Text file that contains configuration options\n"
|
||
" for zdsfs\n"
|
||
+" -x ds_config_file Text file that contains conversion options\n"
|
||
+" for specific datasets\n"
|
||
" -o rdw Keep record descriptor words in byte stream\n"
|
||
" -o ignore_incomplete Continue processing even if parts of a multi"
|
||
" volume\n"
|
||
@@ -1065,6 +1264,10 @@ static void usage(const char *progname)
|
||
" access to datasets\n"
|
||
" -o restserver=URL The URL of the z/OSMF REST server to be used for\n"
|
||
" coordinated access to datasets\n"
|
||
+" -o codepage_convert Enable codepage conversion using default codepages\n"
|
||
+" from 'CP1047' to 'UTF-8'\n"
|
||
+" -o codepage_from=from Set codepage for source. See 'iconv -l' for a list\n"
|
||
+" -o codepage_to=to Set codepage for target. See 'iconv -l' for a list\n"
|
||
, progname);
|
||
}
|
||
|
||
@@ -1167,7 +1370,7 @@ static void zdsfs_process_device_file(const char *devfile)
|
||
void remove_whitespace(const char *s, char *t)
|
||
{
|
||
while (*s != '\0') {
|
||
- if (!isspace(*s)) {
|
||
+ if (!isblank(*s)) {
|
||
*t = *s;
|
||
t++;
|
||
}
|
||
@@ -1176,7 +1379,6 @@ void remove_whitespace(const char *s, char *t)
|
||
*t = '\0';
|
||
}
|
||
|
||
-
|
||
static void zdsfs_process_config_file(const char *config)
|
||
{
|
||
char line[MAX_LINE_LENGTH];
|
||
@@ -1198,7 +1400,7 @@ static void zdsfs_process_config_file(const char *config)
|
||
continue;
|
||
|
||
/* remove all whitespaces */
|
||
- tmp = util_malloc(strlen(line));
|
||
+ tmp = util_malloc(strlen(line) + 1);
|
||
remove_whitespace(line, tmp);
|
||
|
||
key = strtok(tmp, delimiter);
|
||
@@ -1225,6 +1427,163 @@ static void zdsfs_process_config_file(const char *config)
|
||
fclose(fd);
|
||
}
|
||
|
||
+static struct dsconvert *zdsfs_allocate_dsc(char *name, const char *config)
|
||
+{
|
||
+ struct dsconvert *dsc;
|
||
+
|
||
+ /* check for duplicate entries */
|
||
+ if (dsclist_find_by_name(zdsfsinfo.dsclist, name)) {
|
||
+ fprintf(stderr,
|
||
+ "Error in config file %s. Duplicate entry found: %s\n",
|
||
+ config, name);
|
||
+ return NULL;
|
||
+ }
|
||
+ dsc = util_zalloc(sizeof(*dsc));
|
||
+ dsc->name = util_strdup(name);
|
||
+
|
||
+ return dsc;
|
||
+}
|
||
+
|
||
+static int zdsfs_check_codepage_setting(char *from, char *to)
|
||
+{
|
||
+ iconv_t iconv;
|
||
+
|
||
+ /* no conversion is OK */
|
||
+ if (!from && !to)
|
||
+ return 0;
|
||
+
|
||
+ /* partial setup is not OK */
|
||
+ if ((from && !to) || (to && !from))
|
||
+ return 1;
|
||
+
|
||
+ /* return if the codepages are valid */
|
||
+ return setup_iconv(&iconv, from, to);
|
||
+}
|
||
+
|
||
+/*
|
||
+ * process a dataset configuration file that specifies conversion on a per dataset basis
|
||
+ *
|
||
+ * expect a section title each 3 lines
|
||
+ * the section should contain a rdw= and conv= line
|
||
+ * valid values for rdw= are 0/1
|
||
+ * valid values for conv= are 0/1 or a comma separated list
|
||
+ * of codepage_from and codepage_to arguments
|
||
+ */
|
||
+static int zdsfs_process_dataset_conf(const char *config)
|
||
+{
|
||
+ char line[MAX_LINE_LENGTH];
|
||
+ char delimiter[] = " =#\n";
|
||
+ char *tmp, *key, *value;
|
||
+ int linecount, in_section;
|
||
+ unsigned long enabled;
|
||
+ struct dsconvert *dsc;
|
||
+ int rc = 1;
|
||
+ FILE *fd;
|
||
+
|
||
+ fd = fopen(config, "r");
|
||
+ if (!fd) {
|
||
+ fprintf(stderr, "could not open file %s: %s\n",
|
||
+ config, strerror(errno));
|
||
+ return 0;
|
||
+ }
|
||
+ in_section = 0;
|
||
+ linecount = 0;
|
||
+ dsc = NULL;
|
||
+ tmp = NULL;
|
||
+ while (fgets(line, sizeof(line), fd)) {
|
||
+ linecount++;
|
||
+ /* remove all whitespaces */
|
||
+ tmp = util_malloc(strlen(line) + 1);
|
||
+ remove_whitespace(line, tmp);
|
||
+ /* skip empty lines */
|
||
+ if (*tmp == '\n' || *tmp == '#') {
|
||
+ free(tmp);
|
||
+ tmp = NULL;
|
||
+ continue;
|
||
+ }
|
||
+ if (!in_section) {
|
||
+ /* the section title should not contain a '=' */
|
||
+ if (strchr(line, '=') != NULL) {
|
||
+ fprintf(stderr,
|
||
+ "Error in config file %s line %d. Expected section title instead of %s\n",
|
||
+ config, linecount, line);
|
||
+ goto out;
|
||
+ }
|
||
+ }
|
||
+ key = strtok(tmp, delimiter);
|
||
+ if (!in_section) {
|
||
+ dsc = zdsfs_allocate_dsc(key, config);
|
||
+ if (!dsc)
|
||
+ goto out;
|
||
+ in_section = SECTION_ENTRIES;
|
||
+ } else if (strcmp(key, "rdw") == 0) {
|
||
+ value = strtok(NULL, delimiter);
|
||
+ enabled = strtoul(value, NULL, 0);
|
||
+ if (enabled == 1)
|
||
+ dsc->keeprdw = true;
|
||
+ else
|
||
+ dsc->keeprdw = false;
|
||
+ } else if (strcmp(key, "conv") == 0) {
|
||
+ value = strtok(NULL, delimiter);
|
||
+ if (strchr(value, ',') != NULL) {
|
||
+ /* use provided codepages */
|
||
+ value = strtok(value, ",");
|
||
+ dsc->codepage_from = util_strdup(value);
|
||
+ value = strtok(NULL, delimiter);
|
||
+ dsc->codepage_to = util_strdup(value);
|
||
+ } else if (strcmp(value, "1") == 0) {
|
||
+ /* use default codepages */
|
||
+ dsc->codepage_from = CODEPAGE_EDF;
|
||
+ dsc->codepage_to = CODEPAGE_LINUX;
|
||
+ } else if (strcmp(value, "0") == 0) {
|
||
+ /* disable conversion */
|
||
+ dsc->codepage_from = NULL;
|
||
+ dsc->codepage_to = NULL;
|
||
+ } else {
|
||
+ fprintf(stderr,
|
||
+ "Error in config file %s line %d. Invalid 'conv' statement: %s.\n",
|
||
+ config, linecount, value);
|
||
+ goto out;
|
||
+ }
|
||
+ } else {
|
||
+ fprintf(stderr,
|
||
+ "Error in config file %s line %d. Missing 'rdw' or 'conv' statement.\n",
|
||
+ config, linecount);
|
||
+ goto out;
|
||
+ }
|
||
+ in_section--;
|
||
+ /* if the section was parsed completely, add the dsc to the list */
|
||
+ if (!in_section) {
|
||
+ if (zdsfs_check_codepage_setting(dsc->codepage_from,
|
||
+ dsc->codepage_to)) {
|
||
+ fprintf(stderr,
|
||
+ "Error in config file %s. Invalid codepage setting: %s %s.\n",
|
||
+ config, dsc->codepage_from,
|
||
+ dsc->codepage_to);
|
||
+ goto out;
|
||
+ }
|
||
+ dsclist_add(zdsfsinfo.dsclist, dsc);
|
||
+ dsc = NULL;
|
||
+ }
|
||
+ free(tmp);
|
||
+ tmp = NULL;
|
||
+ }
|
||
+ /* find incomplete last section */
|
||
+ if (in_section)
|
||
+ fprintf(stderr,
|
||
+ "Error in config file %s. Missing 'rdw' or 'conv' statement.\n",
|
||
+ config);
|
||
+ else
|
||
+ rc = 0;
|
||
+
|
||
+out:
|
||
+ fclose(fd);
|
||
+ free(tmp);
|
||
+ dsc_free(dsc);
|
||
+
|
||
+ return rc;
|
||
+}
|
||
+
|
||
static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key,
|
||
struct fuse_args *outargs)
|
||
{
|
||
@@ -1313,6 +1672,10 @@ static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key,
|
||
/* note that arg starts with "-c" */
|
||
zdsfsinfo.configfile = util_strdup(arg + 2);
|
||
return 0;
|
||
+ case KEY_DSCONFIG:
|
||
+ /* note that arg starts with "-x" */
|
||
+ zdsfsinfo.dsfile = util_strdup(arg + 2);
|
||
+ return 0;
|
||
case KEY_SERVER:
|
||
if (zdsfsinfo.nr_server >= MAX_SERVER)
|
||
return 0;
|
||
@@ -1321,6 +1684,16 @@ static int zdsfs_process_args(void *UNUSED(data), const char *arg, int key,
|
||
util_strdup(value);
|
||
zdsfsinfo.nr_server++;
|
||
return 0;
|
||
+ case KEY_CODE_FROM:
|
||
+ value = arg + strlen("codepage_from=");
|
||
+ zdsfsinfo.codepage_from =
|
||
+ util_strdup(value);
|
||
+ return 0;
|
||
+ case KEY_CODE_TO:
|
||
+ value = arg + strlen("codepage_to=");
|
||
+ zdsfsinfo.codepage_to =
|
||
+ util_strdup(value);
|
||
+ return 0;
|
||
default:
|
||
fprintf(stderr, "Unknown argument key %x\n", key);
|
||
exit(1);
|
||
@@ -1340,11 +1713,14 @@ int main(int argc, char *argv[])
|
||
zdsfsinfo.tracks_per_frame = 128;
|
||
zdsfsinfo.seek_buffer_size = 1048576;
|
||
zdsfsinfo.configfile = "/etc/zdsfs.conf";
|
||
+ zdsfsinfo.dsfile = "/etc/zdsfs-dataset.conf";
|
||
zdsfsinfo.keepalive = DEFAULT_KEEPALIVE_SEC;
|
||
zdsfsinfo.active_server = -1;
|
||
|
||
rc = lzds_zdsroot_alloc(&zdsfsinfo.zdsroot);
|
||
open_dsh = dshlist_alloc();
|
||
+ zdsfsinfo.dsclist = dsclist_alloc();
|
||
+
|
||
if (rc) {
|
||
fprintf(stderr, "Could not allocate internal structures\n");
|
||
exit(1);
|
||
@@ -1354,8 +1730,19 @@ int main(int argc, char *argv[])
|
||
fprintf(stderr, "Failed to parse option\n");
|
||
exit(1);
|
||
}
|
||
+ if (zdsfs_check_codepage_setting(zdsfsinfo.codepage_from,
|
||
+ zdsfsinfo.codepage_to)) {
|
||
+ fprintf(stderr, "Ivalid codepage setting from '%s' to '%s'\n",
|
||
+ zdsfsinfo.codepage_from,
|
||
+ zdsfsinfo.codepage_to);
|
||
+ rc = -EINVAL;
|
||
+ goto cleanup;
|
||
+ }
|
||
zdsfs_process_config_file(zdsfsinfo.configfile);
|
||
-
|
||
+ if (zdsfs_process_dataset_conf(zdsfsinfo.dsfile)) {
|
||
+ rc = -EACCES;
|
||
+ goto cleanup;
|
||
+ }
|
||
if (!zdsfsinfo.devcount) {
|
||
fprintf(stderr, "Please specify a block device\n");
|
||
fprintf(stderr, "Try '%s --help' for more information\n",
|
||
@@ -1390,12 +1777,12 @@ int main(int argc, char *argv[])
|
||
goto cleanup;
|
||
}
|
||
}
|
||
-
|
||
rc = fuse_main(args.argc, args.argv, &rdf_oper, NULL);
|
||
|
||
cleanup:
|
||
curl_global_cleanup();
|
||
dshlist_free(open_dsh);
|
||
+ dsclist_free(zdsfsinfo.dsclist);
|
||
lzds_zdsroot_free(zdsfsinfo.zdsroot);
|
||
|
||
fuse_opt_free_args(&args);
|
||
--
|
||
2.31.1
|
||
|
||
|
||
From 994e280ae9292821f77d49138f0f17827965e5a6 Mon Sep 17 00:00:00 2001
|
||
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
||
Date: Fri, 2 Jul 2021 11:49:28 +0200
|
||
Subject: [PATCH 5/7] dasd: change default scheduler to reduce CPU consumption
|
||
(#1972038)
|
||
|
||
Description: dasd: change default scheduler to reduce CPU consumption
|
||
Symptom: CPU consumption up to 20% higher for mq-deadline
|
||
compared to none scheduler for DASD devices with no
|
||
difference in throughput.
|
||
Problem: Performance analysis showed that with recent DASD
|
||
device drivers using multi-queue block queuing the
|
||
throughput of mq-deadline and none scheduler is nearly
|
||
identical but the CPU consumption of mq-deadline
|
||
scheduler due to its optimizations is up to 20% higher
|
||
compared to none scheduler.
|
||
Solution: Set none scheduler as default in the DASD udev rule.
|
||
Reproduction: Use DASD devices with mq-deadline scheduler.
|
||
Upstream-ID: a65bc51cf4e5c1fe628bb182cc1a02ee83eb102d
|
||
Problem-ID: 192049
|
||
---
|
||
etc/udev/rules.d/59-dasd.rules | 4 ++--
|
||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||
|
||
diff --git a/etc/udev/rules.d/59-dasd.rules b/etc/udev/rules.d/59-dasd.rules
|
||
index 98fbd18..06c1bf2 100644
|
||
--- a/etc/udev/rules.d/59-dasd.rules
|
||
+++ b/etc/udev/rules.d/59-dasd.rules
|
||
@@ -26,10 +26,10 @@ KERNEL=="dasd*[0-9]", ENV{ID_XUID}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env
|
||
|
||
LABEL="dasd_symlinks_end"
|
||
|
||
-# on device add set request queue scheduler to deadline
|
||
+# on device add set request queue scheduler to none
|
||
SUBSYSTEM!="block", GOTO="sched_end"
|
||
|
||
ACTION!="change", GOTO="sched_end"
|
||
-KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="deadline"
|
||
+KERNEL=="dasd*[!0-9]", TEST=="queue/scheduler", ATTR{queue/scheduler}="none"
|
||
|
||
LABEL="sched_end"
|
||
--
|
||
2.31.1
|
||
|
||
|
||
From 6141192f0487ce450106dfc32a8d6f3d7a89908b Mon Sep 17 00:00:00 2001
|
||
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
||
Date: Fri, 2 Jul 2021 11:50:35 +0200
|
||
Subject: [PATCH 6/7] dbginfo.sh: Collect /proc/kallsyms, issue additional
|
||
commands (#1972041)
|
||
|
||
Description: dbginfo.sh: Collect /proc/kallsyms, issue additional commands
|
||
Symptom: - Required data gets not collected during run of dbginfo.sh
|
||
- vmcp q cache command always fails
|
||
Problem: - some commands yet missing in dbginfo.sh script
|
||
- command vmcp q cache is missing a mandatory parameter
|
||
Solution: Stop to issue a vmcp q cache command (fails always) and add
|
||
- collect /proc/kallsyms as a tgz file (because of huge size)
|
||
- lscpu -ye
|
||
- vmcp q memassist
|
||
- vmcp q pcifunction
|
||
- vmcp q vmrelocate
|
||
Reproduction: Run the dbginfo.sh and check for results in DBGINFOxxx.tgz file
|
||
- /proc/kallsyms
|
||
- zvm_runtime.out
|
||
- runtime.out
|
||
Upstream-ID: eb1fd47a85c93ec247d89a4d02b0a5a2c5a8d444
|
||
Problem-ID: 192022
|
||
---
|
||
scripts/dbginfo.sh | 32 +++++++++++++++++++++++++++-----
|
||
scripts/dbginfo.sh.1 | 8 ++++----
|
||
2 files changed, 31 insertions(+), 9 deletions(-)
|
||
|
||
diff --git a/scripts/dbginfo.sh b/scripts/dbginfo.sh
|
||
index 705b5f0..405dcb3 100755
|
||
--- a/scripts/dbginfo.sh
|
||
+++ b/scripts/dbginfo.sh
|
||
@@ -2,7 +2,7 @@
|
||
#
|
||
# dbginfo.sh - Tool to collect runtime, configuration, and trace information
|
||
#
|
||
-# Copyright IBM Corp. 2002, 2020
|
||
+# Copyright IBM Corp. 2002, 2021
|
||
#
|
||
# s390-tools is free software; you can redistribute it and/or modify
|
||
# it under the terms of the MIT license. See LICENSE for details.
|
||
@@ -21,7 +21,7 @@ readonly SCRIPTNAME="${0##*/}"
|
||
print_version() {
|
||
cat <<EOF
|
||
${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
|
||
-Copyright IBM Corp. 2002, 2020
|
||
+Copyright IBM Corp. 2002, 2021
|
||
EOF
|
||
}
|
||
|
||
@@ -37,8 +37,8 @@ print_usage()
|
||
|
||
Usage: ${SCRIPTNAME} [OPTIONS]
|
||
|
||
-This script collects runtime, configuration and trace information about
|
||
-your Linux on System z installation for debugging purposes.
|
||
+This script collects runtime, configuration and trace information on
|
||
+a Linux on IBM Z installation for debugging purposes.
|
||
|
||
It also traces information about z/VM if the Linux runs under z/VM.
|
||
|
||
@@ -253,6 +253,7 @@ PROCFILES="\
|
||
/proc/driver/z90crypt\
|
||
/proc/interrupts\
|
||
/proc/iomem\
|
||
+ /proc/kallsyms\
|
||
/proc/mdstat\
|
||
/proc/meminfo\
|
||
/proc/misc\
|
||
@@ -435,6 +436,7 @@ CMDS="uname -a\
|
||
:lschp\
|
||
:lscss\
|
||
:lscpu -ae\
|
||
+ :lscpu -ye\
|
||
:lsmem\
|
||
:lsdasd\
|
||
:lsdasd -u\
|
||
@@ -532,7 +534,7 @@ VM_CMDS="q userid\
|
||
:q lan\
|
||
:q lan all details\
|
||
:q lan all access\
|
||
- :q cache\
|
||
+ :q memassist\
|
||
:q nic\
|
||
:q pav\
|
||
:q proc\
|
||
@@ -549,6 +551,8 @@ VM_CMDS="q userid\
|
||
:q dumpdev\
|
||
:q reorder VMUSERID\
|
||
:q quickdsp VMUSERID\
|
||
+ :q pcifunction\
|
||
+ :q vmrelocate\
|
||
:ind load\
|
||
:ind sp\
|
||
:ind user\
|
||
@@ -1008,6 +1012,22 @@ post_processing() {
|
||
touch --time=mtime -t "${file_mtime}" "${file_name}"
|
||
done
|
||
|
||
+ find "${WORKPATH}proc/" -name "kallsyms" 2>/dev/null | while IFS= read -r file_name; do
|
||
+ tmp_file=${file_name}-`uname -r`.tgz
|
||
+ ch_dir="${WORKPATH}proc/"
|
||
+ orig_file="kallsyms"
|
||
+
|
||
+
|
||
+ echo " ${file_name}"
|
||
+ if ! test -e "${file_name}"; then
|
||
+ echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}"
|
||
+ echo
|
||
+ fi
|
||
+
|
||
+ tar -cvzf "${tmp_file}" -C "${ch_dir}" "${orig_file}"
|
||
+ rm -f "${file_name}"
|
||
+
|
||
+ done
|
||
|
||
pr_log_stdout " "
|
||
}
|
||
@@ -1124,6 +1144,8 @@ create_package()
|
||
pr_stdout "Finalizing: Creating archive with collected data"
|
||
cd "${WORKDIR_BASE}"
|
||
|
||
+ touch "${WORKARCHIVE}"
|
||
+ chmod 0600 "${WORKARCHIVE}"
|
||
tar -czf "${WORKARCHIVE}" "${WORKDIR_CURRENT}"
|
||
rc_tar=$?
|
||
if [ $rc_tar -eq 0 ]; then
|
||
diff --git a/scripts/dbginfo.sh.1 b/scripts/dbginfo.sh.1
|
||
index ef9fe89..a59e4c0 100644
|
||
--- a/scripts/dbginfo.sh.1
|
||
+++ b/scripts/dbginfo.sh.1
|
||
@@ -2,7 +2,7 @@
|
||
|
||
.SH NAME
|
||
dbginfo.sh \- collect runtime, configuration and trace information
|
||
-for debugging Linux on System z
|
||
+for debugging Linux on IBM Z
|
||
|
||
.SH SYNOPSIS
|
||
.br
|
||
@@ -12,7 +12,7 @@ for debugging Linux on System z
|
||
|
||
.SH DESCRIPTION
|
||
This script collects runtime, configuration and trace information that can
|
||
-be used to debug a Linux on System z instance.
|
||
+be used to debug a Linux on IBM Z instance.
|
||
For Linux on z/VM, the script also traces information about the z/VM system.
|
||
The debug information is written to a file
|
||
/tmp/DBGINFO\-<date>\-<time>\-<hostname>\-<processorid>.tgz
|
||
@@ -52,7 +52,7 @@ Sample invocation:
|
||
.br
|
||
dbginfo.sh: Debug information script version %S390_TOOLS_VERSION%
|
||
.br
|
||
-Copyright IBM Corp. 2002, 2019
|
||
+Copyright IBM Corp. 2002, 2021
|
||
.PP
|
||
Hardware platform = s390x
|
||
.br
|
||
@@ -104,4 +104,4 @@ Run the script with root authority.
|
||
For Linux on z/VM, only z/VM guest virtual machines with class B privileges
|
||
yield the complete debug information.
|
||
.SH AUTHOR
|
||
-Linux on System z development <linux390@de.ibm.com>
|
||
+Linux on IBM Z development <linux390@de.ibm.com>
|
||
--
|
||
2.31.1
|
||
|
||
|
||
From 29f70132b4ce9ee59f1b34f77172cf966d574808 Mon Sep 17 00:00:00 2001
|
||
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
||
Date: Tue, 20 Jul 2021 10:58:42 +0200
|
||
Subject: [PATCH 7/7] s390-tools: Add support for complete counter set
|
||
extraction
|
||
|
||
Summary: s390-tools: Add support for complete counter set extraction
|
||
Description: cpumf/lshwc: Program to extract complete counter sets
|
||
Program reads complete counter sets from any CPU by opening
|
||
device /dev/hwctr.
|
||
The counter sets and CPUs can be specified on the command line.
|
||
Upstream-ID: 27a562da0ad55032649e0258205d208c07db16ca
|
||
---
|
||
cpumf/Makefile | 5 +-
|
||
cpumf/lshwc.c | 775 ++++++++++++++++++++++++++++++++++++++++++++++
|
||
cpumf/lshwc.h | 92 ++++++
|
||
cpumf/man/lshwc.1 | 134 ++++++++
|
||
4 files changed, 1004 insertions(+), 2 deletions(-)
|
||
create mode 100644 cpumf/lshwc.c
|
||
create mode 100644 cpumf/lshwc.h
|
||
create mode 100644 cpumf/man/lshwc.1
|
||
|
||
diff --git a/cpumf/Makefile b/cpumf/Makefile
|
||
index ecb3515..78612e7 100644
|
||
--- a/cpumf/Makefile
|
||
+++ b/cpumf/Makefile
|
||
@@ -1,7 +1,7 @@
|
||
include ../common.mak
|
||
|
||
-BIN_FILES = lscpumf chcpumf
|
||
-MAN_FILES = lscpumf.1 chcpumf.8
|
||
+BIN_FILES = lscpumf chcpumf lshwc
|
||
+MAN_FILES = lscpumf.1 chcpumf.8 lshwc.1
|
||
|
||
all: $(BIN_FILES)
|
||
|
||
@@ -9,6 +9,7 @@ libs = $(rootdir)/libutil/libutil.a
|
||
|
||
lscpumf: lscpumf.o $(libs)
|
||
chcpumf: chcpumf.o $(libs)
|
||
+lshwc: lshwc.o $(libs)
|
||
|
||
install: all install-man
|
||
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8
|
||
diff --git a/cpumf/lshwc.c b/cpumf/lshwc.c
|
||
new file mode 100644
|
||
index 0000000..39c9fbe
|
||
--- /dev/null
|
||
+++ b/cpumf/lshwc.c
|
||
@@ -0,0 +1,775 @@
|
||
+/* Copyright IBM Corp. 2021
|
||
+ *
|
||
+ * s390-tools is free software; you can redistribute it and/or modify
|
||
+ * it under the terms of the MIT license. See LICENSE for details.
|
||
+ */
|
||
+
|
||
+/* CPU Measurements counter facility counter sets can be extracted by a
|
||
+ * device driver accessible by opening device /dev/hwctr.
|
||
+ * This program extracts complete counter set using this device.
|
||
+ * Counter sets are per CPU, the interface allows to specify counter sets
|
||
+ * for individual CPUs. The supported flags are executed from left to
|
||
+ * right, the first error encountered stops the execution of the program.
|
||
+ */
|
||
+
|
||
+#include <ctype.h>
|
||
+#include <dirent.h>
|
||
+#include <err.h>
|
||
+#include <errno.h>
|
||
+#include <fcntl.h>
|
||
+#include <linux/limits.h>
|
||
+#include <stdarg.h>
|
||
+#include <stdbool.h>
|
||
+#include <stdint.h>
|
||
+#include <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+#include <sys/ioctl.h>
|
||
+#include <sys/stat.h>
|
||
+#include <sys/time.h>
|
||
+#include <sys/user.h>
|
||
+#include <time.h>
|
||
+#include <unistd.h>
|
||
+
|
||
+#include "lib/util_opt.h"
|
||
+#include "lib/util_prg.h"
|
||
+#include "lib/util_base.h"
|
||
+#include "lib/util_path.h"
|
||
+#include "lib/util_scandir.h"
|
||
+#include "lib/util_libc.h"
|
||
+
|
||
+#include "lshwc.h"
|
||
+
|
||
+#define SERVICELEVEL "/proc/service_levels"
|
||
+#define CPUS_ONLINE "/sys/devices/system/cpu/online"
|
||
+#define CPUS_POSSIBLE "/sys/devices/system/cpu/possible"
|
||
+#define CPUS_KERNELMAX "/sys/devices/system/cpu/kernel_max"
|
||
+#define MAXCTRS 512
|
||
+
|
||
+static const unsigned int ioctlsleep = 60;
|
||
+static unsigned int read_interval = ioctlsleep, cfvn, csvn, authorization;
|
||
+static unsigned long loop_count = 1;
|
||
+static unsigned char *ioctlbuffer;
|
||
+static bool allcpu;
|
||
+
|
||
+static unsigned int max_possible_cpus; /* No of possible CPUs */
|
||
+struct ctrname { /* List of defined counters */
|
||
+ char *name; /* Counter name */
|
||
+ bool hitcnt; /* Counter number read from ioctl() */
|
||
+ unsigned long total; /* Total counter value */
|
||
+ unsigned long *ccv; /* Per CPU counter value */
|
||
+} ctrname[MAXCTRS];
|
||
+
|
||
+/* Open file and extract counter number */
|
||
+static int read_counter(const char *p)
|
||
+{
|
||
+ FILE *fp = fopen(p, "r");
|
||
+ int rc = 0, ctr;
|
||
+
|
||
+ if (fp) {
|
||
+ rc = fscanf(fp, "event=%x", &ctr);
|
||
+ fclose(fp);
|
||
+ }
|
||
+ return rc == 1 ? ctr : -EINVAL;
|
||
+}
|
||
+
|
||
+static int add_countername(char *name, int nr)
|
||
+{
|
||
+ ctrname[nr].name = strdup(name);
|
||
+ return ctrname[nr].name ? 0 : -ENOMEM;
|
||
+}
|
||
+
|
||
+static bool read_counternames(void)
|
||
+{
|
||
+ struct dirent **namelist = NULL;
|
||
+ int i, ctr = 0, count = 0;
|
||
+ char *path, *ctrpath;
|
||
+
|
||
+ path = util_path_sysfs("/bus/event_source/devices/cpum_cf/events/");
|
||
+ count = util_scandir(&namelist, alphasort, path, "[^.]");
|
||
+ if (count <= 0) {
|
||
+ warnx("Cannot open %s", path);
|
||
+ free(path);
|
||
+ return false;
|
||
+ }
|
||
+ for (i = 0; i < count && ctr >= 0; i++) {
|
||
+ util_asprintf(&ctrpath, "%s/%s", path, namelist[i]->d_name);
|
||
+ ctr = read_counter(ctrpath);
|
||
+ free(ctrpath);
|
||
+ if (ctr >= 0)
|
||
+ ctr = add_countername(namelist[i]->d_name, ctr);
|
||
+ }
|
||
+ if (ctr < 0)
|
||
+ warnx("Cannot parse %s", path);
|
||
+ util_scandir_free(namelist, count);
|
||
+ free(path);
|
||
+ return ctr < 0 ? false : true;
|
||
+}
|
||
+
|
||
+static void free_counternames(void)
|
||
+{
|
||
+ for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||
+ free(ctrname[i].name);
|
||
+ free(ctrname[i].ccv);
|
||
+ }
|
||
+}
|
||
+
|
||
+static struct check_result {
|
||
+ bool cpu_pos; /* CPU Number possible */
|
||
+ bool cpu_req; /* CPU Number requested */
|
||
+ bool cpu_hit; /* CPU Number received */
|
||
+ unsigned char sets_req; /* Counters sets requested */
|
||
+ unsigned char sets_hit; /* Counters sets received */
|
||
+} *check;
|
||
+
|
||
+static bool check_set(unsigned long a, unsigned long b, unsigned long sets)
|
||
+{
|
||
+ if (a > b)
|
||
+ return false;
|
||
+ for (; a <= b; ++a) {
|
||
+ if (a >= max_possible_cpus || !check[a].cpu_pos)
|
||
+ return false;
|
||
+ check[a].cpu_req = true;
|
||
+ check[a].sets_req = sets;
|
||
+ }
|
||
+ return true;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Functions to parse command line parameters
|
||
+ * Convert a number from ascii to int.
|
||
+ */
|
||
+static unsigned long getnumber(char *word, char stopchar)
|
||
+{
|
||
+ unsigned long no;
|
||
+ char *endp;
|
||
+
|
||
+ no = strtoul(word, &endp, 0);
|
||
+ if (*endp != stopchar)
|
||
+ errx(EXIT_FAILURE, "Invalid parameter %s", word);
|
||
+ return no;
|
||
+}
|
||
+
|
||
+/* Remove all whitespace from string. */
|
||
+static void kill_whitespace(char *s)
|
||
+{
|
||
+ char *cp = s;
|
||
+
|
||
+ for (; *s != '\0'; ++s) {
|
||
+ if (isspace(*s))
|
||
+ continue;
|
||
+ if (isprint(*s))
|
||
+ *cp++ = *s;
|
||
+ }
|
||
+ *cp = '\0';
|
||
+}
|
||
+
|
||
+/* Read file to get all online CPUs */
|
||
+static bool get_cpus(char *file, char *buf, size_t bufsz)
|
||
+{
|
||
+ char fmt[16];
|
||
+ FILE *slp;
|
||
+ int rc;
|
||
+
|
||
+ slp = fopen(file, "r");
|
||
+ if (!slp) {
|
||
+ warnx("Cannot open %s", file);
|
||
+ return false;
|
||
+ }
|
||
+ snprintf(fmt, sizeof(fmt), "%%%zus", bufsz - 1);
|
||
+ rc = fscanf(slp, fmt, buf);
|
||
+ fclose(slp);
|
||
+ if (rc != 1)
|
||
+ warnx("Cannot parse %s", file);
|
||
+ return rc == 1 ? true : false;
|
||
+}
|
||
+
|
||
+/* Parse counter set specification */
|
||
+static unsigned long parse_ctrset(char *cp)
|
||
+{
|
||
+ unsigned long x = 0;
|
||
+
|
||
+ for (; *cp; ++cp) {
|
||
+ switch (tolower(*cp)) {
|
||
+ case 'b':
|
||
+ x |= S390_HWCTR_BASIC;
|
||
+ break;
|
||
+ case 'c':
|
||
+ x |= S390_HWCTR_CRYPTO;
|
||
+ break;
|
||
+ case 'e':
|
||
+ x |= S390_HWCTR_EXT;
|
||
+ break;
|
||
+ case 'm':
|
||
+ x |= S390_HWCTR_MT_DIAG;
|
||
+ break;
|
||
+ case 'p':
|
||
+ case 'u':
|
||
+ x |= S390_HWCTR_USER;
|
||
+ break;
|
||
+ case 'a':
|
||
+ x |= S390_HWCTR_ALL;
|
||
+ break;
|
||
+ default:
|
||
+ errx(EXIT_FAILURE,
|
||
+ "Invalid counter set specification '%c'", *cp);
|
||
+ }
|
||
+ }
|
||
+ return x;
|
||
+}
|
||
+
|
||
+static char *show_ctrset(unsigned long set)
|
||
+{
|
||
+ static char text[16];
|
||
+ int i = 0;
|
||
+
|
||
+ if (set & S390_HWCTR_BASIC)
|
||
+ text[i++] = 'B';
|
||
+ if (set & S390_HWCTR_CRYPTO)
|
||
+ text[i++] = 'C';
|
||
+ if (set & S390_HWCTR_EXT)
|
||
+ text[i++] = 'E';
|
||
+ if (set & S390_HWCTR_MT_DIAG)
|
||
+ text[i++] = 'M';
|
||
+ if (set & S390_HWCTR_USER)
|
||
+ text[i++] = 'U';
|
||
+ text[i] = '\0';
|
||
+ return text;
|
||
+}
|
||
+
|
||
+/* Parse CPU list and counter sets */
|
||
+static void parse_cpulist(char *parm, struct s390_hwctr_start *start)
|
||
+{
|
||
+ __u64 *words = start->cpumask;
|
||
+ unsigned long i, no_a, no_b;
|
||
+ char *cp, *tokens[16]; /* Used to parse command line params */
|
||
+ char cpubuf[256];
|
||
+
|
||
+ start->data_bytes = 0;
|
||
+ if (parm)
|
||
+ kill_whitespace(parm);
|
||
+ if (!parm || *parm == ':') {
|
||
+ /* No CPU list or just counter sets */
|
||
+ if (!get_cpus(CPUS_ONLINE, cpubuf, sizeof(cpubuf)))
|
||
+ exit(EXIT_FAILURE);
|
||
+ if (parm)
|
||
+ strcat(cpubuf, parm);
|
||
+ parm = cpubuf;
|
||
+ }
|
||
+
|
||
+ cp = strchr(parm, ':');
|
||
+ if (cp) { /* Handle counter set */
|
||
+ *cp = '\0';
|
||
+ start->counter_sets = parse_ctrset(++cp);
|
||
+ } else {
|
||
+ start->counter_sets = S390_HWCTR_ALL;
|
||
+ }
|
||
+ /* Check with authorized counter sets */
|
||
+ if ((start->counter_sets & authorization) != start->counter_sets) {
|
||
+ unsigned int noton = ~(start->counter_sets & authorization);
|
||
+
|
||
+ start->counter_sets &= authorization;
|
||
+ if (!start->counter_sets)
|
||
+ errx(EXIT_FAILURE, "No counter sets are authorized");
|
||
+ warnx("One or more counter sets are not authorized: %s",
|
||
+ show_ctrset(noton));
|
||
+ }
|
||
+
|
||
+ for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ",")) != 0;
|
||
+ ++i, parm = 0) {
|
||
+ cp = strchr(tokens[i], '-'); /* Range character? */
|
||
+ if (cp) {
|
||
+ no_a = getnumber(tokens[i], *cp);
|
||
+ no_b = getnumber(++cp, '\0');
|
||
+ } else {
|
||
+ no_b = getnumber(tokens[i], '\0');
|
||
+ no_a = no_b;
|
||
+ }
|
||
+ if (!check_set(no_a, no_b, start->counter_sets))
|
||
+ errx(EXIT_FAILURE, "Invalid CPU list %s", tokens[i]);
|
||
+ }
|
||
+
|
||
+ /* Convert the CPU list to a bitmask for kernel cpumask_t */
|
||
+ for (i = 0, no_b = 0; i < max_possible_cpus; ++i) {
|
||
+ if (check[i].cpu_req) {
|
||
+ no_a = i % __BITS_PER_LONG;
|
||
+ no_b = i / __BITS_PER_LONG;
|
||
+ words[no_b] |= 1ULL << no_a;
|
||
+ }
|
||
+ }
|
||
+ /* no_b is highest used index, swap array */
|
||
+ start->cpumask_len = (no_b + 1) * 8;
|
||
+ for (no_a = 0; no_a < no_b; ++no_a, --no_b) {
|
||
+ __u64 tmp = words[no_a];
|
||
+
|
||
+ words[no_a] = words[no_b];
|
||
+ words[no_b] = tmp;
|
||
+ }
|
||
+ start->version = S390_HWCTR_START_VERSION;
|
||
+}
|
||
+
|
||
+static bool check_setpossible(void)
|
||
+{
|
||
+ char *cp, *parm, *tokens[16]; /* Used to parse command line params */
|
||
+ unsigned long i, no_a, no_b;
|
||
+ char cpubuf[1024];
|
||
+
|
||
+ if (!get_cpus(CPUS_KERNELMAX, cpubuf, sizeof(cpubuf)))
|
||
+ return false;
|
||
+ max_possible_cpus = getnumber(cpubuf, '\0') + 1;
|
||
+ check = calloc(max_possible_cpus, sizeof(*check));
|
||
+ if (!check)
|
||
+ err(EXIT_FAILURE, "Maximum CPUs %u", max_possible_cpus);
|
||
+ if (!get_cpus(CPUS_POSSIBLE, cpubuf, sizeof(cpubuf))) {
|
||
+ free(check);
|
||
+ return false;
|
||
+ }
|
||
+ parm = cpubuf;
|
||
+ for (i = 0; i < ARRAY_SIZE(tokens) && (tokens[i] = strtok(parm, ","));
|
||
+ ++i, parm = 0) {
|
||
+ cp = strchr(tokens[i], '-');
|
||
+ if (cp) { /* Range */
|
||
+ no_a = getnumber(tokens[i], *cp);
|
||
+ no_b = getnumber(++cp, '\0');
|
||
+ } else {
|
||
+ no_b = getnumber(tokens[i], '\0');
|
||
+ no_a = no_b;
|
||
+ }
|
||
+ for (; no_a <= no_b; ++no_a)
|
||
+ check[no_a].cpu_pos = true;
|
||
+ }
|
||
+ return true;
|
||
+}
|
||
+
|
||
+static void show_header(void)
|
||
+{
|
||
+ static bool header;
|
||
+ bool comma = false;
|
||
+
|
||
+ if (header)
|
||
+ return; /* Printed already */
|
||
+ printf("Date,Time,CPU,"); /* Print counter name and number */
|
||
+ for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||
+ if (!ctrname[i].hitcnt)
|
||
+ continue;
|
||
+ if (comma)
|
||
+ putchar(',');
|
||
+ printf("%s(%ld)", ctrname[i].name ?: "Counter", i);
|
||
+ comma = true;
|
||
+ }
|
||
+ putchar('\n');
|
||
+ header = true;
|
||
+}
|
||
+
|
||
+static void line(char *header)
|
||
+{
|
||
+ bool comma;
|
||
+
|
||
+ show_header();
|
||
+ if (allcpu) {
|
||
+ for (unsigned int h = 0; h < max_possible_cpus; ++h) {
|
||
+ char txt[16];
|
||
+
|
||
+ if (!check[h].cpu_hit)
|
||
+ continue;
|
||
+ comma = false;
|
||
+ snprintf(txt, sizeof(txt), "CPU%d,", h);
|
||
+ printf("%s%s", header, txt);
|
||
+ for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||
+ if (!ctrname[i].hitcnt)
|
||
+ continue;
|
||
+ if (comma)
|
||
+ putchar(',');
|
||
+ printf("%ld", ctrname[i].ccv[h]);
|
||
+ comma = true;
|
||
+ }
|
||
+ putchar('\n');
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* Print total count of all CPUs */
|
||
+ printf("%sTotal,", header);
|
||
+ comma = false;
|
||
+ for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||
+ if (!ctrname[i].hitcnt)
|
||
+ continue;
|
||
+ if (comma)
|
||
+ putchar(',');
|
||
+ printf("%ld", ctrname[i].total);
|
||
+ comma = true;
|
||
+ }
|
||
+ putchar('\n');
|
||
+}
|
||
+
|
||
+static void show(void)
|
||
+{
|
||
+ time_t now = time(NULL);
|
||
+ struct tm *now_tm;
|
||
+ char now_text[32];
|
||
+
|
||
+ now_tm = localtime(&now);
|
||
+ strftime(now_text, sizeof(now_text), "%F,%T,", now_tm);
|
||
+ line(now_text);
|
||
+}
|
||
+
|
||
+/* Return Counter set size numbers (in counters) */
|
||
+static unsigned int ctrset_size(int set)
|
||
+{
|
||
+ switch (set) {
|
||
+ case S390_HWCTR_BASIC:
|
||
+ return 6;
|
||
+ case S390_HWCTR_USER:
|
||
+ return (cfvn == 1) ? 6 : 2;
|
||
+ case S390_HWCTR_CRYPTO:
|
||
+ return (csvn <= 5) ? 16 : 20;
|
||
+ case S390_HWCTR_EXT:
|
||
+ switch (csvn) {
|
||
+ case 1: return 32;
|
||
+ case 2: return 48;
|
||
+ case 3:
|
||
+ case 4:
|
||
+ case 5: return 128;
|
||
+ }
|
||
+ return 160;
|
||
+ case S390_HWCTR_MT_DIAG:
|
||
+ switch (csvn) {
|
||
+ case 1:
|
||
+ case 2:
|
||
+ case 3: return 0;
|
||
+ }
|
||
+ return 48;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/* Return counter set offset numbers */
|
||
+static int ctrset_offset(int set)
|
||
+{
|
||
+ switch (set) {
|
||
+ case S390_HWCTR_BASIC:
|
||
+ return 0;
|
||
+ case S390_HWCTR_USER:
|
||
+ return 32;
|
||
+ case S390_HWCTR_CRYPTO:
|
||
+ return 64;
|
||
+ case S390_HWCTR_EXT:
|
||
+ return 128;
|
||
+ case S390_HWCTR_MT_DIAG:
|
||
+ return 448;
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static bool set_and_size_ok(struct s390_hwctr_setdata *p)
|
||
+{
|
||
+ switch (p->set) {
|
||
+ case S390_HWCTR_BASIC:
|
||
+ case S390_HWCTR_USER:
|
||
+ case S390_HWCTR_CRYPTO:
|
||
+ case S390_HWCTR_EXT:
|
||
+ case S390_HWCTR_MT_DIAG:
|
||
+ return p->no_cnts == ctrset_size(p->set);
|
||
+ }
|
||
+ return false;
|
||
+}
|
||
+
|
||
+static bool add_countervalue(size_t idx, unsigned int cpu, unsigned long value)
|
||
+{
|
||
+ if (idx >= ARRAY_SIZE(ctrname)) {
|
||
+ warnx("Invalid counter number %zu", idx);
|
||
+ return false;
|
||
+ }
|
||
+ if (cpu >= max_possible_cpus) {
|
||
+ warnx("Invalid CPU number %d", cpu);
|
||
+ return false;
|
||
+ }
|
||
+ if (!ctrname[idx].ccv) /* Unknown counter */
|
||
+ ctrname[idx].ccv = calloc(max_possible_cpus,
|
||
+ sizeof(unsigned long));
|
||
+ if (ctrname[idx].ccv)
|
||
+ ctrname[idx].ccv[cpu] += value;
|
||
+ ctrname[idx].total += value;
|
||
+ ctrname[idx].hitcnt = true;
|
||
+ return true;
|
||
+}
|
||
+
|
||
+static int test_read(struct s390_hwctr_read *read)
|
||
+{
|
||
+ void *base = &read->data;
|
||
+ size_t offset = 0;
|
||
+
|
||
+ /* Clear previous hit counters */
|
||
+ for (unsigned int i = 0; i < max_possible_cpus; ++i) {
|
||
+ check[i].sets_hit = 0;
|
||
+ check[i].cpu_hit = false;
|
||
+ }
|
||
+
|
||
+ /* Iterate over all CPUs */
|
||
+ for (unsigned int i = 0; i < read->no_cpus; ++i) {
|
||
+ struct s390_hwctr_cpudata *cp = base + offset;
|
||
+
|
||
+ check[cp->cpu_nr].cpu_hit = true;
|
||
+ check[cp->cpu_nr].sets_hit = 0;
|
||
+
|
||
+ offset += sizeof(cp->cpu_nr) + sizeof(cp->no_sets);
|
||
+ /* Iterate over all counter sets */
|
||
+ for (unsigned int j = 0; j < cp->no_sets; ++j) {
|
||
+ struct s390_hwctr_setdata *sp = base + offset;
|
||
+
|
||
+ check[cp->cpu_nr].sets_hit |= sp->set;
|
||
+ offset += sizeof(sp->set) + sizeof(sp->no_cnts);
|
||
+ if (!set_and_size_ok(sp)) {
|
||
+ warnx("CPU %d inconsistent set %d size %d",
|
||
+ cp->cpu_nr, sp->set, sp->no_cnts);
|
||
+ return -1;
|
||
+ }
|
||
+ /* Iterate over all counters in each set */
|
||
+ for (unsigned int k = 0; k < sp->no_cnts; ++k) {
|
||
+ __u64 value;
|
||
+ void *addr = base + offset;
|
||
+ size_t idx = ctrset_offset(sp->set) + k;
|
||
+
|
||
+ memcpy(&value, addr, sizeof(value));
|
||
+ offset += sizeof(value);
|
||
+ if (!add_countervalue(idx, cp->cpu_nr, value))
|
||
+ return -1;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ show();
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int do_open(void)
|
||
+{
|
||
+ int fd = open(S390_HWCTR_DEVICE, O_RDWR);
|
||
+
|
||
+ if (fd < 0)
|
||
+ warn(S390_HWCTR_DEVICE);
|
||
+ return fd;
|
||
+}
|
||
+
|
||
+static int do_stop(int ioctlfd)
|
||
+{
|
||
+ int rc = ioctl(ioctlfd, S390_HWCTR_STOP, 0);
|
||
+
|
||
+ if (rc < 0)
|
||
+ warn("ioctl S390_HWCTR_STOP");
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int do_start(int ioctlfd, struct s390_hwctr_start *start)
|
||
+{
|
||
+ int rc = ioctl(ioctlfd, S390_HWCTR_START, start);
|
||
+
|
||
+ if (rc < 0)
|
||
+ warn("ioctl S390_HWCTR_START");
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static int do_read(int ioctlfd)
|
||
+{
|
||
+ size_t ioctlbuffer_len = PAGE_SIZE * max_possible_cpus +
|
||
+ sizeof(struct s390_hwctr_read);
|
||
+ struct s390_hwctr_read *read;
|
||
+ int rc;
|
||
+
|
||
+ if (!ioctlbuffer) {
|
||
+ ioctlbuffer = malloc(ioctlbuffer_len);
|
||
+ if (!ioctlbuffer) {
|
||
+ warn("ioctl S390_HWCTR_START");
|
||
+ return -ENOMEM;
|
||
+ }
|
||
+ }
|
||
+ read = (struct s390_hwctr_read *)ioctlbuffer;
|
||
+ rc = ioctl(ioctlfd, S390_HWCTR_READ, read);
|
||
+ if (!rc)
|
||
+ rc = test_read(read);
|
||
+ else
|
||
+ warn("ioctl S390_HWCTR_READ");
|
||
+ return rc;
|
||
+}
|
||
+
|
||
+static void do_sleep(void)
|
||
+{
|
||
+ struct timespec req = {
|
||
+ .tv_sec = read_interval,
|
||
+ .tv_nsec = 0
|
||
+ };
|
||
+
|
||
+ nanosleep(&req, NULL);
|
||
+}
|
||
+
|
||
+/* Execute commands and report first error */
|
||
+static int do_it(char *s)
|
||
+{
|
||
+ struct s390_hwctr_start start;
|
||
+ int ioctlfd;
|
||
+ int rc;
|
||
+
|
||
+ memset(&start, 0, sizeof(start));
|
||
+ rc = max_possible_cpus / sizeof(__u64);
|
||
+ start.cpumask = alloca(max_possible_cpus / sizeof(__u64));
|
||
+ memset(start.cpumask, 0, rc);
|
||
+ parse_cpulist(s, &start);
|
||
+ errno = 0;
|
||
+ ioctlfd = do_open();
|
||
+ if (ioctlfd < 0)
|
||
+ return EXIT_FAILURE;
|
||
+
|
||
+ rc = do_start(ioctlfd, &start);
|
||
+ if (rc < 0) {
|
||
+ close(ioctlfd);
|
||
+ return EXIT_FAILURE;
|
||
+ }
|
||
+
|
||
+ for (unsigned long i = 0; !rc && i < loop_count; ++i) {
|
||
+ rc = do_read(ioctlfd);
|
||
+ if (rc) {
|
||
+ close(ioctlfd);
|
||
+ return EXIT_FAILURE;
|
||
+ }
|
||
+ if (read_interval && i + 1 < loop_count)
|
||
+ do_sleep();
|
||
+ }
|
||
+ rc = do_stop(ioctlfd);
|
||
+ close(ioctlfd);
|
||
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||
+}
|
||
+
|
||
+/* Read counter first and second version number */
|
||
+static bool get_cvn(void)
|
||
+{
|
||
+ char *linep = NULL;
|
||
+ bool good = false;
|
||
+ size_t line_sz;
|
||
+ ssize_t nbytes;
|
||
+ FILE *slp;
|
||
+
|
||
+ slp = fopen(SERVICELEVEL, "r");
|
||
+ if (!slp) {
|
||
+ warn(SERVICELEVEL);
|
||
+ return false;
|
||
+ }
|
||
+ while ((nbytes = getline(&linep, &line_sz, slp)) != EOF) {
|
||
+ if (!strncmp(linep, "CPU-MF: Counter facility:", 25)) {
|
||
+ int rc;
|
||
+
|
||
+ rc = sscanf(linep, "CPU-MF: Counter facility: version=%d.%d authorization=%x",
|
||
+ &cfvn, &csvn, &authorization);
|
||
+ good = rc == 3;
|
||
+ if (!good)
|
||
+ warnx("Cannot parse line %s", linep);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ fclose(slp);
|
||
+ free(linep);
|
||
+ return good;
|
||
+}
|
||
+
|
||
+static struct util_opt opt_vec[] = {
|
||
+ UTIL_OPT_SECTION("OPTIONS"),
|
||
+ {
|
||
+ .option = { "all", no_argument, NULL, 'a' },
|
||
+ .desc = "Displays all CPUs in output"
|
||
+ },
|
||
+ {
|
||
+ .option = { "loop", required_argument, NULL, 'l' },
|
||
+ .argument = "NUMBER",
|
||
+ .desc = "Specifies loop count for next read"
|
||
+ },
|
||
+ {
|
||
+ .option = { "interval", required_argument, NULL, 'i' },
|
||
+ .argument = "NUMBER",
|
||
+ .desc = "Specifies interval between read operations (seconds)"
|
||
+ },
|
||
+ UTIL_OPT_HELP,
|
||
+ UTIL_OPT_VERSION,
|
||
+ UTIL_OPT_END
|
||
+};
|
||
+
|
||
+static const struct util_prg prg = {
|
||
+ .desc = "Read CPU Measurement facility counter sets",
|
||
+ .copyright_vec = {
|
||
+ {
|
||
+ .owner = "IBM Corp.",
|
||
+ .pub_first = 2021,
|
||
+ .pub_last = 2021,
|
||
+ },
|
||
+ UTIL_PRG_COPYRIGHT_END
|
||
+ }
|
||
+};
|
||
+
|
||
+/* Check for hardware support and exit if not available */
|
||
+static void have_support(void)
|
||
+{
|
||
+ struct stat statbuf;
|
||
+
|
||
+ if (stat(S390_HWCTR_DEVICE, &statbuf) == -1)
|
||
+ errx(EXIT_FAILURE,
|
||
+ "No support for CPU Measurement Counter set facility");
|
||
+}
|
||
+
|
||
+int main(int argc, char **argv)
|
||
+{
|
||
+ char *slash;
|
||
+ int ch;
|
||
+
|
||
+ util_prg_init(&prg);
|
||
+ util_opt_init(opt_vec, NULL);
|
||
+
|
||
+ while ((ch = util_opt_getopt_long(argc, argv)) != -1) {
|
||
+ switch (ch) {
|
||
+ default:
|
||
+ util_opt_print_parse_error(ch, argv);
|
||
+ return EXIT_FAILURE;
|
||
+ case 'h':
|
||
+ util_prg_print_help();
|
||
+ util_opt_print_help();
|
||
+ return EXIT_SUCCESS;
|
||
+ case 'v':
|
||
+ util_prg_print_version();
|
||
+ return EXIT_SUCCESS;
|
||
+ case 'l':
|
||
+ errno = 0;
|
||
+ loop_count = strtoul(optarg, &slash, 0);
|
||
+ if (errno || *slash)
|
||
+ errx(EXIT_FAILURE, "Invalid argument for -%c",
|
||
+ ch);
|
||
+ break;
|
||
+ case 'i':
|
||
+ errno = 0;
|
||
+ read_interval = (unsigned int)strtoul(optarg, &slash, 0);
|
||
+ if (errno || *slash)
|
||
+ errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||
+ break;
|
||
+ case 'a':
|
||
+ allcpu = true;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ have_support();
|
||
+ if (!get_cvn())
|
||
+ return EXIT_FAILURE;
|
||
+ if (!check_setpossible())
|
||
+ return EXIT_FAILURE;
|
||
+ if (!read_counternames()) {
|
||
+ free(check);
|
||
+ return EXIT_FAILURE;
|
||
+ }
|
||
+
|
||
+ if (optind >= argc) {
|
||
+ ch = do_it(NULL);
|
||
+ } else {
|
||
+ while (optind < argc) {
|
||
+ ch = do_it(argv[optind++]);
|
||
+ if (ch)
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ free_counternames();
|
||
+ free(check);
|
||
+ return ch;
|
||
+}
|
||
diff --git a/cpumf/lshwc.h b/cpumf/lshwc.h
|
||
new file mode 100644
|
||
index 0000000..d8044dc
|
||
--- /dev/null
|
||
+++ b/cpumf/lshwc.h
|
||
@@ -0,0 +1,92 @@
|
||
+/* Copyright IBM Corp. 2021
|
||
+ *
|
||
+ * s390-tools is free software; you can redistribute it and/or modify
|
||
+ * it under the terms of the MIT license. See LICENSE for details.
|
||
+ */
|
||
+
|
||
+/*
|
||
+ * CPU Measurement counter facility application for device driver.
|
||
+ *
|
||
+ * Ioctl system call definitions.
|
||
+ */
|
||
+
|
||
+#ifndef LSHWC_H
|
||
+#define LSHWC_H
|
||
+
|
||
+#include <sys/ioctl.h>
|
||
+
|
||
+enum {
|
||
+ S390_HWCTR_BASIC = 0x2, /* BASIC counter set */
|
||
+ S390_HWCTR_USER = 0x4, /* Problem-State Counter Set */
|
||
+ S390_HWCTR_CRYPTO = 0x8, /* Crypto-Activity Counter Set */
|
||
+ S390_HWCTR_EXT = 0x1, /* Extended Counter Set */
|
||
+ S390_HWCTR_MT_DIAG = 0x20, /* MT-diagnostic Counter Set */
|
||
+ S390_HWCTR_ALL = S390_HWCTR_BASIC | S390_HWCTR_USER |
|
||
+ S390_HWCTR_CRYPTO | S390_HWCTR_EXT |
|
||
+ S390_HWCTR_MT_DIAG
|
||
+};
|
||
+
|
||
+/* The ioctl(..., S390_HWCTR_READ, ...) is the only subcommand which returns
|
||
+ * data. It requires member data_bytes to be positive and indicates the
|
||
+ * maximum amount of data available to store counter set data. The other
|
||
+ * ioctl() subcommands do not use this member and it should be set to zero.
|
||
+ *
|
||
+ * The cpuset data is flattened using the following scheme, stored in member
|
||
+ * data:
|
||
+ *
|
||
+ * 0x0 0x8 0xc 0x10 0x14 0x18 0x20 0x28 0xU-1
|
||
+ * +---------+-----+---------+-----+---------+-----+-----+------+------+
|
||
+ * | no_cpus | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||
+ * +---------+-----+---------+-----+---------+-----+-----+------+------+
|
||
+ *
|
||
+ * 0xU 0xU+4 0xU+8 0xU+10 0xV-1
|
||
+ * +-----+---------+-----+-----+------+------+
|
||
+ * | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||
+ * +-----+---------+-----+-----+------+------+
|
||
+ *
|
||
+ * 0xV 0xV+4 0xV+8 0xV+c
|
||
+ * +-----+---------+-----+---------+-----+-----+------+------+
|
||
+ * | cpu | no_sets | set | no_cnts | cv1 | cv2 | .... | cv_n |
|
||
+ * +-----+---------+-----+---------+-----+-----+------+------+
|
||
+ *
|
||
+ * U and V denote arbitrary hexadezimal addresses.
|
||
+ * In fact the first int represents the number of CPUs data was extracted
|
||
+ * from. This is followed by CPU number and number of counter sets extracted.
|
||
+ * Both are two integer values. This is followed by the set number and number
|
||
+ * of counters extracted. Both are two integer values. This is followed by
|
||
+ * the counter values, each element is eight bytes in size.
|
||
+ */
|
||
+
|
||
+struct s390_hwctr_start { /* Set CPUs to operate on */
|
||
+ __u64 version; /* Version of interface */
|
||
+ __u64 data_bytes; /* # of bytes required */
|
||
+ __u64 cpumask_len; /* Length of CPU mask in bytes */
|
||
+ __u64 *cpumask; /* Pointer to CPU mask */
|
||
+ __u64 counter_sets; /* Bit mask of counter set to get */
|
||
+};
|
||
+
|
||
+struct s390_hwctr_setdata { /* Counter set data */
|
||
+ __u32 set; /* Counter set number */
|
||
+ __u32 no_cnts; /* # of counters stored in cv[] */
|
||
+ __u64 cv[0]; /* Counter values (variable length) */
|
||
+};
|
||
+
|
||
+struct s390_hwctr_cpudata { /* Counter set data per CPU */
|
||
+ __u32 cpu_nr; /* Counter set number */
|
||
+ __u32 no_sets; /* # of counters sets in data[] */
|
||
+ struct s390_hwctr_setdata data[0];
|
||
+};
|
||
+
|
||
+struct s390_hwctr_read { /* Structure to get all ctr sets */
|
||
+ __u64 no_cpus; /* Total # of CPUs data taken from */
|
||
+ struct s390_hwctr_cpudata data[0];
|
||
+};
|
||
+
|
||
+#define S390_HWCTR_MAGIC 'C' /* Random magic # for ioctls */
|
||
+#define S390_HWCTR_START _IOWR(S390_HWCTR_MAGIC, 1, struct s390_hwctr_start)
|
||
+#define S390_HWCTR_STOP _IO(S390_HWCTR_MAGIC, 2)
|
||
+#define S390_HWCTR_READ _IOWR(S390_HWCTR_MAGIC, 3, struct s390_hwctr_read)
|
||
+
|
||
+#define S390_HWCTR_START_VERSION 1 /* Version # s390_hwctr_start */
|
||
+#define S390_HWCTR_DEVICE "/dev/hwctr" /* Device name */
|
||
+#endif
|
||
diff --git a/cpumf/man/lshwc.1 b/cpumf/man/lshwc.1
|
||
new file mode 100644
|
||
index 0000000..b7b6943
|
||
--- /dev/null
|
||
+++ b/cpumf/man/lshwc.1
|
||
@@ -0,0 +1,134 @@
|
||
+.\" lshwc.1
|
||
+.\"
|
||
+.\"
|
||
+.\" Copyright IBM Corp. 2021
|
||
+.\" s390-tools is free software; you can redistribute it and/or modify
|
||
+.\" it under the terms of the MIT license. See LICENSE for details.
|
||
+.\" ----------------------------------------------------------------------
|
||
+.ds c \fBlshwc\fP
|
||
+.
|
||
+.TH \*c "1" "February 2021" "s390-tools" "CPU-MF management programs"
|
||
+.
|
||
+.SH NAME
|
||
+\*c \- extract CPU Measurement Facilities counter sets
|
||
+.
|
||
+.SH SYNOPSIS
|
||
+\*c
|
||
+.RB [ \-a ]
|
||
+.RB [ \-l
|
||
+.IR count ]
|
||
+.RB [ \-i
|
||
+.IR interval ]
|
||
+\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||
+.br
|
||
+\*c
|
||
+.BR \-h | \-\-help
|
||
+.br
|
||
+\*c
|
||
+.BR \-v | \-\-version
|
||
+.
|
||
+.
|
||
+.SH DESCRIPTION
|
||
+The \*c command extracts complete counter sets from the CPU
|
||
+Measurement Facilities for Linux on Z.
|
||
+Counter sets can be specified and extracted for individual CPUs.
|
||
+The output is a comma-separated values file.
|
||
+Each line starts with a timestamp and the CPU number,
|
||
+followed by the extracted counter values.
|
||
+.
|
||
+.SH OPTIONS
|
||
+.TP
|
||
+.BR \-h ", " \-\-help
|
||
+Displays help information, then exits.
|
||
+.
|
||
+.TP
|
||
+.BR \-v ", " \-\-version
|
||
+Displays version information, then exits.
|
||
+.
|
||
+.TP
|
||
+.BR \-a ", " \-\-allcpu
|
||
+Displays counter values from each CPU.
|
||
+The default is a total summary line of all counters from all CPUs.
|
||
+.
|
||
+.TP
|
||
+.BR \-i ", " \-\-interval \fI\ seconds\fP
|
||
+Specifies a time interval, in seconds,
|
||
+that the command waits between read operations.
|
||
+The default is 60 seconds.
|
||
+.
|
||
+.TP
|
||
+.BR \-l ", " \-\-loop \fI\ count\fP
|
||
+Performs the specified number of read operations.
|
||
+.
|
||
+.TP
|
||
+\fR[\fIcpulist\fR][:\fIsets\fR]\fP
|
||
+A comma-separated list of CPUs.
|
||
+Each CPU can optionally be followed by characters that specify the counter set.
|
||
+See below for details.
|
||
+.
|
||
+.SS "CPU List and counter-set specification"
|
||
+In the comma-separated list of CPUs,
|
||
+each element is a CPU or a range of CPUs.
|
||
+By default, \*c lists all CPUs.
|
||
+.P
|
||
+The CPU list can be followed by an optional list
|
||
+of characters that specify the counter sets to be extracted,
|
||
+preceded by a colon.
|
||
+The characters can be upper or lower case.
|
||
+By default, all counter sets are used.
|
||
+.IP b
|
||
+Include the basic counter set.
|
||
+.IP c
|
||
+Include the crypto counter set.
|
||
+.IP e
|
||
+Include the extended counter set.
|
||
+.IP m
|
||
+Include the MT_Diagnostic counter set.
|
||
+.IP p|u
|
||
+Include the problem counter set.
|
||
+.IP a
|
||
+Include all known counter sets (default).
|
||
+.SH "Concurrency with perf tool"
|
||
+The \*c tool and the linux
|
||
+.B perf
|
||
+tool use the same hardware and cannot be used concurrently.
|
||
+Both tools print an error message and abort when they
|
||
+detect this situation.
|
||
+.SH "EXAMPLES"
|
||
+The first example enables the basic and problem counter sets on CPU 0 and 1.
|
||
+Two read operations are performed and a summary line is printed for each
|
||
+read operation.
|
||
+.sp 1
|
||
+.nf
|
||
+.ft CW
|
||
+# lshwc -l2 0-1:BP
|
||
+Date,Time,CPU,CPU_CYCLES(0),INSTRUCTIONS(1),L1I_DIR_WRITES(2),L1I_PENALTY_CYCLES(3),L1D_DIR_WRITES(4),
|
||
+ L1D_PENALTY_CYCLES(5),PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||
+2021-04-01,11:50:32,Total,125422,39421,304,13953,454,
|
||
+ 97489,0,0
|
||
+2021-04-01,11:51:32,Total,68074231,16386850,194028,21382384,317227,
|
||
+ 104503489,777383,14198
|
||
+.ft
|
||
+.fi
|
||
+.sp 1
|
||
+This example shows the counter values of the problem state counter set
|
||
+per CPU. CPU 0 and CPU 1 is selected.
|
||
+.nf
|
||
+.ft CW
|
||
+.sp 1
|
||
+# lshwc -l3 -a 0-1:P
|
||
+Date,Time,CPU,PROBLEM_STATE_CPU_CYCLES(32),PROBLEM_STATE_INSTRUCTIONS(33)
|
||
+2021-04-01,11:54:47,CPU0,0,0
|
||
+2021-04-01,11:54:47,CPU1,0,0
|
||
+2021-04-01,11:54:47,Total,0,0
|
||
+2021-04-01,11:55:47,CPU0,818775,14198
|
||
+2021-04-01,11:55:47,CPU1,125689,1306
|
||
+2021-04-01,11:55:47,Total,944464,15504
|
||
+2021-04-01,11:56:47,CPU0,3207071426,1489122591
|
||
+2021-04-01,11:56:47,CPU1,3225092021,1489278312
|
||
+2021-04-01,11:56:47,Total,6432163447,2978400903
|
||
+.ft
|
||
+.fi
|
||
+.SH "SEE ALSO"
|
||
+.BR lscpumf (1)
|
||
+.BR chcpumf (8)
|
||
--
|
||
2.31.1
|
||
|