-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcros_fp_device.cc
751 lines (643 loc) · 24 KB
/
cros_fp_device.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "biod/cros_fp_device.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <algorithm>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <chromeos/ec/cros_ec_dev.h>
#include "biod/ec_command.h"
#include "biod/ec_command_async.h"
#include "biod/fp_context_command_factory.h"
#include "biod/fp_frame_command.h"
#include "biod/versions_command.h"
namespace {
std::string FourCC(const uint32_t a) {
return base::StringPrintf(
"%c%c%c%c", static_cast<char>(a), static_cast<char>(a >> 8),
static_cast<char>(a >> 16), static_cast<char>(a >> 24));
}
} // namespace
namespace biod {
constexpr char CrosFpDevice::kCrosFpPath[];
CrosFpDevice::~CrosFpDevice() {
// Current session is gone, clean-up temporary state in the FP MCU.
if (cros_fd_.is_valid())
ResetContext();
}
base::Optional<CrosFpDevice::EcProtocolInfo> CrosFpDevice::EcProtoInfo() {
/* read max request / response size from the MCU for protocol v3+ */
EcCommand<EmptyParam, struct ec_response_get_protocol_info> cmd(
EC_CMD_GET_PROTOCOL_INFO);
// We retry this command because it is known to occasionally fail
// with ETIMEDOUT on first attempt.
if (!cmd.RunWithMultipleAttempts(cros_fd_.get(), kMaxIoAttempts)) {
return base::nullopt;
}
uint16_t max_read =
cmd.Resp()->max_response_packet_size - sizeof(struct ec_host_response);
// TODO(vpalatin): workaround for b/78544921, can be removed if MCU is fixed.
uint16_t max_write =
cmd.Resp()->max_request_packet_size - sizeof(struct ec_host_request) - 4;
return EcProtocolInfo{
.max_read = max_read,
.max_write = max_write,
};
}
base::Optional<std::string> CrosFpDevice::ReadVersion() {
// TODO(b/131438292): Remove the hardcoded size for the version buffer.
std::array<uint8_t, 80> version_buf;
for (int retry = 0; retry < kMaxIoAttempts; retry++) {
ssize_t bytes_read =
read(cros_fd_.get(), version_buf.data(), version_buf.size());
if (bytes_read >= 0) {
LOG_IF(INFO, retry > 0)
<< "FPMCU read cros_fp device succeeded on attempt " << retry + 1
<< "/" << kMaxIoAttempts << ".";
// Ignore the last character read, since it should be a NUL.
auto str = std::string(version_buf.cbegin(),
version_buf.cbegin() + bytes_read - 1);
size_t newline_pos = str.find_first_of('\n');
if (newline_pos == std::string::npos) {
return base::nullopt;
}
return str.substr(0, newline_pos);
}
if (errno != ETIMEDOUT) {
PLOG(ERROR) << "FPMCU failed to read cros_fp device on attempt "
<< retry + 1 << "/" << kMaxIoAttempts
<< ", retry is not allowed for error";
return base::nullopt;
}
PLOG(ERROR) << "FPMCU failed to read cros_fp device on attempt "
<< retry + 1 << "/" << kMaxIoAttempts;
}
return base::nullopt;
}
bool CrosFpDevice::EcDevInit() {
// This is a special read (before events are enabled) that can fail due
// to ETIMEDOUT. This is because the first read with events disabled
// triggers a get_version request to the FPMCU, which can timeout.
base::Optional<std::string> version = ReadVersion();
if (!version) {
LOG(ERROR) << "Failed to read cros_fp device version.";
return false;
}
LOG(INFO) << "cros_fp device version: " << *version;
if (version != CROS_EC_DEV_VERSION) {
LOG(ERROR) << "Invalid device version";
return false;
}
base::Optional<EcProtocolInfo> info = EcProtoInfo();
if (!info) {
LOG(ERROR) << "Failed to get cros_fp protocol info.";
return false;
}
ec_protocol_info_ = *info;
unsigned long mask = 1 << EC_MKBP_EVENT_FINGERPRINT; // NOLINT(runtime/int)
if (ioctl(cros_fd_.get(), CROS_EC_DEV_IOCEVENTMASK_V2, mask) < 0) {
LOG(ERROR) << "Fail to request fingerprint events";
return false;
}
return true;
}
void CrosFpDevice::OnEventReadable() {
struct ec_response_get_next_event evt;
ssize_t sz = read(cros_fd_.get(), &evt, sizeof(evt));
// We are interested only in fingerprint events, discard the other ones.
if (evt.event_type != EC_MKBP_EVENT_FINGERPRINT ||
sz < sizeof(evt.event_type) + sizeof(evt.data.fp_events))
return;
// Properly aligned event value.
uint32_t events;
memcpy(&events, &evt.data.fp_events, sizeof(events));
mkbp_event_.Run(events);
}
bool CrosFpDevice::SetFpMode(const FpMode& mode) {
EcCommand<struct ec_params_fp_mode, struct ec_response_fp_mode> cmd(
EC_CMD_FP_MODE, kVersionZero, {.mode = mode.RawVal()});
bool ret = cmd.Run(cros_fd_.get());
if (ret) {
return true;
}
// In some cases the EC Command might go through, but the AP suspends
// before the EC can ACK it. When the AP wakes up, it considers the
// EC command to have timed out. Since this seems to happen during mode
// setting, check the mode in case of a failure.
FpMode cur_mode = GetFpMode();
if (cur_mode == FpMode(FpMode::Mode::kModeInvalid)) {
LOG(ERROR) << "Failed to get FP mode to verify mode was set in the MCU.";
return false;
}
if (cur_mode == mode) {
LOG(WARNING)
<< "EC Command to set mode failed, but mode was set successfully.";
return true;
} else {
LOG(ERROR) << "EC command to set FP mode: " << mode
<< " failed; current FP mode: " << cur_mode;
}
return false;
}
FpMode CrosFpDevice::GetFpMode() {
EcCommand<struct ec_params_fp_mode, struct ec_response_fp_mode> cmd(
EC_CMD_FP_MODE, kVersionZero,
{.mode = static_cast<uint32_t>(FP_MODE_DONT_CHANGE)});
if (!cmd.Run(cros_fd_.get())) {
LOG(ERROR) << "Failed to get FP mode from MCU.";
return FpMode(FpMode::Mode::kModeInvalid);
}
return FpMode(cmd.Resp()->mode);
}
EcCmdVersionSupportStatus CrosFpDevice::EcCmdVersionSupported(uint16_t cmd_code,
uint32_t ver) {
VersionsCommand versions_cmd(cmd_code);
versions_cmd.RunWithMultipleAttempts(cros_fd_.get(), kMaxIoAttempts);
return versions_cmd.IsVersionSupported(ver);
}
bool CrosFpDevice::SupportsPositiveMatchSecret() {
switch (EcCmdVersionSupported(EC_CMD_FP_READ_MATCH_SECRET, 0)) {
case EcCmdVersionSupportStatus::SUPPORTED:
LOG(INFO) << "Positive match secret is supported.";
return true;
case EcCmdVersionSupportStatus::UNSUPPORTED:
LOG(INFO) << "Positive match secret is not supported.";
return false;
case EcCmdVersionSupportStatus::UNKNOWN:
LOG(WARNING) << "Failed to check support for positive match secret. "
"Defaulting to not supporting.";
return false;
}
}
base::Optional<brillo::SecureVector> CrosFpDevice::FpReadMatchSecret(
uint16_t index) {
EcCommand<struct ec_params_fp_read_match_secret,
struct ec_response_fp_read_match_secret>
cmd(EC_CMD_FP_READ_MATCH_SECRET, 0, {.fgr = index});
if (!cmd.Run(cros_fd_.get()) &&
cmd.Result() == kEcCommandUninitializedResult) {
LOG(ERROR) << "Failed to run EC_CMD_FP_READ_MATCH_SECRET command.";
return base::nullopt;
}
if (cmd.Result() != EC_RES_SUCCESS) {
LOG(ERROR) << "Failed to read positive match secret, result: "
<< cmd.Result() << ".";
return base::nullopt;
}
brillo::SecureVector secret(sizeof(cmd.Resp()->positive_match_secret));
std::copy(cmd.Resp()->positive_match_secret,
cmd.Resp()->positive_match_secret +
sizeof(cmd.Resp()->positive_match_secret),
secret.begin());
brillo::SecureClear(cmd.Resp()->positive_match_secret,
sizeof(cmd.Resp()->positive_match_secret));
return secret;
}
bool CrosFpDevice::UpdateFpInfo() {
info_ = ec_command_factory_->FpInfoCommand();
if (!info_->Run(cros_fd_.get())) {
LOG(ERROR) << "Failed to get FP information.";
return false;
}
return true;
}
base::Optional<CrosFpDeviceInterface::FpStats> CrosFpDevice::GetFpStats() {
EcCommand<EmptyParam, struct ec_response_fp_stats> cmd(EC_CMD_FP_STATS);
if (!cmd.Run(cros_fd_.get())) {
return base::nullopt;
}
uint8_t inval = cmd.Resp()->timestamps_invalid;
if (inval & (FPSTATS_CAPTURE_INV | FPSTATS_MATCHING_INV)) {
return base::nullopt;
}
FpStats stats = {
.capture_ms = cmd.Resp()->capture_time_us / 1000,
.matcher_ms = cmd.Resp()->matching_time_us / 1000,
.overall_ms = cmd.Resp()->overall_time_us / 1000,
};
return stats;
}
// static
bool CrosFpDevice::WaitOnEcBoot(const base::ScopedFD& cros_fp_fd,
ec_current_image expected_image) {
int tries = 50;
ec_current_image image = EC_IMAGE_UNKNOWN;
while (tries) {
tries--;
// Check the EC has the right image.
EcCommand<EmptyParam, struct ec_response_get_version> cmd(
EC_CMD_GET_VERSION);
if (!cmd.Run(cros_fp_fd.get())) {
LOG(ERROR) << "Failed to retrieve cros_fp firmware version.";
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
continue;
}
image = static_cast<ec_current_image>(cmd.Resp()->current_image);
if (image == expected_image) {
LOG(INFO) << "EC image is " << (image == EC_IMAGE_RO ? "RO" : "RW")
<< ".";
return true;
}
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
}
LOG(ERROR) << "EC rebooted to incorrect image " << image;
return false;
}
// static
bool CrosFpDevice::GetVersion(const base::ScopedFD& cros_fp_fd,
EcVersion* ver) {
CHECK(ver);
EcCommand<EmptyParam, struct ec_response_get_version> cmd(EC_CMD_GET_VERSION);
if (!cmd.Run(cros_fp_fd.get())) {
LOG(ERROR) << "Failed to fetch cros_fp firmware version.";
return false;
}
// buffers should already be null terminated -- this is a safeguard
cmd.Resp()->version_string_ro[sizeof(cmd.Resp()->version_string_ro) - 1] =
'\0';
cmd.Resp()->version_string_rw[sizeof(cmd.Resp()->version_string_rw) - 1] =
'\0';
ver->ro_version = std::string(cmd.Resp()->version_string_ro);
ver->rw_version = std::string(cmd.Resp()->version_string_rw);
ver->current_image = static_cast<ec_current_image>(cmd.Resp()->current_image);
return true;
}
bool CrosFpDevice::EcReboot(ec_current_image to_image) {
DCHECK(to_image == EC_IMAGE_RO || to_image == EC_IMAGE_RW);
EcCommand<EmptyParam, EmptyParam> cmd_reboot(EC_CMD_REBOOT);
// Don't expect a return code, cros_fp has rebooted.
cmd_reboot.Run(cros_fd_.get());
if (!WaitOnEcBoot(cros_fd_, EC_IMAGE_RO)) {
LOG(ERROR) << "EC did not come back up after reboot.";
return false;
}
if (to_image == EC_IMAGE_RO) {
// Tell the EC to remain in RO.
EcCommand<struct ec_params_rwsig_action, EmptyParam> cmd_rwsig(
EC_CMD_RWSIG_ACTION);
cmd_rwsig.SetReq({.action = RWSIG_ACTION_ABORT});
if (!cmd_rwsig.Run(cros_fd_.get())) {
LOG(ERROR) << "Failed to keep cros_fp in RO.";
return false;
}
}
// EC jumps to RW after 1 second. Wait enough time in case we want to reboot
// to RW. In case we wanted to remain in RO, wait anyway to ensure that the EC
// received the instructions.
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(3));
if (!WaitOnEcBoot(cros_fd_, to_image)) {
LOG(ERROR) << "EC did not load the right image.";
return false;
}
return true;
}
bool CrosFpDevice::AddEntropy(bool reset) {
// Create the secret.
EcCommandAsync<struct ec_params_rollback_add_entropy, EmptyParam>
cmd_add_entropy(EC_CMD_ADD_ENTROPY, ADD_ENTROPY_GET_RESULT,
{.poll_for_result_num_attempts = 20,
.poll_interval = base::TimeDelta::FromMilliseconds(100),
// The EC temporarily stops responding to EC commands
// when this command is run, so we will keep trying until
// we get success (or time out).
.validate_poll_result = false});
if (reset) {
cmd_add_entropy.SetReq({.action = ADD_ENTROPY_RESET_ASYNC});
} else {
cmd_add_entropy.SetReq({.action = ADD_ENTROPY_ASYNC});
}
if (cmd_add_entropy.Run(cros_fd_.get())) {
LOG(INFO) << "Entropy has been successfully added.";
return true;
}
LOG(ERROR) << "Failed to check status of entropy command.";
return false;
}
base::Optional<int32_t> CrosFpDevice::GetRollBackInfoId() {
EcCommand<EmptyParam, struct ec_response_rollback_info> cmd_rb_info(
EC_CMD_ROLLBACK_INFO);
if (!cmd_rb_info.Run(cros_fd_.get())) {
return base::nullopt;
}
return cmd_rb_info.Resp()->id;
}
bool CrosFpDevice::InitEntropy(bool reset) {
base::Optional<int32_t> block_id = GetRollBackInfoId();
if (!block_id) {
LOG(ERROR) << "Failed to read block ID from FPMCU.";
return false;
}
if (!reset && *block_id != 0) {
// Secret has been set.
LOG(INFO) << "Entropy source had been initialized previously.";
return true;
}
LOG(INFO) << "Entropy source has not been initialized yet.";
bool success = UpdateEntropy(reset);
if (!success) {
LOG(INFO) << "Entropy addition failed.";
return false;
}
LOG(INFO) << "Entropy has been successfully added.";
return true;
}
bool CrosFpDevice::Init() {
cros_fd_ = base::ScopedFD(open(kCrosFpPath, O_RDWR));
if (cros_fd_.get() < 0) {
LOG(ERROR) << "Failed to open " << kCrosFpPath;
return false;
}
if (!EcDevInit())
return false;
if (!InitEntropy(false)) {
return false;
}
// Clean MCU memory if anything is remaining from aborted sessions
ResetContext();
// Retrieve the sensor information / parameters.
if (!UpdateFpInfo())
return false;
LOG(INFO) << "CROS FP Sensor Info ";
LOG(INFO) << " Vendor ID : " << FourCC(info_->sensor_id()->vendor_id);
LOG(INFO) << " Product ID : " << info_->sensor_id()->product_id;
LOG(INFO) << " Model ID : 0x" << std::hex << info_->sensor_id()->model_id;
LOG(INFO) << " Version : " << info_->sensor_id()->version;
std::string error_flags;
if ((info_->GetFpSensorErrors() & FpSensorErrors::kNoIrq) !=
FpSensorErrors::kNone)
error_flags += "NO_IRQ ";
if ((info_->GetFpSensorErrors() & FpSensorErrors::kSpiCommunication) !=
FpSensorErrors::kNone)
error_flags += "SPI_COMM ";
if ((info_->GetFpSensorErrors() & FpSensorErrors::kBadHardwareID) !=
FpSensorErrors::kNone)
error_flags += "BAD_HWID ";
if ((info_->GetFpSensorErrors() & FpSensorErrors::kInitializationFailure) !=
FpSensorErrors::kNone)
error_flags += "INIT_FAIL";
LOG(INFO) << " Errors : " << error_flags;
LOG(INFO) << "CROS FP Image Info ";
// Prints the pixel format in FOURCC format.
LOG(INFO) << " Pixel Format : "
<< FourCC(info_->sensor_image()->pixel_format);
LOG(INFO) << " Image Data Size : " << info_->sensor_image()->frame_size;
LOG(INFO) << " Image Dimensions : " << info_->sensor_image()->width << "x"
<< info_->sensor_image()->height << " "
<< info_->sensor_image()->bpp << " bpp";
LOG(INFO) << "CROS FP Finger Template Info ";
LOG(INFO) << " Template data format : " << info_->template_info()->version;
LOG(INFO) << " Template Data Size : " << info_->template_info()->size;
LOG(INFO) << " Max number of fingers : "
<< info_->template_info()->max_templates;
auto fp_resp = GetFlashProtect();
if (!fp_resp) {
LOG(ERROR) << "Unable to read flash protect state";
} else {
LOG(INFO) << "Flash Protect Flags : 0x" << std::hex << fp_resp->flags
<< "\t: " << FpFlashProtectCommand::ParseFlags(fp_resp->flags);
LOG(INFO) << "Valid Flags : 0x" << std::hex << fp_resp->valid_flags
<< "\t: "
<< FpFlashProtectCommand::ParseFlags(fp_resp->valid_flags);
LOG(INFO) << "writable flags : 0x" << std::hex
<< fp_resp->writable_flags << "\t: "
<< FpFlashProtectCommand::ParseFlags(fp_resp->writable_flags);
}
watcher_ = base::FileDescriptorWatcher::WatchReadable(
cros_fd_.get(), base::BindRepeating(&CrosFpDevice::OnEventReadable,
base::Unretained(this)));
if (!watcher_) {
LOG(ERROR) << "Unable to watch MKBP events";
return false;
}
if (!input_device_.Init()) {
LOG(ERROR) << "Failed to create Uinput device";
return false;
}
return true;
}
base::Optional<std::bitset<32>> CrosFpDevice::GetDirtyMap() {
// Retrieve the up-to-date dirty bitmap from the MCU.
if (!UpdateFpInfo()) {
return base::nullopt;
}
return info_->template_info()->dirty;
}
bool CrosFpDevice::GetIndexOfLastTemplate(int* index) {
if (!UpdateFpInfo())
return false;
*index = info_->template_info()->num_valid - 1;
if (*index < 0 || *index >= MaxTemplateCount()) {
LOG(ERROR) << "Invalid index of last template: " << *index << ".";
return false;
}
return true;
}
base::Optional<brillo::SecureVector> CrosFpDevice::GetPositiveMatchSecret(
int index) {
if (index == kLastTemplate) {
if (!GetIndexOfLastTemplate(&index)) {
return base::nullopt;
}
}
return FpReadMatchSecret(static_cast<uint16_t>(index));
}
std::unique_ptr<VendorTemplate> CrosFpDevice::GetTemplate(int index) {
if (index == kLastTemplate) {
if (!GetIndexOfLastTemplate(&index)) {
return nullptr;
}
// Is the last one really a new created one ?
const auto& dirty = info_->template_info()->dirty;
if (index >= dirty.size() || !dirty.test(index)) {
return nullptr;
}
}
// In the EC_CMD_FP_FRAME host command, the templates are indexed starting
// from 1 (aka FP_FRAME_INDEX_TEMPLATE), as 0 (aka FP_FRAME_INDEX_RAW_IMAGE)
// is used for the finger image.
auto fp_frame_cmd = ec_command_factory_->FpFrameCommand(
index + FP_FRAME_INDEX_TEMPLATE, info_->template_info()->size,
ec_protocol_info_.max_read);
if (!fp_frame_cmd->Run(cros_fd_.get())) {
LOG(ERROR) << "Failed to get frame, result: " << fp_frame_cmd->Result();
return nullptr;
}
return fp_frame_cmd->frame();
}
bool CrosFpDevice::UploadTemplate(const VendorTemplate& tmpl) {
union cmd_with_data {
struct ec_params_fp_template req;
uint8_t _fullsize[kMaxPacketSize];
};
EcCommand<union cmd_with_data, EmptyParam> cmd(EC_CMD_FP_TEMPLATE);
struct ec_params_fp_template* req = &cmd.Req()->req;
size_t max_chunk = ec_protocol_info_.max_write -
offsetof(struct ec_params_fp_template, data);
auto pos = tmpl.begin();
while (pos < tmpl.end()) {
size_t remaining = tmpl.end() - pos;
uint32_t tlen = std::min(max_chunk, remaining);
req->offset = pos - tmpl.begin();
req->size = tlen | (remaining == tlen ? FP_TEMPLATE_COMMIT : 0);
std::copy(pos, pos + tlen, req->data);
cmd.SetReqSize(tlen + sizeof(struct ec_params_fp_template));
if (!cmd.Run(cros_fd_.get())) {
LOG(ERROR) << "Failed to run FP_TEMPLATE command";
biod_metrics_->SendUploadTemplateResult(metrics::kCmdRunFailure);
return false;
}
if (cmd.Result() != EC_RES_SUCCESS) {
LOG(ERROR) << "FP_TEMPLATE command failed @ " << pos - tmpl.begin();
biod_metrics_->SendUploadTemplateResult(cmd.Result());
return false;
}
pos += tlen;
}
biod_metrics_->SendUploadTemplateResult(EC_RES_SUCCESS);
return true;
}
std::unique_ptr<struct ec_response_flash_protect>
CrosFpDevice::GetFlashProtect() {
auto fp_cmd = ec_command_factory_->FpFlashProtectCommand(0, 0);
if (!fp_cmd) {
LOG(ERROR) << "Unable to create FP flash protect command";
return nullptr;
}
bool success = fp_cmd->Run(cros_fd_.get());
if (!success) {
return nullptr;
}
auto ret = std::make_unique<struct ec_response_flash_protect>();
memcpy(ret.get(), fp_cmd->Resp(), fp_cmd->RespSize());
return ret;
}
bool CrosFpDevice::SetContext(std::string user_hex) {
auto fp_context_cmd = ec_command_factory_->FpContextCommand(this, user_hex);
if (!fp_context_cmd) {
LOG(ERROR) << "Unable to create FP context command";
biod_metrics_->SendSetContextSuccess(false);
return false;
}
bool success = true;
FpMode original_mode = GetFpMode();
if (original_mode == FpMode(FpMode::Mode::kModeInvalid)) {
LOG(ERROR) << "Unable to get FP Mode.";
success = false;
}
// FPMCU does not allow resetting context when mode is not none, to prevent
// interrupting sensor library and leaking memory. However, for removing
// fingerprints, since the user is in the fingerprint list UI, FPMCU is in
// match mode. In this case we have to exit match mode and re-enter after
// setting context.
if (original_mode == FpMode(FpMode::Mode::kMatch)) {
LOG(INFO) << "Attempting to set context with match mode.";
if (!SetFpMode(FpMode(FpMode::Mode::kNone))) {
LOG(ERROR) << "Setting FPMCU context: failed to switch mode from match "
<< "to none.";
success = false;
}
} else if (original_mode != FpMode(FpMode::Mode::kNone)) {
LOG(ERROR) << "Attempting to set context with mode: " << original_mode
<< ".";
success = false;
}
biod_metrics_->SendSetContextMode(original_mode);
success &= fp_context_cmd->Run(cros_fd_.get());
if (original_mode == FpMode(FpMode::Mode::kMatch)) {
if (!SetFpMode(original_mode)) {
LOG(ERROR) << "Setting FPMCU context: failed to switch back to match "
<< "mode after setting context.";
success = false;
}
}
biod_metrics_->SendSetContextSuccess(success);
return success;
}
bool CrosFpDevice::ResetContext() {
FpMode cur_mode = GetFpMode();
if (cur_mode == FpMode(FpMode::Mode::kModeInvalid)) {
LOG(ERROR) << "Unable to get FP Mode.";
}
// ResetContext is called when we no longer expect any session to be running
// (such as when the user logs out or biod is starting/stopping). This check
// exists to make sure that we have disabled any matching in the firmware
// when this is called. See https://crbug.com/980614 for details.
if (cur_mode != FpMode(FpMode::Mode::kNone)) {
LOG(ERROR) << "Attempting to reset context with mode: " << cur_mode;
}
CHECK(biod_metrics_);
biod_metrics_->SendResetContextMode(cur_mode);
return SetContext(std::string());
}
bool CrosFpDevice::UpdateEntropy(bool reset) {
// Stash the most recent block id.
base::Optional<int32_t> block_id = GetRollBackInfoId();
if (!block_id) {
LOG(ERROR) << "Failed to block ID from FPMCU before entropy reset.";
return false;
}
// Reboot the EC to RO.
if (!EcReboot(EC_IMAGE_RO)) {
LOG(ERROR) << "Failed to reboot cros_fp to initialise entropy.";
return false;
}
// Initialize the secret.
if (!AddEntropy(reset)) {
LOG(ERROR) << "Failed to add entropy.";
return false;
}
// Entropy added, reboot cros_fp to RW.
if (!EcReboot(EC_IMAGE_RW)) {
LOG(ERROR) << "Failed to reboot cros_fp after initializing entropy.";
return false;
}
base::Optional<int32_t> new_block_id = GetRollBackInfoId();
if (!new_block_id) {
LOG(ERROR) << "Failed to block ID from FPMCU after entropy reset.";
return false;
}
int32_t block_id_diff = 2;
if (!reset) {
block_id_diff = 1;
}
if (new_block_id != *block_id + block_id_diff) {
LOG(ERROR) << "Entropy source has not been updated; old block_id: "
<< *block_id << ", new block_id: " << *new_block_id;
return false;
}
return true;
}
int CrosFpDevice::MaxTemplateCount() {
if (!info_ || !info_->template_info()) {
UpdateFpInfo();
}
CHECK(info_);
CHECK(info_->template_info());
return info_->template_info()->max_templates;
}
int CrosFpDevice::TemplateVersion() {
if (!info_ || !info_->template_info()) {
UpdateFpInfo();
}
CHECK(info_);
CHECK(info_->template_info());
return info_->template_info()->version;
}
int CrosFpDevice::DeadPixelCount() {
if (!info_ || !info_->template_info()) {
UpdateFpInfo();
}
CHECK(info_);
CHECK(info_->template_info());
return info_->NumDeadPixels();
}
void CrosFpDevice::SetMkbpEventCallback(CrosFpDevice::MkbpCallback callback) {
mkbp_event_ = callback;
}
} // namespace biod