From fe157b161a39f4e850ff94d63ed1dd893399acc8 Mon Sep 17 00:00:00 2001 From: Nikhil V Date: Mon, 11 Sep 2023 11:54:18 +0530 Subject: [PATCH 1/2] drivers: qcom: Vendor hooks to support compressed block count In case of hibernation with compression enabled, 'n' number of pages will be compressed to 'x' number of pages before being written to the disk. Keep a note of these compressed block counts so that bootloader can directly read 'x' pages and pass it on to the decompressor. An array will be maintained which will hold the count of these compressed blocks and later on written to the disk as part of the hibernation image save process. Change-Id: If48cd5c396eda674467b3d40c75ad3c2e91c2e5e Signed-off-by: Nikhil V --- drivers/soc/qcom/qcom_secure_hibernation.c | 82 +++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/qcom_secure_hibernation.c b/drivers/soc/qcom/qcom_secure_hibernation.c index d03569d804e4..42f07d2505d3 100644 --- a/drivers/soc/qcom/qcom_secure_hibernation.c +++ b/drivers/soc/qcom/qcom_secure_hibernation.c @@ -85,6 +85,9 @@ static unsigned short root_swap_dev; static struct work_struct save_params_work; static struct completion write_done; static unsigned char iv[IV_SIZE]; +static uint8_t *compressed_blk_array; +static int blk_array_pos; +static unsigned long nr_pages; static void init_sg(struct scatterlist *sg, void *data, unsigned int size) { @@ -301,6 +304,26 @@ static int write_page(void *buf, sector_t offset, struct hib_bio_batch *hb) return hib_submit_io(REQ_OP_WRITE, REQ_SYNC, offset, src, hb); } +/* + * Number of pages compressed at one time. This is inline with UNC_PAGES + * in kernel/power/swap.c. + */ +#define UNCMP_PAGES 32 + +static uint32_t get_size_of_compression_block_array(unsigned long pages) +{ + /* + * Get the max index based on total no. of pages. Current compression + * algorithm compresses each UNC_PAGES pages to x pages. Use this logic to + * get the max index. + */ + uint32_t max_index = DIV_ROUND_UP(pages, UNCMP_PAGES); + + uint32_t size = ALIGN((max_index * sizeof(*compressed_blk_array)), PAGE_SIZE); + + return size; +} + static void save_auth_and_params_to_disk(struct work_struct *work) { int cur_slot; @@ -309,7 +332,7 @@ static void save_auth_and_params_to_disk(struct work_struct *work) int authslot_count = 0; int authpage_count = read_authpage_count(); struct hib_bio_batch hb; - int err2; + int err2, i = 0; hib_init_batch(&hb); @@ -327,6 +350,19 @@ static void save_auth_and_params_to_disk(struct work_struct *work) } params->authslot_count = authslot_count; write_page(params, params_slot, &hb); + + /* + * Write the array holding the compressed block count to disk + */ + if (compressed_blk_array) { + uint32_t size = get_size_of_compression_block_array(nr_pages); + + for (i = 0; i < size / PAGE_SIZE; i++) { + cur_slot = alloc_swapdev_block(root_swap_dev); + write_page(compressed_blk_array + (i * PAGE_SIZE), cur_slot, &hb); + } + } + err2 = hib_wait_io(&hb); hib_finish_batch(&hb); complete_all(&write_done); @@ -457,6 +493,15 @@ void deinit_aes_encrypt(void) kfree(params); } +static void cleanup_cmp_blk_array(void) +{ + blk_array_pos = 0; + if (compressed_blk_array) { + kvfree((void *)compressed_blk_array); + compressed_blk_array = NULL; + } +} + static int hibernate_pm_notifier(struct notifier_block *nb, unsigned long event, void *unused) { @@ -492,6 +537,7 @@ static int hibernate_pm_notifier(struct notifier_block *nb, case (PM_POST_HIBERNATION): deinit_aes_encrypt(); + cleanup_cmp_blk_array(); break; default: @@ -543,6 +589,38 @@ err_auth: kfree(params); } +/* + * Bit(part of swsusp_header_flags) to indicate if the image is uncompressed + * or not. This is inline with SF_NOCOMPRESS_MODE defined in + * kernel/power/power.h. + */ +#define SF_NOCOMPRESS_MODE 2 + +static void hibernated_do_mem_alloc(void *data, unsigned long pages, + unsigned int swsusp_header_flags, int *ret) +{ + uint32_t size; + + /* total no. of pages in the snapshot image */ + nr_pages = pages; + + if (!(swsusp_header_flags & SF_NOCOMPRESS_MODE)) { + size = get_size_of_compression_block_array(pages); + + compressed_blk_array = kvzalloc(size, GFP_KERNEL); + if (!compressed_blk_array) + *ret = -ENOMEM; + } +} + +static void hibernate_save_cmp_len(void *data, size_t cmp_len) +{ + uint8_t pages; + + pages = DIV_ROUND_UP(cmp_len, PAGE_SIZE); + compressed_blk_array[blk_array_pos++] = pages; +} + static int __init qcom_secure_hibernattion_init(void) { int ret; @@ -551,6 +629,8 @@ static int __init qcom_secure_hibernattion_init(void) register_trace_android_vh_init_aes_encrypt(init_aes_encrypt, NULL); register_trace_android_vh_skip_swap_map_write(skip_swap_map_write, NULL); register_trace_android_vh_post_image_save(save_params_to_disk, NULL); + register_trace_android_vh_hibernate_save_cmp_len(hibernate_save_cmp_len, NULL); + register_trace_android_vh_hibernated_do_mem_alloc(hibernated_do_mem_alloc, NULL); ret = register_pm_notifier(&pm_nb); if (ret) { From 87822fc6153ba896e7e37046a9f1237091dea5df Mon Sep 17 00:00:00 2001 From: Nikhil V Date: Thu, 14 Sep 2023 23:54:08 +0530 Subject: [PATCH 2/2] drivers: qcom: Save authentication tag slot number to disk Currently bootloader does the following to calculate the authentication tag slot number. authslot = NrMetaPages + NrCopyPages + NrSwapMapPages + HDR_SWP_INFO_NUM_PAGES However, with compression enabled, we cannot apply the above logic to get the authentication slot number. So this data should be provided to the bootloader for decryption to work. The current implementation doesn't make use of the swap_map_pages for restoring the hibernation image. Use the slot number of the first swap_map_page to store the authentication tag slot number. Change-Id: Iddfb98cc5adc7bd79c0f52f3f5d64ad282efc9b4 Signed-off-by: Nikhil V --- drivers/soc/qcom/qcom_secure_hibernation.c | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/qcom_secure_hibernation.c b/drivers/soc/qcom/qcom_secure_hibernation.c index 42f07d2505d3..691f4b8ae42f 100644 --- a/drivers/soc/qcom/qcom_secure_hibernation.c +++ b/drivers/soc/qcom/qcom_secure_hibernation.c @@ -88,6 +88,7 @@ static unsigned char iv[IV_SIZE]; static uint8_t *compressed_blk_array; static int blk_array_pos; static unsigned long nr_pages; +static void *auth_slot; static void init_sg(struct scatterlist *sg, void *data, unsigned int size) { @@ -341,6 +342,27 @@ static void save_auth_and_params_to_disk(struct work_struct *work) */ params_slot = alloc_swapdev_block(root_swap_dev); + if (auth_slot) { + *(int *)auth_slot = params_slot + 1; + + /* Currently bootloader code does the following to + * calculate the authentication slot index. + * authslot = NrMetaPages + NrCopyPages + NrSwapMapPages + + * HDR_SWP_INFO_NUM_PAGES; + * + * However, with compression enabled, we cannot apply the + * above logic to get the authentication slot. So this + * data should be provided to the BL for decryption to work. + * + * In the current implementation, BL doesn't make use of + * the swap_map_pages for restoring the hibernation image. So these pages + * could be used for other purposes. Use this to store the + * authentication slot number. This data will be stored at index as + * that of the first swap_map_page. + */ + write_page(auth_slot, 1, &hb); + } + authpage = authslot_start; while (authslot_count < authpage_count) { cur_slot = alloc_swapdev_block(root_swap_dev); @@ -500,6 +522,11 @@ static void cleanup_cmp_blk_array(void) kvfree((void *)compressed_blk_array); compressed_blk_array = NULL; } + if (auth_slot) { + free_page((unsigned long)auth_slot); + auth_slot = NULL; + + } } static int hibernate_pm_notifier(struct notifier_block *nb, @@ -608,8 +635,17 @@ static void hibernated_do_mem_alloc(void *data, unsigned long pages, size = get_size_of_compression_block_array(pages); compressed_blk_array = kvzalloc(size, GFP_KERNEL); - if (!compressed_blk_array) + if (!compressed_blk_array) { *ret = -ENOMEM; + return; + } + + /* Allocate memory to hold authentication slot start */ + auth_slot = (void *)get_zeroed_page(GFP_KERNEL); + if (!auth_slot) { + pr_err("Failed to allocate page for storing authentication tag slot number\n"); + *ret = -ENOMEM; + } } }