2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
8 #include <sys/endian.h>
10 #include <sys/queue.h>
13 #include <machine/vmm.h>
28 struct basl_table_checksum
{
29 STAILQ_ENTRY(basl_table_checksum
) chain
;
35 struct basl_table_length
{
36 STAILQ_ENTRY(basl_table_length
) chain
;
41 struct basl_table_pointer
{
42 STAILQ_ENTRY(basl_table_pointer
) chain
;
43 uint8_t src_signature
[ACPI_NAMESEG_SIZE
];
49 STAILQ_ENTRY(basl_table
) chain
;
51 uint8_t fwcfg_name
[QEMU_FWCFG_MAX_NAME
];
56 STAILQ_HEAD(basl_table_checksum_list
, basl_table_checksum
) checksums
;
57 STAILQ_HEAD(basl_table_length_list
, basl_table_length
) lengths
;
58 STAILQ_HEAD(basl_table_pointer_list
, basl_table_pointer
) pointers
;
60 static STAILQ_HEAD(basl_table_list
, basl_table
) basl_tables
= STAILQ_HEAD_INITIALIZER(
63 static __inline
uint64_t
64 basl_le_dec(void *pp
, size_t len
)
70 return ((uint8_t *)pp
)[0];
83 basl_le_enc(void *pp
, uint64_t val
, size_t len
)
94 basl_dump_table(const struct basl_table
*const table
, const bool mem
)
96 const ACPI_TABLE_HEADER
*const header
= table
->data
;
102 data
= vm_map_gpa(table
->ctx
, BHYVE_ACPI_BASE
+ table
->off
,
109 printf("%.4s @ %8x (%s)\n", header
->Signature
,
110 BHYVE_ACPI_BASE
+ table
->off
, mem
? "Memory" : "FwCfg");
111 hexdump(data
, table
->len
, NULL
, 0);
117 basl_dump(const bool mem
)
119 struct basl_table
*table
;
121 STAILQ_FOREACH(table
, &basl_tables
, chain
) {
122 BASL_EXEC(basl_dump_table(table
, mem
));
129 basl_fill_gas(ACPI_GENERIC_ADDRESS
*const gas
, const uint8_t space_id
,
130 const uint8_t bit_width
, const uint8_t bit_offset
,
131 const uint8_t access_width
, const uint64_t address
)
135 gas
->SpaceId
= space_id
;
136 gas
->BitWidth
= bit_width
;
137 gas
->BitOffset
= bit_offset
;
138 gas
->AccessWidth
= access_width
;
139 gas
->Address
= htole64(address
);
143 basl_finish_install_guest_tables(struct basl_table
*const table
, uint32_t *const off
)
147 table
->off
= roundup2(*off
, table
->alignment
);
148 *off
= table
->off
+ table
->len
;
149 if (*off
<= table
->off
) {
150 warnx("%s: invalid table length 0x%8x @ offset 0x%8x", __func__
,
151 table
->len
, table
->off
);
156 * Install ACPI tables directly in guest memory for use by guests which
157 * do not boot via EFI. EFI ROMs provide a pointer to the firmware
158 * generated ACPI tables instead, but it doesn't hurt to install the
161 gva
= vm_map_gpa(table
->ctx
, BHYVE_ACPI_BASE
+ table
->off
, table
->len
);
163 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]", __func__
,
164 (uint64_t)BHYVE_ACPI_BASE
+ table
->off
,
165 (uint64_t)BHYVE_ACPI_BASE
+ table
->off
+ table
->len
);
168 memcpy(gva
, table
->data
, table
->len
);
174 basl_finish_patch_checksums(struct basl_table
*const table
)
176 struct basl_table_checksum
*checksum
;
178 STAILQ_FOREACH(checksum
, &table
->checksums
, chain
) {
179 uint8_t *gva
, *checksum_gva
;
185 if (len
== BASL_TABLE_CHECKSUM_LEN_FULL_TABLE
) {
189 assert(checksum
->off
< table
->len
);
190 assert(checksum
->start
< table
->len
);
191 assert(checksum
->start
+ len
<= table
->len
);
194 * Install ACPI tables directly in guest memory for use by
195 * guests which do not boot via EFI. EFI ROMs provide a pointer
196 * to the firmware generated ACPI tables instead, but it doesn't
197 * hurt to install the tables always.
199 gpa
= BHYVE_ACPI_BASE
+ table
->off
+ checksum
->start
;
200 if ((gpa
< BHYVE_ACPI_BASE
) ||
201 (gpa
< BHYVE_ACPI_BASE
+ table
->off
)) {
202 warnx("%s: invalid gpa (off 0x%8x start 0x%8x)",
203 __func__
, table
->off
, checksum
->start
);
207 gva
= vm_map_gpa(table
->ctx
, gpa
, len
);
209 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
210 __func__
, gpa
, gpa
+ len
);
214 checksum_gva
= gva
+ checksum
->off
;
215 if (checksum_gva
< gva
) {
216 warnx("%s: invalid checksum offset 0x%8x", __func__
,
222 for (uint32_t i
= 0; i
< len
; ++i
) {
225 *checksum_gva
= -sum
;
231 static struct basl_table
*
232 basl_get_table_by_signature(const uint8_t signature
[ACPI_NAMESEG_SIZE
])
234 struct basl_table
*table
;
236 STAILQ_FOREACH(table
, &basl_tables
, chain
) {
237 const ACPI_TABLE_HEADER
*const header
=
238 (const ACPI_TABLE_HEADER
*)table
->data
;
241 if (strncmp(header
->Signature
, signature
,
242 sizeof(header
->Signature
)) == 0) {
245 if (strncmp(header
->Signature
, (char *)signature
,
246 sizeof(header
->Signature
)) == 0) {
252 warnx("%s: %.4s not found", __func__
, signature
);
257 basl_finish_patch_pointers(struct basl_table
*const table
)
259 struct basl_table_pointer
*pointer
;
261 STAILQ_FOREACH(pointer
, &table
->pointers
, chain
) {
262 const struct basl_table
*src_table
;
266 assert(pointer
->off
< table
->len
);
267 assert(pointer
->off
+ pointer
->size
<= table
->len
);
269 src_table
= basl_get_table_by_signature(pointer
->src_signature
);
270 if (src_table
== NULL
) {
271 warnx("%s: could not find ACPI table %.4s", __func__
,
272 pointer
->src_signature
);
277 * Install ACPI tables directly in guest memory for use by
278 * guests which do not boot via EFI. EFI ROMs provide a pointer
279 * to the firmware generated ACPI tables instead, but it doesn't
280 * hurt to install the tables always.
282 gpa
= BHYVE_ACPI_BASE
+ table
->off
;
283 if (gpa
< BHYVE_ACPI_BASE
) {
284 warnx("%s: table offset of 0x%8x is too large",
285 __func__
, table
->off
);
289 gva
= vm_map_gpa(table
->ctx
, gpa
, table
->len
);
291 warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
292 __func__
, gpa
, gpa
+ table
->len
);
296 val
= basl_le_dec(gva
+ pointer
->off
, pointer
->size
);
297 val
+= BHYVE_ACPI_BASE
+ src_table
->off
;
298 basl_le_enc(gva
+ pointer
->off
, val
, pointer
->size
);
305 basl_finish_set_length(struct basl_table
*const table
)
307 struct basl_table_length
*length
;
309 STAILQ_FOREACH(length
, &table
->lengths
, chain
) {
310 assert(length
->off
< table
->len
);
311 assert(length
->off
+ length
->size
<= table
->len
);
313 basl_le_enc((uint8_t *)table
->data
+ length
->off
, table
->len
,
323 struct basl_table
*table
;
326 if (STAILQ_EMPTY(&basl_tables
)) {
327 warnx("%s: no ACPI tables found", __func__
);
332 * We have to install all tables before we can patch them. Therefore,
333 * use two loops. The first one installs all tables and the second one
336 STAILQ_FOREACH(table
, &basl_tables
, chain
) {
337 BASL_EXEC(basl_finish_set_length(table
));
338 BASL_EXEC(basl_finish_install_guest_tables(table
, &off
));
340 STAILQ_FOREACH(table
, &basl_tables
, chain
) {
341 BASL_EXEC(basl_finish_patch_pointers(table
));
344 * Calculate the checksum as last step!
346 BASL_EXEC(basl_finish_patch_checksums(table
));
359 basl_table_add_checksum(struct basl_table
*const table
, const uint32_t off
,
360 const uint32_t start
, const uint32_t len
)
362 struct basl_table_checksum
*checksum
;
364 assert(table
!= NULL
);
366 checksum
= calloc(1, sizeof(struct basl_table_checksum
));
367 if (checksum
== NULL
) {
368 warnx("%s: failed to allocate checksum", __func__
);
373 checksum
->start
= start
;
376 STAILQ_INSERT_TAIL(&table
->checksums
, checksum
, chain
);
382 basl_table_add_length(struct basl_table
*const table
, const uint32_t off
,
385 struct basl_table_length
*length
;
387 assert(table
!= NULL
);
388 assert(size
== 4 || size
== 8);
390 length
= calloc(1, sizeof(struct basl_table_length
));
391 if (length
== NULL
) {
392 warnx("%s: failed to allocate length", __func__
);
399 STAILQ_INSERT_TAIL(&table
->lengths
, length
, chain
);
405 basl_table_add_pointer(struct basl_table
*const table
,
406 const uint8_t src_signature
[ACPI_NAMESEG_SIZE
], const uint32_t off
,
409 struct basl_table_pointer
*pointer
;
411 assert(table
!= NULL
);
412 assert(size
== 4 || size
== 8);
414 pointer
= calloc(1, sizeof(struct basl_table_pointer
));
415 if (pointer
== NULL
) {
416 warnx("%s: failed to allocate pointer", __func__
);
420 memcpy(pointer
->src_signature
, src_signature
,
421 sizeof(pointer
->src_signature
));
423 pointer
->size
= size
;
425 STAILQ_INSERT_TAIL(&table
->pointers
, pointer
, chain
);
431 basl_table_append_bytes(struct basl_table
*const table
, const void *const bytes
,
436 assert(table
!= NULL
);
437 assert(bytes
!= NULL
);
439 if (table
->len
+ len
<= table
->len
) {
440 warnx("%s: table too large (table->len 0x%8x len 0x%8x)",
441 __func__
, table
->len
, len
);
445 table
->data
= reallocf(table
->data
, table
->len
+ len
);
446 if (table
->data
== NULL
) {
447 warnx("%s: failed to realloc table to length 0x%8x", __func__
,
453 end
= (uint8_t *)table
->data
+ table
->len
;
456 memcpy(end
, bytes
, len
);
462 basl_table_append_checksum(struct basl_table
*const table
, const uint32_t start
,
465 assert(table
!= NULL
);
467 BASL_EXEC(basl_table_add_checksum(table
, table
->len
, start
, len
));
468 BASL_EXEC(basl_table_append_int(table
, 0, 1));
474 basl_table_append_content(struct basl_table
*table
, void *data
, uint32_t len
)
476 assert(data
!= NULL
);
477 assert(len
>= sizeof(ACPI_TABLE_HEADER
));
479 return (basl_table_append_bytes(table
,
480 (void *)((uintptr_t)(data
) + sizeof(ACPI_TABLE_HEADER
)),
481 len
- sizeof(ACPI_TABLE_HEADER
)));
485 basl_table_append_gas(struct basl_table
*const table
, const uint8_t space_id
,
486 const uint8_t bit_width
, const uint8_t bit_offset
,
487 const uint8_t access_width
, const uint64_t address
)
489 ACPI_GENERIC_ADDRESS gas_le
= {
491 .BitWidth
= bit_width
,
492 .BitOffset
= bit_offset
,
493 .AccessWidth
= access_width
,
494 .Address
= htole64(address
),
497 return (basl_table_append_bytes(table
, &gas_le
, sizeof(gas_le
)));
501 basl_table_append_header(struct basl_table
*const table
,
502 const uint8_t signature
[ACPI_NAMESEG_SIZE
], const uint8_t revision
,
503 const uint32_t oem_revision
)
505 ACPI_TABLE_HEADER header_le
;
506 /* + 1 is required for the null terminator */
507 char oem_table_id
[ACPI_OEM_TABLE_ID_SIZE
+ 1];
509 assert(table
!= NULL
);
510 assert(table
->len
== 0);
512 memcpy(header_le
.Signature
, signature
, ACPI_NAMESEG_SIZE
);
513 header_le
.Length
= 0; /* patched by basl_finish */
514 header_le
.Revision
= revision
;
515 header_le
.Checksum
= 0; /* patched by basl_finish */
516 memcpy(header_le
.OemId
, "BHYVE ", ACPI_OEM_ID_SIZE
);
517 snprintf(oem_table_id
, ACPI_OEM_TABLE_ID_SIZE
, "BV%.4s ", signature
);
518 memcpy(header_le
.OemTableId
, oem_table_id
,
519 sizeof(header_le
.OemTableId
));
520 header_le
.OemRevision
= htole32(oem_revision
);
521 memcpy(header_le
.AslCompilerId
, "BASL", ACPI_NAMESEG_SIZE
);
522 header_le
.AslCompilerRevision
= htole32(0x20220504);
525 basl_table_append_bytes(table
, &header_le
, sizeof(header_le
)));
527 BASL_EXEC(basl_table_add_length(table
,
528 offsetof(ACPI_TABLE_HEADER
, Length
), sizeof(header_le
.Length
)));
529 BASL_EXEC(basl_table_add_checksum(table
,
530 offsetof(ACPI_TABLE_HEADER
, Checksum
), 0,
531 BASL_TABLE_CHECKSUM_LEN_FULL_TABLE
));
537 basl_table_append_int(struct basl_table
*const table
, const uint64_t val
,
542 assert(size
<= sizeof(val
));
544 basl_le_enc(buf
, val
, size
);
545 return (basl_table_append_bytes(table
, buf
, size
));
549 basl_table_append_length(struct basl_table
*const table
, const uint8_t size
)
551 assert(table
!= NULL
);
552 assert(size
<= sizeof(table
->len
));
554 BASL_EXEC(basl_table_add_length(table
, table
->len
, size
));
555 BASL_EXEC(basl_table_append_int(table
, 0, size
));
561 basl_table_append_pointer(struct basl_table
*const table
,
562 const uint8_t src_signature
[ACPI_NAMESEG_SIZE
], const uint8_t size
)
564 assert(table
!= NULL
);
565 assert(size
== 4 || size
== 8);
567 BASL_EXEC(basl_table_add_pointer(table
, src_signature
, table
->len
, size
));
568 BASL_EXEC(basl_table_append_int(table
, 0, size
));
574 basl_table_create(struct basl_table
**const table
, struct vmctx
*ctx
,
575 const uint8_t *const name
, const uint32_t alignment
)
577 struct basl_table
*new_table
;
579 assert(table
!= NULL
);
581 new_table
= calloc(1, sizeof(struct basl_table
));
582 if (new_table
== NULL
) {
583 warnx("%s: failed to allocate table", __func__
);
587 new_table
->ctx
= ctx
;
590 snprintf(new_table
->fwcfg_name
, sizeof(new_table
->fwcfg_name
),
591 "etc/acpi/%s", name
);
593 snprintf((char *)new_table
->fwcfg_name
, sizeof (new_table
->fwcfg_name
),
594 "etc/acpi/%s", name
);
597 new_table
->alignment
= alignment
;
599 STAILQ_INIT(&new_table
->checksums
);
600 STAILQ_INIT(&new_table
->lengths
);
601 STAILQ_INIT(&new_table
->pointers
);
603 STAILQ_INSERT_TAIL(&basl_tables
, new_table
, chain
);