blob: 80ece6ec935ac3a3b610496b3f33c69e9832fe4f [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define TLOG_TAG "sancov-rt"
#include <assert.h>
#include <interface/coverage/aggregator.h>
#include <lib/coverage/common/ipc.h>
#include <lib/coverage/common/record.h>
#include <lib/coverage/common/cov_shm.h>
#include <lib/tipc/tipc.h>
#include <lk/macros.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <trusty_log.h>
#include <uapi/err.h>
#define PAGE_SIZE getauxval(AT_PAGESZ)
typedef uint8_t counter_t;
struct sancov_ctx {
handle_t coverage_srv;
size_t idx;
struct cov_shm mailbox;
struct cov_shm data;
volatile struct coverage_record_header* headers;
volatile counter_t* counters;
volatile uintptr_t* pcs;
size_t record_len;
size_t num_counters;
};
static bool in_sancov = false;
#define SANCOV_START \
if (in_sancov) { \
return; \
} \
in_sancov = true;
#define SANCOV_FINISH in_sancov = false;
static size_t header_len(void) {
return sizeof(struct coverage_record_header) + /* COV_START */
sizeof(struct coverage_record_header) + /* COV_8BIT_COUNTERS */
sizeof(struct coverage_record_header) + /* COV_INSTR_PCS */
sizeof(struct coverage_record_header); /* COV_TOTAL_LENGTH */
}
static size_t counters_data_len(size_t num_counters) {
return sizeof(counter_t) * num_counters;
}
static size_t pcs_data_len(size_t num_counters) {
return sizeof(uintptr_t) * num_counters;
}
static size_t record_len(size_t num_counters) {
return header_len() + counters_data_len(num_counters) +
pcs_data_len(num_counters);
}
static void initialize_header(volatile struct coverage_record_header* headers,
size_t num_counters) {
uint32_t offset = header_len();
headers[1].type = COV_8BIT_COUNTERS;
headers[1].offset = offset;
offset += sizeof(counter_t) * num_counters;
headers[2].type = COV_INSTR_PCS;
headers[2].offset = offset;
offset += sizeof(uintptr_t) * num_counters;
headers[3].type = COV_TOTAL_LENGTH;
headers[3].offset = offset;
/* Mark the header as finished */
headers[0].offset = 0;
headers[0].type = COV_START;
}
static int init(struct sancov_ctx* ctx, size_t num_counters) {
int rc;
handle_t chan;
handle_t memref;
struct coverage_aggregator_req req;
struct coverage_aggregator_resp resp;
rc = tipc_connect(&chan, COVERAGE_AGGREGATOR_PORT);
if (rc != NO_ERROR) {
TLOGE("failed (%d) to connect to coverage aggregator service\n", rc);
return rc;
}
req.hdr.cmd = COVERAGE_AGGREGATOR_CMD_REGISTER;
req.register_args.record_len = round_up(record_len(num_counters), PAGE_SIZE);
rc = coverage_aggregator_rpc(chan, &req, NULL, &resp, &memref);
if (rc != NO_ERROR) {
TLOGE("failed (%d) coverage aggregator RPC\n", rc);
goto err_rpc;
}
rc = cov_shm_mmap(&ctx->mailbox, memref, resp.register_args.mailbox_len);
if (rc != NO_ERROR) {
TLOGE("failed to mmap() mailbox shared memory\n");
goto err_mmap;
}
ctx->num_counters = num_counters;
ctx->record_len = record_len(num_counters);
ctx->coverage_srv = chan;
ctx->idx = resp.register_args.idx;
close(memref);
return NO_ERROR;
err_mmap:
close(memref);
err_rpc:
close(chan);
return rc;
}
static int get_record(struct sancov_ctx* ctx) {
int rc;
handle_t memref;
struct coverage_aggregator_req req;
struct coverage_aggregator_resp resp;
size_t shm_len;
if (cov_shm_is_mapped(&ctx->data)) {
cov_shm_munmap(&ctx->data);
}
ctx->counters = NULL;
ctx->pcs = NULL;
req.hdr.cmd = COVERAGE_AGGREGATOR_CMD_GET_RECORD;
rc = coverage_aggregator_rpc(ctx->coverage_srv, &req, NULL, &resp, &memref);
if (rc != NO_ERROR) {
TLOGE("failed (%d) coverage aggregator RPC\n", rc);
return rc;
}
shm_len = resp.get_record_args.shm_len;
if (shm_len < ctx->record_len) {
TLOGE("not enough shared memory, received: %zu, need at least: %zu\n",
shm_len, ctx->record_len);
rc = ERR_BAD_LEN;
goto out;
}
rc = cov_shm_mmap(&ctx->data, memref, resp.get_record_args.shm_len);
if (rc != NO_ERROR) {
TLOGE("failed to mmap() coverage record shared memory\n");
goto out;
}
ctx->headers = ctx->data.base;
initialize_header(ctx->headers, ctx->num_counters);
ctx->counters = ctx->data.base + header_len();
ctx->pcs = ctx->data.base + header_len() +
counters_data_len(ctx->num_counters);
rc = NO_ERROR;
out:
close(memref);
return rc;
}
static void update_record(struct sancov_ctx* ctx, size_t idx, uintptr_t pc) {
assert(idx < ctx->num_counters);
/*
* Since counters are fixed-sized, there is always a chance of overflowing.
* Cap maximum counter value instead of overflowing.
*/
if (ctx->counters[idx] < (counter_t)(-1)) {
ctx->counters[idx]++;
}
if (!ctx->pcs[idx]) {
ctx->pcs[idx] = pc - getauxval(AT_BASE);
}
}
static int get_event(struct sancov_ctx* ctx) {
int* app_mailbox = (int*)(ctx->mailbox.base) + ctx->idx;
int event = READ_ONCE(*app_mailbox);
WRITE_ONCE(*app_mailbox, COVERAGE_MAILBOX_EMPTY);
return event;
};
static struct sancov_ctx ctx;
__attribute__((__weak__)) void __sanitizer_cov_trace_pc_guard_init(
uint32_t* start,
uint32_t* stop) {
SANCOV_START;
static size_t num_counters = 0;
int rc;
/* Initialize only once */
if (start == stop || *start) {
goto out;
}
for (uint32_t* x = start; x < stop; x++) {
*x = ++num_counters;
}
TLOGI("sancov initialized with %lu counters\n", num_counters);
rc = init(&ctx, num_counters * sizeof(counter_t));
assert(rc == NO_ERROR);
out:
SANCOV_FINISH;
}
__attribute__((__weak__)) void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
SANCOV_START;
int rc;
int event = get_event(&ctx);
/* Guards start at 1, and indices start at 0 */
assert(*guard > 0);
size_t idx = *guard - 1;
switch (event) {
case COVERAGE_MAILBOX_EMPTY:
break;
case COVERAGE_MAILBOX_RECORD_READY:
rc = get_record(&ctx);
assert(rc == NO_ERROR);
break;
default:
TLOGE("unknown event: %d\n", event);
abort();
}
if (cov_shm_is_mapped(&ctx.data)) {
uintptr_t ret_address = (uintptr_t)__builtin_return_address(0);
/* The sancov tool expects the address of the instruction before the
* call to this function on ARM and AArch64. */
#if defined(__aarch64__)
ret_address -= 4;
#elif defined(__arm__)
ret_address = (ret_address - 3) & (~1);
#else
#error Only ARM and AArch64 are supported by the Trusty sancov runtime
#endif
update_record(&ctx, idx, ret_address);
}
SANCOV_FINISH;
}