Index: root/xen-unstable.hg/tools/firmware/rombios/Makefile =================================================================== --- root.orig/xen-unstable.hg/tools/firmware/rombios/Makefile +++ root/xen-unstable.hg/tools/firmware/rombios/Makefile @@ -12,7 +12,7 @@ clean: rm -f rombios*.txt rombios*.sym usage biossums rm -f BIOS-bochs-* -BIOS-bochs-latest: rombios.c biossums +BIOS-bochs-latest: rombios.c biossums tcgbios.c tcgbios.h tpm_tis.c tpm_atmel.c gcc -DBX_SMP_PROCESSORS=1 -E -P $< > _rombios_.c bcc -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s Index: root/xen-unstable.hg/tools/firmware/rombios/rombios.c =================================================================== --- root.orig/xen-unstable.hg/tools/firmware/rombios/rombios.c +++ root/xen-unstable.hg/tools/firmware/rombios/rombios.c @@ -154,6 +154,10 @@ #define BX_USE_ATADRV 1 #define BX_ELTORITO_BOOT 1 +#define BX_TCGBIOS 0 /* main switch for TCG BIOS ext. */ +#define BX_TCGBIOS_PHYSICAL_PRESENCE 0 +#define BX_TCGBIOS_SHA1_NO_HARDWARE 1 /* faster in SW than with TPM */ + #define BX_MAX_ATA_INTERFACES 4 #define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2) @@ -638,6 +642,10 @@ typedef struct { #define BiosData ((bios_data_t *) 0) +#if BX_TCGBIOS +#include "tcgbios.h" +#endif + #if BX_USE_ATADRV typedef struct { Bit16u heads; // # heads @@ -745,6 +753,9 @@ typedef struct { cdemu_t cdemu; #endif // BX_ELTORITO_BOOT +#if BX_TCGBIOS + tcpa_t tcpa; +#endif } ebda_data_t; #define EbdaData ((ebda_data_t *) 0) @@ -1844,6 +1855,9 @@ print_bios_banner() { printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":""); printf("%s %s\n", bios_cvs_version_string, bios_date_string); +#if BX_TCGBIOS + printf("TCG-enabled BIOS.\n"); +#endif printf("\n"); } @@ -7603,6 +7617,23 @@ Bit8u bseqnr; if((bootseq&0x20)==0) bootdrv=0x80; #endif // BX_ELTORITO_BOOT +#if BX_TCGBIOS + if (1) { + char *string; + if (bootcd == 0) { + if (bootdrv == 0x00) { + string = "Booting BCV Device 00h (Floppy)"; + } else { + string = "Booting BCV Device 80h (HDD)"; + } + } else { + string = "Booting from CD ROM"; + } + tcpa_add_measurement_to_log(4L, 5, 0L, get_CS(), string, + strlen(get_CS(), string)); + } +#endif + #if BX_ELTORITO_BOOT // We have to boot from cd if (bootcd != 0) { @@ -7674,6 +7705,10 @@ ASM_END } } +#if BX_TCGBIOS + tcpa_ipl(bootseg); /* specs: 8.2.3 steps 4 and 5 */ +#endif + #if BX_ELTORITO_BOOT // Print out the boot string print_boot_device(bootcd, bootdrv); @@ -7885,6 +7920,10 @@ int1a_function(regs, ds, iret_addr) } } +#if BX_TCGBIOS +#include "tcgbios.c" +#endif + void int70_function(regs, ds, iret_addr) pusha_regs_t regs; // regs pushed from PUSHA instruction @@ -9400,6 +9439,9 @@ rom_scan: ;; 2 ROM length in 512-byte blocks ;; 3 ROM initialization entry point (FAR CALL) +#if BX_TCGBIOS + call _tcpa_start_option_rom_scan /* specs: 3.2.3.3 + 10.4.3 */ +#endif mov cx, #0xc000 rom_scan_loop: mov ds, cx @@ -9418,6 +9460,17 @@ rom_scan_loop: add al, #0x04 block_count_rounded: +#if BX_TCGBIOS + push ax + push ds + mov ax, #0x0000 + mov ds, ax + push cx ;; segment where option rom is located at + call _tcpa_option_rom /* specs: 3.2.3.3 */ + pop cx ;; pop segement + pop ds + pop ax +#endif xor bx, bx ;; Restore DS back to 0000: mov ds, bx push ax ;; Save AX @@ -9683,6 +9736,11 @@ post_default_ints: in al, 0x71 mov 0x0410, ax +#if BX_TCGBIOS + call _tcpa_acpi_init + call _tcpa_initialize_tpm + call _tcpa_wake_event /* specs: 3.2.3.7 */ +#endif ;; Parallel setup SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler) @@ -9803,9 +9861,16 @@ post_default_ints: ;; #endif // BX_ELTORITO_BOOT +#if BX_TCGBIOS + call _tcpa_calling_int19h /* specs: 8.2.3 step 1 */ + call _tcpa_add_event_separators /* specs: 8.2.3 step 2 */ +#endif int #0x19 //JMP_EP(0x0064) ; INT 19h location +#if BX_TCGBIOS + call _tcpa_returned_int19h /* specs: 8.2.3 step 3/7 */ +#endif .org 0xe2c3 ; NMI Handler Entry Point nmi: @@ -10288,6 +10353,21 @@ db 0x00 ;; base 23:16 ;---------- .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point int1a_handler: +#if BX_TCGBIOS + cmp ah, #0xbb + jne no_tcg + pushf + push ds + push es + pushad + call _int1a_function32 + popad + pop es + pop ds + popf + iret +no_tcg: +#endif #if BX_PCIBIOS cmp ah, #0xb1 jne int1a_normal Index: root/xen-unstable.hg/tools/firmware/rombios/tcgbios.c =================================================================== --- /dev/null +++ root/xen-unstable.hg/tools/firmware/rombios/tcgbios.c @@ -0,0 +1,4232 @@ +/* + * Implementation of the TCG BIOS extension according to the specification + * described in + * https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementationforBIOS_1-20_1-00.pdf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Stefan Berger + */ + +/* + * Note: A mixture of 16bit real-mode code and 32 bit protected mode code + * has been implemented. All functions ending in '32' assume that they + * will be called in 32bit protected mode. 32 bit mode needs to + * be supported here since some of the pointers to data areas are + * outside the real mode-reachable area. The specs suggest big + * real-mode, but that won't work without emulation. + * + * Note: The extended BIOS data area (EBDA) is used to store some TCG ACPI + * specific information as well as hold certain register values + * (ss,cs,ds,es,MSW(esp)) during the execution in protected mode. The + * EBDA has been used since it provides a well-defined memory area + * and no application should touch it. + */ +#include "tcgbios.h" + +/* which TPM interface to use */ +#define TPM_TYPE TPM_TIS + +#define SEGMENT_OFFSET 0xf0000 +#define REAL_MODE_CODE_SEGMENT 0xf000 + +#define START_PM_CODE USE32 +#define END_PM_CODE USE16 + +/* the segment where the ACPI data is to be found */ +#define ACPI_SEGMENT 0xe000 + + ASM_START + +MACRO Calc32FromSegOff + ; Calculate a 32 bit address from segment:offset + ; + ; this is done in an order such that ss:sp can be used also + ; as parameters and sp does not change due to the 'push' + xor ?1, ?1 ;clear destination reg + mov ?2, ?4 ;mov offset to 16 bit dest. reg + push ?5 ;pop@1; save contents of scratch reg. + xor ?5, ?5 ;clear scratch register + mov ?6, ?3 ;segment to 16 bit scratch + rol ?5, #4 ;adjust 32 bit scratch for segment + add ?1, ?5 ;from final address + pop ?5 ;@1 +MEND + + ASM_END + +#if TPM_TYPE == TPM_ATMEL +#include "tpm_atmel.c" +#endif +#if TPM_TYPE == TPM_TIS +#include "tpm_tis.c" +#endif + +/* + * Utility functions to read/write from/to 32-bit addresses - to be used by + * 'C' code. + */ + +/* read a byte from a 32 bit physical address */ +Bit8u +read_byte32(addr) + Bit32u addr; +{ + ASM_START + push bp ;pop@1 + mov bp, sp + push ebx ;pop@2 + + call switch_to_protmode + START_PM_CODE + + mov ebx, 4[bp] ; addr + mov al, [ebx] ; val = *(char *)addr; + + call switch_to_realmode + END_PM_CODE + + pop ebx ;@2 + pop bp ;@1 + ASM_END +} + + +/* + read a 32bit value from a 32 bit (physical) address + return value in dx and ax: value = (register dx << 16) | register ax + */ +Bit32u +read_dword32(addr) + Bit32u addr; +{ + ASM_START + push bp ;pop@1 + mov bp, sp + push ebx ;pop@2 + + call switch_to_protmode + START_PM_CODE + + mov ebx, 4[bp] ; addr + mov ax, [ebx] ; lo(val)=*(uint16_t *)addr + inc ebx ; addr+=1 + inc ebx ; addr+=1 + mov dx, [ebx] ; hi(val)=*(uint16_t *)addr + + call switch_to_realmode + END_PM_CODE + + pop ebx ;@2 + pop bp ;@1 + ASM_END +} + +/* write a byte to a 32 bit (physical) address */ +void +write_byte32(addr, val) + Bit32u addr; + Bit8u val; +{ + ASM_START + push bp ;pop@1 + mov bp, sp + push ebx ;pop@2 + push ax ;pop@3 + + call switch_to_protmode + START_PM_CODE + + mov ebx, 4[bp] ; addr + mov al, 8[bp] ; val + mov [ebx], al ; *(char *)addr = val + + call switch_to_realmode + END_PM_CODE + + pop ax ;@3 + pop ebx ;@2 + pop bp ;@1 + + ASM_END +} + +/* copy bytes in 32 bit (physical) address space */ +void +memcpy32(des, src, n) + Bit32u des; + Bit32u src; + Bit16u n; +{ + ASM_START + push bp ;pop@1 + mov bp, sp + push ebx ;pop@2 + push edx ;pop@3 + push ecx ;pop@4 + push ax ;pop@5 + + call switch_to_protmode + START_PM_CODE + + xor ecx, ecx + + mov ebx, #4[bp] ; dest + mov edx, #8[bp] ; src + mov cx, #12[bp] ; n + +memcpy32_loop: + dec ecx + cmp ecx, #0 + jl memcpy32_end + mov al, [edx+ecx] + mov [ebx+ecx], al + jmp memcpy32_loop + +memcpy32_end: + call switch_to_realmode + END_PM_CODE + + pop ax ;@5 + pop ecx ;@4 + pop edx ;@3 + pop ebx ;@2 + pop bp ;@1 + + ret + ASM_END +} + + +/* + * Two often-used functions + */ +Bit16u +read_word_from_ebda(offset) + Bit16u offset; +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + return read_word(ebda_seg, offset); +} + +Bit32u +read_dword_from_ebda(offset) + Bit16u offset; +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + return read_dword(ebda_seg, offset); +} + +#if BX_TCGBIOS_SHA1_NO_HARDWARE +/******************************************************************* + Calculation of SHA1 without the TPM but only in SW + + See: RFC3174, Wikipedia's SHA1 alogrithm description + ******************************************************************/ +typedef struct _sha1_ctx { + Bit32u h[5]; +} sha1_ctx; + +#if 0 +void sha1_dump(ctx_seg, ctx) + Bit16u ctx_seg; + sha1_ctx *ctx; +{ + Bit8u i; + for (i = 0; i < 5; i ++) { + Bit32u d = read_dword(ctx_seg, &ctx->h[i]); + printf("h[%d] = ",i); + printf("0x%x",(Bit16u)(d>>16)); + printf("%x\n",(Bit16u)d); + } +} +#endif + + +Bit32u rol(val, rol) + Bit32u val; + Bit16u rol; +{ + return (val << rol) | (val >> (32 - rol)); +} + +/* missing implementation of xor in bcc! */ +Bit32u xor(val1, val2) + Bit32u val1; + Bit32u val2; +{ + ASM_START + push bp + mov bp, sp + + mov ax, #4[bp] ; lo + xor ax, #8[bp] + mov dx, #6[bp] ; hi + xor dx, #10[bp] + + pop bp + ret + ASM_END +} + +/* change endianess */ +Bit32u bswap(val) + Bit32u val; +{ + ASM_START + push bp + mov bp, sp + + mov dx, #4[bp] + rol dx, #8 + mov ax, #6[bp] + rol ax, #8 + + pop bp + ret + ASM_END +} + +static Bit32u ko[4] = { 0x5a827999L, + 0x6ed9eba1L, + 0x8f1bbcdcL, + 0xca62c1d6L }; + +void sha1_block(w_seg, ww, ctx_seg, ctx) + Bit16u w_seg; + Bit16u ww; + Bit16u ctx_seg; + sha1_ctx *ctx; +{ + Bit16u i; + Bit32u a,b,c,d,e,f; + Bit32u tmp; + Bit32u *w = (Bit32u *)ww; + Bit16u idx; + + /* change endianess of given data */ + for (i = 0; i < 16; i++) { + tmp = read_dword(w_seg, &w[i]); + write_dword(w_seg, &w[i], bswap(tmp)); + } + + for (i = 16; i <= 79; i++) { + tmp = xor(read_dword(w_seg, &w[i-3]), + read_dword(w_seg, &w[i-8])); + tmp = xor(tmp, read_dword(w_seg, &w[i-14])); + tmp = xor(tmp, read_dword(w_seg, &w[i-16])); + tmp = rol(tmp,1); + write_dword(w_seg, &w[i], tmp); + } + + a = read_dword(ctx_seg, &ctx->h[0]); + b = read_dword(ctx_seg, &ctx->h[1]); + c = read_dword(ctx_seg, &ctx->h[2]); + d = read_dword(ctx_seg, &ctx->h[3]); + e = read_dword(ctx_seg, &ctx->h[4]); + + for (i = 0; i <= 79; i++) { + if (i <= 19) { + f = (b & c) | (xor(b,0xffffffffL) & d); + idx = 0; + } else if (i <= 39) { + f = xor(xor(b, c), d); + idx = 1; + } else if (i <= 59) { + f = (b & c) | (b & d) | (c & d); + idx = 2; + } else { + f = xor(xor(b, c), d); + idx = 3; + } + + tmp = rol(a, 5) + + f + + e + + read_dword(get_CS(), &ko[idx]) + + read_dword(w_seg, &w[i]); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = tmp; + } + + a += read_dword(ctx_seg, &ctx->h[0]); + b += read_dword(ctx_seg, &ctx->h[1]); + c += read_dword(ctx_seg, &ctx->h[2]); + d += read_dword(ctx_seg, &ctx->h[3]); + e += read_dword(ctx_seg, &ctx->h[4]); + + write_dword(ctx_seg, &ctx->h[0], a); + write_dword(ctx_seg, &ctx->h[1], b); + write_dword(ctx_seg, &ctx->h[2], c); + write_dword(ctx_seg, &ctx->h[3], d); + write_dword(ctx_seg, &ctx->h[4], e); +} + +void sha1_do(ctx_seg, ctx, data32, length) + Bit16u ctx_seg; + sha1_ctx *ctx; + Bit32u data32; + Bit32u length; +{ + Bit32u offset; + Bit16u ss; + Bit32u dst; + Bit16u num; + Bit32u bits = 0L; + Bit8u w[80*4]; + + ss = get_SS(); + + /* 32 bit destination address of w-array */ + dst = ((Bit32u)ss << 4) + (Bit32u)&w[0]; + + /* treat data in 64-byte chunks */ + for (offset = 0L; length - offset >= 64; offset += 64) { + /* copy into the 'w' array */ + memcpy32(dst, data32 + offset, 64); + /* hash the block in the 'w' array */ + sha1_block(ss, w, ctx_seg, ctx); + bits += (64 * 8); + } + + /* last block with less than 64 bytes */ + num = length - offset; + bits += (num << 3); + + memsetb(ss, w, 0x0, 64); + memcpy32(dst, data32 + offset, num); + write_byte(ss, &w[num], 0x80); + + if (num >= 56) { + /* cannot append number of bits here */ + sha1_block(ss, w, ctx_seg, ctx); + memsetb(ss, w, 0x0, 60); + } + + /* write number of bits to end of block */ + write_dword(ss, &w[60], bswap(bits)); + sha1_block(ss, w, ctx_seg, ctx); + + /* need to switch result's endianess */ + for (num = 0; num < 5; num++) { + Bit32u tmp = read_dword(ctx_seg, &ctx->h[num]); + write_dword(ctx_seg, &ctx->h[num], bswap(tmp)); + } +} + +/* sha1 initialization constants */ +static Bit32u sha_const[5] = { + 0x67452301L, + 0xefcdab89L, + 0x98badcfeL, + 0x10325476L, + 0xc3d2e1f0L +}; + +void sha1(data32, length, hash32) + Bit32u data32; + Bit32u length; + Bit32u hash32; +{ + sha1_ctx ctx; + Bit16u ctx_seg = get_SS(); + + memcpyb(ctx_seg, &ctx.h[0], get_CS(), sha_const, 20); + sha1_do(ctx_seg, &ctx, data32, length); + + memcpy32(hash32, + (((Bit32u)ctx_seg << 4) + (Bit32u)&ctx), + 20); +} + +#if 0 +char test1[4] = "abc"; + +void sha1_test() +{ + Bit32u data32, hash32; + Bit16u i; + Bit32u hash[5]; + + memsetb(get_SS(), hash, 0x0, 20); + + data32 = ((Bit32u)get_CS() << 4) + (Bit32u)test1; + hash32 = ((Bit32u)get_SS() << 4) + (Bit32u)hash; + sha1(data32, 3L, hash32); + + sha1_dump(get_SS(), hash); +} +#endif + +#endif + +/******************************************************************* + Support for TCPA ACPI logging + ******************************************************************/ + +static char rsdp_sig[9] = "RSD PTR "; + +/* + * Extend the ACPI log with the given entry by copying the + * entry data into the log. + * Input + * Pointer to the structure to be copied into the log + * + * Output: + * lower 16 bits of return code contain entry number + * if entry number is '0', then upper 16 bits contain error code. + */ +Bit32u tcpa_extend_acpi_log(entry_ptr) + Bit32u entry_ptr; +{ + Bit32u res = 0L; + Bit16u ebda_seg = read_word(0x0040, 0x000E); + Bit32u lasa_last = tcpa_get_lasa_last_ptr(); + Bit32u lasa_base = tcpa_get_lasa_base_ptr(); + Bit32u size; + Bit16u entry_count = read_word (ebda_seg, + &EbdaData->tcpa.tcpa_acpi.entry_count); + + if (lasa_last == 0L) { + lasa_last = lasa_base; + } else { + /* skip the last entry in the log */ + size = read_dword32(lasa_last + 28); + size += 32; + lasa_last = lasa_last + size; + } + + if (lasa_last == 0L) { + res = TCG_PC_LOGOVERFLOW; + res <<= 16; + } + + if (res == 0L) { + Bit32u tcpa = read_dword(ebda_seg, + &EbdaData->tcpa.tcpa_acpi.tcpa_ptr); + Bit32u laml = read_dword32(tcpa + 0x26); + size = read_dword32(entry_ptr + 28); + size += 32; + if ((lasa_last + size - lasa_base) > laml) { + res = TCG_PC_LOGOVERFLOW; + res <<= 16; + } + } + + if (res == 0L) { + /* copy the log entry into the ACPI log */ + memcpy32(lasa_last, entry_ptr, size); + /* + * update the pointers and entry counter that were modified + * due to the new entry in the log + */ + write_dword(ebda_seg, + &EbdaData->tcpa.tcpa_acpi.lasa_last_ptr, + lasa_last); + entry_count++; + write_word (ebda_seg, + &EbdaData->tcpa.tcpa_acpi.entry_count, + entry_count); + + res = entry_count; + } + return res; +} + +/* + * Determine the length of a null-terminated ASCII string + * + * Input parameters: + * string_seg : The segment where the string is located in + * string : Offset within the segement pointing to the beginning of the + * string + * + * Returns length of the string excluding the terminating '0' in 'ax'. + */ + Bit16u +strlen(string_seg, string) + Bit16u string_seg; + Bit16u string; +{ + ASM_START + push bp ;pop@1 + mov bp, sp + push si ;pop@2 + push bx ;pop@3 + push ds ;pop@4 + xor bx,bx + xor ax,ax + + mov si, 2+2+2[bp] + mov ds, 2+2[bp] +strlen_loop_0: + seg ds + cmp byte ptr [si+bx], al + je strlen_goon_0 + inc bx + jmp strlen_loop_0 +strlen_goon_0: + mov ax, bx + + pop ds ;@4 + pop bx ;@3 + pop si ;@2 + pop bp ;@1 + ASM_END +} + +/* + * Compare two strings given their segments and offsets within the segments. + * Compare a maximum of 'n' characters. + * Returns '0' if string=string2, a value less than '0' if string1tcpa.tcpa_acpi.tcpa_ptr, + tcpa); + write_dword(ebda_seg, + &EbdaData->tcpa.tcpa_acpi.lasa_last_ptr, + 0L); + write_word (ebda_seg, + &EbdaData->tcpa.tcpa_acpi.entry_count, + 0); +} + +/* get the address of the last entry in the log */ +Bit32u +tcpa_get_lasa_last_ptr() +{ + return read_dword_from_ebda(&EbdaData->tcpa.tcpa_acpi.lasa_last_ptr); +} + +/* get the pointer to the first entry in the log */ + Bit32u +tcpa_get_lasa_base_ptr() +{ + Bit32u lasa = 0L; + Bit32u tcpa = read_dword_from_ebda(&EbdaData->tcpa.tcpa_acpi.tcpa_ptr); + if (tcpa != 0L) { + Bit16u tcpa_off = (Bit16u)tcpa; + Bit16u class = read_word(ACPI_SEGMENT, tcpa_off + 0x24); + if (class == TCPA_ACPI_CLASS_CLIENT) { + /* client type */ + lasa = read_dword(ACPI_SEGMENT, tcpa_off + 0x2a); + } else if (class == TCPA_ACPI_CLASS_SERVER) { + /* server type */ + lasa = read_dword(ACPI_SEGMENT, tcpa_off + 0x30); + } + } + return lasa; +} + +/* + * Add a measurement to the log; the data at data_seg:data/length are + * appended to the TCG_PCClientPCREventStruct + * + * Input parameters: + * pcrIndex : which PCR to extend + * event_type : type of event; specs 10.4.1 + * event_id : (unused) + * data_seg : real mode segement where the data are located + * data : offset where the data are located + * length : length of the data + */ +Bit16u +tcpa_add_measurement_to_log(pcrIndex, + event_type, + event_id, + data_seg, + data, + length) + Bit32u pcrIndex; + Bit16u event_type; + Bit32u event_id; + Bit16u data_seg; + Bit16u data; + Bit16u length; +{ + ASM_START + push bp ;pop@1 + mov bp, sp + push ecx ;pop@2 + push ebx ;pop@3 + push edx ;pop@4 + push si ;pop@5 + push ds ;pop@6 + push di ;pop@7 + push es ;pop@8 + push fs ;pop@9 + ; determine length of string + xor ebx, ebx + mov fs, 2+2+4+2+4[bp] ; data_seg + mov si, 2+2+4+2+4+2[bp] ; data + mov bx, 2+2+4+2+4+2+2[bp] ; length + + ; create room on the stack for HashLogExtendEvent input and + ; output blocks and the TCG_PCClientPCREventStruc + ;bx contains size of event in TCG_CPCleintPCREventStruc + sub sp, #(0x18+(8+20)+32) ;adjust @10 + sub sp, bx ;adjust@11 + + ; first initialize the TCG_PCClientPCREventStruc (es:di) + mov di, sp + mov ax, ss + mov es, ax + + push eax ;pop@A + + mov eax, 2+2[bp] ;pcrIndex + seg es + mov 0[di], eax + + mov ax, 2+2+4[bp] ;event_type + and eax, #0xffff + seg es + mov 4[di], eax + + pop eax ;@A + + seg es + mov 28[di], ebx ;eventDataSize + + push ebx ;pop@B +tcpa_add_measurement_to_log_loop_1: + dec ebx + jl tcpa_add_measurement_to_log_goon_1 + seg fs + mov al, [si+bx] ; copy data to end of + seg es + mov 32[di+bx], al ; PCClientPCREventStruc + jmp tcpa_add_measurement_to_log_loop_1 + +tcpa_add_measurement_to_log_goon_1: + pop ebx ;@B + + ; edx contains 32 bit address of TCG_PCClientPCREventStruc + Calc32FromSegOff(edx, dx, es, di, eax, ax) + + ; initialize the HashLogExtentEvent Input Block + add di, #32 + add di, bx + + seg es + mov word ptr 0[di], word #0x18 ;length + seg es + mov word ptr 2[di], word #0x0 ;reserved + + add edx, #32 + seg es + mov dword ptr 4[di], edx ;data to be hashed + sub edx, #32 + seg es + mov dword ptr 8[di], ebx ;length of data + + mov ecx, 2+2[bp] ;pcrIndex + seg es + mov dword ptr 12[di], ecx + + seg es + mov dword ptr 16[di], edx ;TCG_PCClientPCREventStruc + add ebx, #32 + seg es + mov dword ptr 20[di], ebx ; total length of structure + sub ebx, #32 + + ; initialize the pointers to the HashLogExtendEvent Ouptut Block + mov si, di + mov ax, sp + mov ds, ax + add si, #0x18 + + push eax + call TCG_HashLogExtendEvent + mov cx, ax + pop eax + + mov ax, cx + + add sp, bx ;@11 + add sp, #(0x18 +(8+20)+32) ;@10 + pop fs ;@9 + pop es ;@8 + pop di ;@7 + pop ds ;@6 + pop si ;@5 + pop edx ;@4 + pop ebx ;@3 + pop ecx ;@2 + pop bp ;@1 + ASM_END +} + + +/* + * Add a measurement to the log; further description of the data + * that are to be hashed are NOT appended to the TCG_PCClientPCREventStruc. + * Input parameters: + * pcrIndex : PCR to extend + * event_type : type of event; specs 10.4.1 + * ptr : 32 bit pointer to the data to be hashed + * length : length of the data to be hashed + * + * Returns lower 16 bit of return code of TCG_HashLogExtendEvent. '0' means + * success, otherwise an error is indicated. + */ +Bit16u +tcpa_add_measurement_to_log_simple(pcrIndex, event_type, ptr, length) + Bit32u pcrIndex; + Bit16u event_type; + Bit32u ptr; + Bit32u length; +{ + ASM_START + push bp ;pop@1 + mov bp, sp + push ecx ;pop@2 + push ebx ;pop@3 + push edx ;pop@4 + push si ;pop@5 + push ds ;pop@6 + push di ;pop@7 + push es ;pop@8 + + ; create room on the stack for HashLogExtendEvent input and + ; output blocks and the TCG_PCClientPCREventStruc + ;bx contains size of event in TCG_CPCleintPCREventStruc + sub sp, #(0x18+(8+20)+32) ;adjust @9 + + ; first initialize the TCG_PCClientPCREventstruc + mov di, sp + mov ax, ss + mov es, ax + + push eax ;pop@A + + mov eax, 2+2[bp] ;pcrIndex + seg es + mov 0[di], eax + + mov ax, 2+2+4[bp] ;event_type + and eax, #0xffff + seg es + mov 4[di], eax + + pop eax ;@A + + mov ecx, 2+2+4+2+4[bp] + seg es + mov 28[di], ecx ;eventDataSize + + ; edx contains 32 bit address of TCG_PCClientPCREventStruc + Calc32FromSegOff(edx, dx, es, di, eax, ax) + + ; initialize the HashLogExtentEvent Input Block + add di, #32 + + seg es + mov word ptr 0[di], word #0x18 ;length + seg es + mov word ptr 2[di], word #0x0 ;reserved + + mov ebx, 2+2+4+2[bp] ; data ptr + seg es + mov dword ptr 4[di], ebx ;data to be hashed + seg es + mov dword ptr 8[di], ecx ;length of data + + mov ecx, 2+2[bp] ;pcrIndex + seg es + mov dword ptr 12[di], ecx + + seg es + mov dword ptr 16[di], edx ;TCG_PCClientPCREventStruc + seg es + mov dword ptr 20[di], #32 ; total length of structure + + ; initialize the pointers to the HashLogExtendEvent Ouptut Block + mov si, di + mov ax, sp + mov ds, ax + add si, #0x18 + + push eax + call TCG_HashLogExtendEvent + mov cx, ax + pop eax + + mov ax, cx + + add sp, #(0x18 +(8+20)+32) ;@9 + pop es ;@8 + pop di ;@7 + pop ds ;@6 + pop si ;@5 + pop edx ;@4 + pop ebx ;@3 + pop ecx ;@2 + pop bp ;@1 + ASM_END +} + + + +/* table of event types according to 10.4.1 / table 11 */ +static char ev_action[][23] = { + /* 0 */ "Calling INT 19h", + "Returned INT 19h", + "Returned via INT 18h", + "", + "", + /* 5 */ "", + "", + "", + "", + "", + /* 10 */ "", + "", + "", + "", + "Start Option ROM Scan" +}; + +static char evt_separator[] = "---------------"; +static char wake_event_1[] = "Wake Event 1"; + +/* + * Add a measurement to the list of measurements + * pcrIndex : PCR to be extended + * event_type : type of event; specs 10.4.1 + * data : additional parameter; used as parameter for 10.4.3 + * 'action index' + */ + void +tcpa_add_measurement(pcrIndex, event_type, data) + Bit32u pcrIndex; + Bit16u event_type; + Bit32u data; +{ + char *string; + Bit16u cs = get_CS(); + + switch (event_type) { + case EV_SEPARATOR: + tcpa_add_measurement_to_log(pcrIndex, + event_type, + 0L, + cs, + evt_separator, + strlen(cs, evt_separator)); + break; + case EV_ACTION: + string = ev_action[data /* event_id */]; + tcpa_add_measurement_to_log(pcrIndex, + event_type, + data, + cs, + string, + strlen(cs, string)); + break; + } +} + +/* + * Add measurement to log about call of int 19h + */ + void +tcpa_calling_int19h() +{ + tcpa_add_measurement(4L, EV_ACTION, 0L); +} + +/* + * Add measurement to log about retuning from int 19h + */ + void +tcpa_returned_int19h() +{ + tcpa_add_measurement(4L, EV_ACTION, 1L); +} + +/* + * Add event separators for PCRs 0 to 7; specs 8.2.3 + */ + void +tcpa_add_event_separators() +{ + Bit32u pcrIndex = 0L; + while (pcrIndex <= 7L) { + tcpa_add_measurement(pcrIndex, EV_SEPARATOR, 0L); + pcrIndex ++; + } +} + +/* + * Add a wake event to the log + */ + void +tcpa_wake_event() +{ + Bit16u cs = get_CS(); + tcpa_add_measurement_to_log(6L, + EV_ACTION, + 10L, + cs, + wake_event_1, + strlen(cs, wake_event_1)); +} + +/* + * Add measurement to the log about option rom scan + * 10.4.3 : action 14 + */ + void +tcpa_start_option_rom_scan() +{ + tcpa_add_measurement(2L, EV_ACTION, 14L); +} + +/* + * Add measurement to the log about an option rom + */ + void +tcpa_option_rom(seg) + Bit16u seg; +{ + Bit32u len = read_byte(seg, 2) << 9; + Bit32u addr = seg << 4; + Bit8u append[32]; /* TCG_PCClientTaggedEventStruct and + OptionROMExecuteStructure; specs 10.4.2.1 */ + Bit8u ipb[0x10]; /* HashAll Input Block; specs 12.10 */ + + memsetb(get_SS(), append, 0x0, 32 ); + memsetb(get_SS(), ipb , 0x0, 0x10); + + append[0] = 7; /* Option ROM Execute */ + append[4] = 24;/* size of OptionROMExecute Structure */ + /* leave the rest to '0' */ + + /* 12.10 table 21 */ + ipb[0] = 0x10; /* little endian! */ + + ipb[4] = (addr >> 00) & 0xff; + ipb[5] = (addr >> 08) & 0xff; + ipb[6] = (addr >> 16) & 0xff; + ipb[7] = (addr >> 24) & 0xff; + + ipb[8] = (len >> 0) & 0xff; + ipb[9] = (len >> 8) & 0xff; + + ipb[12] = TPM_ALG_SHA; + + _TCG_HashAll(get_SS(), + ipb, + get_SS(), + append+12, + TCG_MAGIC, + 0L, + 0L); + + tcpa_add_measurement_to_log(2L, + EV_EVENT_TAG, + 0L, + get_SS(), + append, + 32); +} + +/* + * Add a measurement to the log in support of 8.2.5.3 + * Creates two log entries + * + * Input parameter: + * seg : segment where the IPL data are located + */ + void +tcpa_ipl(seg) + Bit16u seg; +{ + /* specs: 8.2.5.3 */ + Bit32u addr = (Bit32u)(seg << 4); + /* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */ + tcpa_add_measurement_to_log_simple(4L, + EV_IPL, + addr, + 0x1b8L); + /* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */ + tcpa_add_measurement_to_log_simple(5L, + EV_IPL_PARTITION_DATA, + addr + 0x1b8L, + 0x48L); +} + +/* definition of used code/data segment descriptors */ +#define PM_NORMAL_CS (gdt_entry_pm_cs - gdt_base) +#define PM_16BIT_CS (gdt_entry_pm_16bit_cs - gdt_base) +#define PM_32BIT_DS (gdt_entry_pm_32bit_ds - gdt_base) + + ASM_START + + ; Switch into protected mode to allow access to 32 bit addresses. + ; This function allows switching into protected mode. + ; (the specs says big real mode, but that will not work) + ; + ; preserves all registers and prepares cs, ds, es, ss for usage + ; in protected mode; while in prot.mode interrupts remain disabled +switch_to_protmode: + cli + + ; have to fix the stack for proper return address in 32 bit mode + push WORD #(REAL_MODE_CODE_SEGMENT>>12) ;extended return address + push bp ;pop@A1 + mov bp, sp + push eax ;pop@A2 + mov eax, 2[bp] ; fix return address + rol eax, #16 + mov 2[bp], eax + + mov eax, esp + ror eax, #16 ; hi(esp) + push ax ; remember it + push es + push ds + push cs + push ss + call _store_segment_registers + add sp, #10 + + ; calculate protected-mode esp from ss:sp + and esp, #0xffff + xor eax, eax + mov ax, ss + rol eax, #4 + add eax, esp + mov esp, eax + + seg cs + lgdt my_gdtdesc ; switch to own table + + mov eax, cr0 + or al, #0x1 ; protected mode 'on' + mov cr0, eax + + jmpf DWORD (SEGMENT_OFFSET | switch_to_protmode_goon_1), #PM_NORMAL_CS + + START_PM_CODE + +switch_to_protmode_goon_1: + mov ax, #PM_32BIT_DS ; 32 bit segment that allows + mov ds, ax ; to reach all 32 bit + mov es, ax ; addresses + mov ss, ax + + pop eax ;@A2 + pop bp ;@A1 + ret + + END_PM_CODE + + + + .align 16 +gdt_base: + ; see Intel SW Dev. Manuals section 3.4.5, Volume 3 for meaning of bits + .word 0,0 + .byte 0,0,0,0 + +gdt_entry_pm_cs: + ; 32 bit code segment for protected mode + .word 0xffff, 0x0000 + .byte 0x00, 0x9a, 0xcf, 0x00 + +gdt_entry_pm_16bit_cs: + ; temp. 16 bit code segment used while in protected mode + .word 0xffff, 0x0000 + .byte SEGMENT_OFFSET >> 16, 0x9a, 0x0, 0x0 + +gdt_entry_pm_32bit_ds: + ; (32 bit) data segment (r/w) reaching all possible areas in 32bit memory + ; 4kb granularity + .word 0xffff, 0x0000 + .byte 0x0, 0x92, 0xcf, 0x0 +gdt_entry_end: + +my_gdtdesc: + .word (gdt_entry_end - gdt_base) - 1 + .long gdt_base | SEGMENT_OFFSET + + +realmode_gdtdesc: ;to be used in real mode + .word 0xffff + .long 0x0 + + + +switch_to_realmode: + ; Implementation of switching from protected mode to real mode + ; restores all registers and prepares cs, es, ds, ss to be used + ; in real mode + START_PM_CODE + + ; need to fix up the stack to return in 16 bit mode + ; currently the 32 bit return address is on the stack + push bp ;pop@A1 + mov bp, sp + push eax ;pop@X + + mov eax, [bp] ; return address low 16bits + ; and 'bp' are being moved + mov 2[bp], eax + + pop eax ;@X + add sp, #2 ; adjust stack for 'lost' bytes + + push eax ;pop@1 + push bx ;pop@2 + push si ;pop@3 + + call _ebda_ss_offset32 ; get the offset of the ss + mov bx, ax ; entry within the ebda. + + jmpf switch_to_realmode_goon_1, #PM_16BIT_CS + + END_PM_CODE + +switch_to_realmode_goon_1: + mov eax, cr0 + and al, #0xfe ; protected mode 'off' + mov cr0, eax + + jmpf switch_to_realmode_goon_2, #REAL_MODE_CODE_SEGMENT + +switch_to_realmode_goon_2: + + ; get orig. 'ss' without using the stack (no 'call'!) + xor eax, eax ; clear upper 16 bits (and lower) + mov ax, #0x40 ; where is the ebda located? + mov ds, ax + mov si, #0xe + seg ds + mov ax, [si] ; ax = segment of ebda + + mov ds, ax ; segment of ebda + seg ds + mov ax, [bx] ; stack segment - bx has been set above + mov ss, ax + + ; from esp and ss calculate real-mode sp + rol eax, #4 + sub esp, eax + + call _get_register_ds ; get orig. 'ds' + mov ds, ax + call _get_register_es ; get orig. 'es' + mov es, ax + call _get_register_esp_hi ; fix the upper 16 bits of esp + ror esp, #16 + mov sp, ax + rol esp, #16 + + seg cs + lgdt realmode_gdtdesc + + sti ; allow interrupts + + pop si ;@3 + pop bx ;@2 + pop eax ;@1 + pop bp ;@A1 + + ret + + ASM_END + + +/* + * Helper function to get the offset of the reg_ss within the ebda struct + * Only 'C' can tell the offset. + */ +Bit16u +ebda_ss_offset32() +{ + ASM_START + START_PM_CODE // need to have this + ASM_END // compiled for protected mode + return &EbdaData->tcpa.reg_ss; // 'C' knows the offset! + ASM_START + END_PM_CODE + ASM_END +} + + ASM_START + + + +TCG_StatusCheck: + ; specs: 12.5 + ; + ; Input: + ; None at this level + ; Ouput: + ; As specified in the specs; eax contains error code, '0' for success + + ; Check whether the TPM is present at all + ; is special MADriver function call + ; Build stack for MADriverCall + push dx ;pop@1 + + mov ax, #(CODE_MAIsTPMPresent) ; MAIsTPMPresent + + call _TCG_DoMADriverCall + ; return code is in dx + and dx, dx + jne tcg_statuscheck_goon_1 + + pop dx ;@1 + mov eax, #(TCG_PC_TPMERROR | (TCG_PC_TPM_NOT_PRESENT) << 16) + + ret ; EXIT +tcg_statuscheck_goon_1: + ; Prepare paramters for DoMADriverCall + + mov ax, #(CODE_MAInitTPM) ; MAInitTPM + mov dx, #(TPM_ST_CLEAR) + + call _TCG_DoMADriverCall + ; return code is in dx + and dx, dx + je tcg_statuscheck_ok_1 + + mov ax, dx + pop dx ;@1 + ret ; EXIT +tcg_statuscheck_ok_1: + + call _tcpa_get_lasa_base_ptr; + ror eax, #16 + mov ax, dx + rol eax, #16 + mov esi, eax + + call _tcpa_get_lasa_last_ptr + ror eax, #16 + mov ax, dx + rol eax, #16 + mov edi, eax + + xor eax, eax + mov ebx, #(TCG_MAGIC) + mov ch, #0x1 + mov cl, #0x2 + xor edx, edx + + pop dx ;@1 + ret ; EXIT + + + + +TCG_HashLogExtendEvent: + ; specs: 12.6 + ; Input: + ; es:di Pointer to HashLogExtendEvent Input Block + ; ds:si Pointer to HashLogExtendEvent Output Block + ; ebx : 0x41504354 (not verified here) + ; ecx : 0 (not verified here) + ; edx : 0 (not verified here) + ; Ouput: + ; eax: error code, '0' for success + + call _TCG_IsShutdownPreBootInterface + cmp ax, #0 + je tcg_hashlogextendevent_ok_0 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INTERFACE_SHUTDOWN << 16)) + ret ;EXIT + +tcg_hashlogextendevent_ok_0: + push cx ;pop @1 + seg es + mov cx, [di] ;check input block + cmp cx, #0x18 ;short version? + je hashlogextendevent_ok_1 ;yes + cmp cx, #0x1c ;long version? + je hashlogextendevent_ok_1 ;yes + + pop cx ;@1 + + ; must initialize minimum output block + mov 0x0[si], #0x4 + mov 0x2[si], #0x0 + + mov eax, dword #(TCG_PC_TPMERROR | (TCG_INVALID_ACCESS_REQUEST << 16)) + ret ; EXIT + +hashlogextendevent_ok_1: + + ; Build a HashLogEvent Input Block on the stack + push edx ; pop @2 + push ecx ; pop @3 + push bp ; pop @4 + sub sp, #0x1c ; pop @5 + mov bp, sp + + mov 0x0[bp], #0x1c ; IPBlength + mov 0x2[bp], #0x0 ; reserved + + seg es + mov eax, dword ptr 0x4[di] ; copy HashDataPtr + mov dword 0x4[bp], eax + + seg es + mov eax, dword ptr 0x8[di] ; copy HashDataLen + mov dword 0x8[bp], eax + + seg es + mov eax, dword ptr 0xc[di] ; copy PCRIndex + mov dword 0xc[bp], eax + + ; type 1 or type 2 message? + cmp cx, #0x18 ;short input block? + jne hashlogextendevent_long_1 + + ; type 1 message (short) + seg es + mov eax, dword ptr 0x10[di] ; read LogDataPtr + seg es + mov ecx, dword ptr 0x14[di] ; read LogDataLen + + jmp hashlogextendevent_goon_1 + +hashlogextendevent_long_1: + ; type 2 message (long) + seg es + mov eax, dword ptr 0x14[di] ; read LogDataPtr + seg es + mov ecx, dword ptr 0x18[di] ; read LogDataLen + +hashlogextendevent_goon_1: + mov dword 0x14[bp], eax ; LogDataPtr + mov dword 0x18[bp], ecx ; LogDataPtr + + ; copy the LogEventType + + call switch_to_protmode + START_PM_CODE + + mov ecx, 0x4[eax] ; LogEventType from PCClientPCREventStruc + + call switch_to_realmode + END_PM_CODE + + mov dword 0x10[bp], ecx ; LogEventType + + + ; put the pointer to the LogEvent Input in es:di + push es ;pop @6 + push di ;pop @7 + mov ax, ss + mov es, ax + mov di, bp + + ; create a HashLog Event Output Block on the stack + push ds ;pop @8 + push si ;pop @9 + + sub sp, #0x8 ;adjust @10 + ; put the pointer to the LogEvent Output in ds:si + mov ax, ss + mov ds, ax + mov si, sp + + ; Call HashLogEvent + mov eax, #(TCG_MAGIC) + ; es:di LogEvent Input block + ; ds:si LogEvent Output block + call TCG_HashLogEvent + + ; want to have ss:bp to be HashLogEvent Output block + mov bp, sp + + add sp, #0x8 ;@10 + pop si ;@9 + pop ds ;@8 + pop di ;@7 + pop es ;@6 + + ; error by HashLogEvent; save it; must do Extend anyway + mov edx, eax + + ; ds:si contains pointer to HashLogExtendEvent Output block + ; es:di contains pointer to HashLogExtendEvent Input Block + ; ss:bp contains pointer to HashLogEvent Output block + + ; need to fill HashLogExtendEvent Output structure + mov 0x0[si], #0x1c ; OPBlength + mov 0x2[si], #0x0 ; Reserved + + mov eax, dword 0x4[bp] ; read EventNumber + mov dword 0x4[si], eax ; write EventNumber + + ; get the hash from the PCClientPCREventStruc + seg es + mov ax, 0x0[di] ; length + cmp ax, #0x18 ; short input block? + jne hashlogextendevent_long_2 + + ; short input block + seg es + mov eax, dword ptr 0x10[di] ; read LogDataPtr + jmp hashlogextendevent_goon_2 + +hashlogextendevent_long_2: + ; long input block + seg es + mov eax, dword ptr 0x14[di] ; read LogDataPtr + +hashlogextendevent_goon_2: + + ; copy the hash into the output block using the stack + ; to avoid switching back and forth between pm and rm + call switch_to_protmode + START_PM_CODE + + push dword #8[eax] + push dword #12[eax] + push dword #16[eax] + push dword #20[eax] + push dword #24[eax] + + call switch_to_realmode + END_PM_CODE + + pop dword #24[si] + pop dword #20[si] + pop dword #16[si] + pop dword #12[si] + pop dword #8[si] + + ; TCG_TPM_Extend + ; Parameters + ; ds:si: Hash to extend PCR with + ; eax : PCR Index + seg es + mov dword eax, dword 0xc[di] ; PCR Index + + add si, #8 ; adjust to position of hash + + call _TCG_TPM_Extend + + ; Check for error code from previously called HashLog function + cmp edx, #0 + je hashlogextendevent_goon_3 + + ; return the error code form the HashLog function instead + mov eax, edx + +hashlogextendevent_goon_3: + ; adjust si to point to the beginning of the opb + sub si, #8 + +hashlogextendevent_out_1: + add sp, #0x1c ;@5 + pop bp ;@4 + pop ecx ;@3 + pop edx ;@2 + + pop cx ;@1 + + ret + + + + + + + +TCG_PassThroughToTPM: + ; specs: 12.7 + ; + ; Input: + ; es:di Pointer to TPM Input Block + ; ds:si Pointer to TPM Output Block + ; ebx : 0x41504354 (not verified here) + ; ecx : 0 (not verified here) + ; edx : 0 (not verified here) + ; Output: + ; eax : error code, '0' for success + + call _TCG_IsShutdownPreBootInterface + cmp ax, #0 + je tcg_passthroughtotpm_ok_0 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INTERFACE_SHUTDOWN << 16)) + ret + +tcg_passthroughtotpm_ok_0: + ; interface still up + seg es + cmp 0x0[di], #0x8 ; input block ok? + jge passthroughtotpm_ok_1 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INVALID_ACCESS_REQUEST << 16)) +passthroughtotpm_err_exit_1: + ; input block too short - return error + ; must initialize minimum output block + mov 0x0[si], #0x4 + mov 0x2[si], #0x0 + + ret ; EXIT + +passthroughtotpm_ok_1: + push ecx ;pop@1 + push ebx ;pop@2 + push edx ;pop@3 + xor ecx, ecx + + seg es + mov cx, 0x4[di] ;size of output buffer + cmp cx, #0x4 + jge passthroughtotpm_ok_2 + mov eax, #(TCG_PC_TPMERROR | (TCG_INVALID_ACCESS_REQUEST << 16)) + jmp passthroughtotpm_err_exit_1 + +passthroughtotpm_ok_2: + ; should now transmit the data to the low level driver + + ; Send the data + ; ax: Code of MATransmit function + ; edx: start of input buffer + ; ecx: length of output buffer + ; ebx: pointer to place result into + + ; edx from es:[di+8] + + Calc32FromSegOff(edx, dx, es, di, eax, ax) + add edx, #8 + + ; calculate ebx from ds:[si+4] + Calc32FromSegOff(ebx, bx, ds, si, eax, ax) + add ebx, #4 + + ; actual buffer is 4 bytes shorter due to header of + ; PassThroughToTPM Output block; that is from si + 4 + sub ecx, #4 + + mov ax, #(CODE_MATransmit) ; MAIsTPMPresent + call _TCG_DoMADriverCall + + xor eax, eax + ; result is in dl + cmp dl, #0 + je passthroughtotpm_ok_3 + + ; create proper error code + mov al, dl + rol eax, #16 + mov al, #(TCG_PC_TPMERROR) + +passthroughtotpm_ok_3: + pop edx ;@3 + pop ebx ;@2 + pop ecx ;@1 + ret ; EXIT + + + +TCG_ShutdownPreBootInterface: + ; specs: 12.8 + ; + ; Input: + ; es:di Pointer to HashLogEvent Input Block + ; ds:si Pointer to HashLogEvent Output Block + ; ebx : 0x41504354 (not verified here) + ; ecx : 0 (not verified here) + ; edx : 0 (not verified here) + ; Ouput: + ; eax : error code, '0' for success + call _TCG_IsShutdownPreBootInterface + cmp ax, #0 + je tcg_shutdownprebootinterface_ok_0 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INTERFACE_SHUTDOWN << 16)) + ret + +tcg_shutdownprebootinterface_ok_0: + call _TCG_ShutdownPreBootInterface + xor eax, eax + ret + + +TCG_HashLogEvent: + ; specs: 12.9 + ; + ; Input: + ; es:di Pointer to HashLogEvent Input Block + ; ds:si Pointer to HashLogEvent Output Block + ; ebx : 0x41504354 (not verified here) + ; ecx : 0 (not verified here) + ; edx : 0 (not verified here) + ; Output: + ; eax : error code: '0' for success + call _TCG_IsShutdownPreBootInterface + cmp ax, #0 + je tcg_hashlogevent_ok_0 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INTERFACE_SHUTDOWN << 16)) + jmp hashlogevent_err_exit_1 + +tcg_hashlogevent_ok_0: + seg es + mov ax, 0x0[di] + cmp ax, #0x1c + je hashlogevent_ok_1 + mov eax, dword #(TCG_PC_TPMERROR | (TCG_INVALID_ACCESS_REQUEST << 16)) + +hashlogevent_err_exit_1: + ; must initialize minimum output block + mov word ptr 0x0[si], word #0x4 + mov word ptr 0x2[si], word #0x0 + + ret + +hashlogevent_ok_1: + ; test the TCG_PCClientPCREventStruc + push ecx ;pop @1 + push edx ;pop @2 + push ebx ;pop @3 + seg es + mov ebx, dword ptr 0x14[di] ; LogDataPtr + seg es + mov ecx, dword ptr 0x18[di] ; LogDataLen + + call switch_to_protmode + START_PM_CODE + + mov edx, 0x0[ebx] ; PCRIndex + + call switch_to_realmode + END_PM_CODE + + seg es + cmp dword 0xc[di], edx ; PCRIndex compare + je hashlogevent_ok_2 + + jmp hashlogevent_err_exit_2 ; EXIT + +hashlogevent_ok_2: + + call switch_to_protmode + START_PM_CODE + + mov edx, dword 0x4[ebx] ; EventType + + call switch_to_realmode + END_PM_CODE + + seg es + cmp dword 0x10[di], edx ; EventType compare + je hashlogevent_ok_3 + +hashlogevent_err_exit_2: + pop ebx ;@3 + pop edx ;@2 + pop ecx ;@1 + mov eax, dword #(TCG_PC_TPMERROR | (TCG_INVALID_ACCESS_REQUEST << 16)) + + jmp hashlogevent_err_exit_1 ; EXIT + +hashlogevent_ok_3: + seg es + mov edx, dword ptr 0x4[di] ; hash data ptr + seg es + mov ecx, dword ptr 0x8[di] ; hash data len + + cmp edx, #0 ; test hash data ptr != 0 + jne hashlogevent_goon_1 + cmp edx, #0 ; test hash data len != 0 + je hashlogevent_no_hashall + +hashlogevent_goon_1: + ; create a HashAll Input Parameter Block on the stack + push es ;pop @4 + push di ;pop @5 + sub sp, #0x10 ;adjust @6 + + ; let es:di point to the structure + mov ax, ss + mov es, ax + mov di, sp + + ; initialize the structure + seg es + mov 0x0[di], #0x10 ; IPBlength + seg es + mov 0x2[di], #0x0 ; Reserved + seg es + mov dword 0x4[di], edx ; HashDataPtr + seg es + mov dword 0x8[di], ecx ; HashDataLen + seg es + mov dword 0xc[di], #(TPM_ALG_SHA) ; AlgorithmID + + ; create room for a SHA1 digest on the stack + push ds ;pop @7 + push si ;pop @8 + sub sp, #20 ;adjust @9 + + ; let ds:si point to the structure + mov ax, ss + mov ds, ax + mov si, sp + + ; no initialization necessary + ; hash it + call TCG_HashAll + + ; ds:si contains hash if eax == 0 + ; check return code from TCG_HashAll + cmp eax, #0 + je hashlogevent_ok_4 + + ; something went wrong while hashing the data + add sp, #20 ;@9 + pop si ;@8 + pop ds ;@7 + + add sp, #0x10 ;@6 + pop di ;@5 + pop es ;@4 + + pop ebx ;@3 + pop edx ;@2 + pop ecx ;@1 + + jmp hashlogevent_err_exit_1 ; EXIT + + +hashlogevent_ok_4: + ; hashing was done ok + + ; copy the hash into the TCG_PCClientPCREventStruct + ; ebx contains address of this structure + + ; use reverse copying of data through the stack to avoid + ; switch between protected and real mode + push dword 0[si] + push dword 4[si] + push dword 8[si] + push dword 12[si] + push dword 16[si] + + call switch_to_protmode + START_PM_CODE + + pop dword 24[ebx] + pop dword 20[ebx] + pop dword 16[ebx] + pop dword 12[ebx] + pop dword 8[ebx] + + call switch_to_realmode + END_PM_CODE + ; hash is copied + + add sp, #20 ;@9 + pop si ;@8 + pop ds ;@7 + + add sp, #0x10 ;@6 + pop di ;@5 + pop es ;@4 + +hashlogevent_no_hashall: + ; getting here after HashAll has been call or also if not + ; ebx must contain the PCClientPCRStruct pointer + + + ; extend the log with this event + push ebx ;pop@A + call _tcpa_extend_acpi_log + pop ebx ;@A + + ; expecting eax to return the entry number somehow + ; error code in upper 16 bits and lower 16 bits + ; hold event number; 0 if error code is set + and ax, ax + jne hashlogevent_ok_5 + + ; error while updating the log + ; need minimal initialization of output block + mov 0x0[si], #0x4 + mov 0x2[si], #0x0 + ; bring down the error code to lower 16 bits + ror eax, #16 + jmp hashlogevent_out_1 ; EXIT + +hashlogevent_ok_5: + ; updating the log was fine + ; the log event number is in lower 16 bits + + ; update the output structure + mov 0x0[si], #8 + mov 0x2[si], #0 + mov dword 0x4[si], eax + + mov eax, #TCG_PC_OK + +hashlogevent_out_1: + pop ebx ;@3 + pop edx ;@2 + pop ecx ;@1 + + ret ; EXIT + + +TCG_HashAll: + ; specs: 12.10 + ; Input: + ; es:di HashAll Input Block + ; ds:si Pointer to place for digest + ; ebx : 0x41504354 (not verified here) + ; ecx : 0 (not verified here) + ; edx : 0 (not verified here) + ; Output: + ; eax : error code: '0' for success + + call _TCG_IsShutdownPreBootInterface + cmp ax, #0 + je tcg_hashall_ok_0 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INTERFACE_SHUTDOWN << 16)) + ret + +tcg_hashall_ok_0: + seg es + mov ax, 0x0[di] + cmp ax, #0x10 + je hashall_ok_1 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INVALID_ACCESS_REQUEST << 16)) + ret ; EXIT + +hashall_ok_1: + seg es + mov eax, dword 0xc[di]; ; algorithm id + cmp eax, #(TPM_ALG_SHA) + je hashall_ok_2 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INVALID_ACCESS_REQUEST << 16)) + ret ; EXIT + +hashall_ok_2: + push ecx ;pop@1 + push edx ;pop@2 + push ebx ;pop@3 + seg es + mov edx, dword ptr 0x4[di]; ; data to hash + seg es + mov ecx, dword ptr 0x8[di]; ; length of data + + ; hash the data + ; ax: Code of MAHashAll function + ; edx: start of input buffer + ; ecx: Length of input buffer + ; ebx: pointer to place digest into; must be 20 bytes long + + ; calculate ebx from ds:si + + Calc32FromSegOff(ebx, bx, ds, si, eax, ax) + + mov ax, #(CODE_MAHashAll) ; MAIsTPMPresent + call _TCG_DoMADriverCall + ; error code is in edx + mov eax, edx + + pop ebx ;@3 + pop edx ;@2 + pop ecx ;@1 + + ret + + +TCG_TSS: + ; specs: 12.11 + ; Input: + ; es:di TSS Input Parameter Block + ; ds:di TSS Output Parameter Block + ; ebx : 0x41504354 (not verified here) + ; ecx : 0 (not verified here) + ; edx : 0 (not verified here) + ; Output: + ; eax : error code: '0' for success + + call _TCG_IsShutdownPreBootInterface + cmp ax, #0 + je tcg_tss_ok_0 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INTERFACE_SHUTDOWN << 16)) + ret + +tcg_tss_ok_0: + + mov 0x0[si], #4 + mov 0x2[si], #0 + + mov eax, #(TCG_PC_UNSUPPORTED) + ret + + + +TCG_CompactHashLogExtendEvent: + ; specs: 12.12 + ; Input: + ; es:di Buffer to be hashed + ; esi: event field value + ; ebx = 0x41504354 (not verified here) + ; ecx : length of es:di buffer + ; edx : PCR Number + ; Output: + ; eax : return code + ; edx : event number that was logged + ; ** all other registers are preserved ** + + call _TCG_IsShutdownPreBootInterface + cmp ax, #0 + je tcg_compacthashlogextendevent_ok_0 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INTERFACE_SHUTDOWN << 16)) + ret + +tcg_compacthashlogextendevent_ok_0: + + ; sanity check of parameters; see 12.12 + mov ax, es + cmp ax, #0 + jne tcg_compacthashlogextendevent_goon_1 + cmp di, #0 + jne tcg_compacthashlogextendevent_goon_1 + + mov eax, #(TCG_PC_TPMERROR | (TCG_INVALID_INPUT_PARA << 16)) + ret ;EXIT + +tcg_compacthashlogextendevent_goon_1: + push bp ;pop@1 + + ; create space for a + ; HashLogExtendEvent Input + Output blocks + ; PCClientPCREventStruc block + sub sp, #(0x18 + (8 + 20) + 36) ;adjust@2 + mov bp, sp + + ; fill out HashLogExtendEvent Input Block + mov ax, #0x18 + mov 0x0[bp], ax ; IPBlength + + mov ax, #0x0 + mov 0x2[bp], ax ; Reserved + + Calc32FromSegOff(eax, ax, es, di, ebx, bx) + + mov dword 0x4[bp], eax ; HashDataPtr + mov dword 0x8[bp], ecx ; HashDataLen + mov dword 0xc[bp], edx ; PCRIndex + + Calc32FromSegOff(eax, ax, ss, bp, ebx, bx) + add eax, #(0x18 + 8 + 20) + + mov dword 0x10[bp], eax ; LogDataPtr (located on stack) + + mov eax, #32 ; size of PCClientPCREventStruc + mov dword 0x14[bp], eax + + add bp, #(0x18 + (8 + 20)) ; point to PCCliencPCREventStruc + + mov dword 0x0[bp], edx ; PCRIndex + mov eax, #(EV_COMPACT_HASH) ; eventType + mov dword 0x4[bp], eax + + mov eax, #4 + mov dword 28[bp], eax ; eventDataSize = 4 + mov dword 32[bp], esi ; eventData + + sub bp, #(8 + 20) ; point to output block + push ds ;pop@3 + push si ;pop@4 + mov ax, ss + mov ds, ax + mov si, bp + + sub bp, #0x18 ; point to input block + + push es ;pop@5 + push di ;pop@6 + mov es, ax + mov di, bp + + ; ds:si Output block + ; es:di Input Block + call TCG_HashLogExtendEvent + + mov edx, dword 0x4[si] + + pop di ;@6 + pop es ;@5 + pop si ;@4 + pop ds ;@3 + add sp, #(0x18 + (8 +20) + 36) ;@2 + pop bp ;@1 + ret + + +/* + * vendore-specific functions + */ + +_TCG_TPM_Extend: + ; Extend a given PCR with a given hash = run the TPM_Extend function + ; + ; Input: + ; ds:si: Hash to extend PCR with + ; eax : PCR Index + ; Output: + ; eax : errorcode; '0' for success + + push bp ;pop @1 + push ecx ;pop @2 + + ; make room for a + ; PassThroughToTPM + Extend Command + Output Block + ; on the stack + sub sp, #(8+34+30) ;adjust @3 + mov bp, sp + + ; Initialize the PassThroughToTPM Input Block + mov cx, #(8+34) ; total length + mov 0[bp], cx ; IPBlength + + mov cx, #0 + mov 2[bp], cx ; Reserved + mov 6[bp], cx ; Reserved + + mov cx, #30 + mov 4[bp], cx + + ; Initialize the request - adjust bp (pointer) + add bp, #8 + + mov cx, #0xc100 ; big endian 0x00, 0xc1 + mov 0[bp], cx ; Tag + + mov ecx, #0x0 + mov dword 2[bp], ecx ; clear length + mov cl, #34 + mov 5[bp], cl ; Length + + mov ecx, #0x0 + mov dword 6[bp], ecx + mov cl, #0x14 + mov 9[bp], cl ; Ordinal (Extend=0x14) + + mov 13[bp], al ; PCR Index + shr eax, #8 + mov 12[bp], al + shr eax, #8 + mov 11[bp], al + shr eax, #8 + mov 10[bp], al + + ; copy the digest + mov eax, dword 0[si] + mov dword 14[bp], eax + + mov eax, dword 4[si] + mov dword 18[bp], eax + + mov eax, dword 8[si] + mov dword 22[bp], eax + + mov eax, dword 12[si] + mov dword 26[bp], eax + + mov eax, dword 16[si] + mov dword 30[bp], eax + + sub bp, #8 ; points at Input block again + push es ;pop@4 + push di ;pop@5 + push ds ;pop@6 + push si ;pop@7 + + ; create es:di from ss:bp + ; create ds:si from ss:(bp+8+34) + mov ax, ss + mov es, ax + mov di, bp + + mov ds, ax + add bp, #(8+34) ; bp points at output block + mov si, bp + + push edx ;pop@8 + + call TCG_PassThroughToTPM + ; only the result from this operation is intersting + + pop edx ;@8 + pop si ;@7 + pop ds ;@6 + pop di ;@5 + pop es ;@4 + add sp, #(8+34+30) ;@3 + pop ecx ;@2 + pop bp ;@1 + ret + + +/* + * low-level driver support implementation + */ + +BIOSDriverHeader: + .word 0x55aa ; Signature + .long 0x00000000 ; Pointer to beginning of code + .word 0x0000 ; total size of driver +#if TPM_TYPE == TPM_TIS + .long TPM_TIS_BASE_ADDRESS ; base address of TIS interface +#elif TPM_TYPE == TPM_ATMEL + .long 0x00000000 ; ignored by that driver +#endif + .long 0x00000000 ; alternate address + .byte 0xff ; IRQ level + .byte 0xff ; DMA channel + .byte 0x00 ; XOR-checksum of driver + .byte 0x00 ; Reserved + .long 0x00000000 ; PCI PFA + .long 0x00000000 ; USB, CardBus, etc. + .long 0x00000000 ; TPM Configuration port location + + + ; MA Driver Implementation +_TCG_DoMADriverCall: + ; build the stack frame to call the MA Driver. + ; Input: + ; ax: contains function number to call + ; other registers prepared for the function's parameters + push eax ;pop@1 + push si ;pop@2 + + push ax ;adjust@3 + + ; Pointer to BIOS Driver Header + mov eax, #BIOSDriverHeader + SEGMENT_OFFSET + + push eax ;adjust@4 + + ; specs: 14.2.5 MADriver wants to return via 'retf' + callf MADriverCall, REAL_MODE_CODE_SEGMENT + + add sp, #6 ;@4,@3 + pop si ;@2 + pop eax ;@1 + ret + + + ; Implementation of MA Driver function call dispatcher + ; Call this function instead of the other ones directly! + ; +MADriverCall: + push bp ;pop@1 (this moves the layout on + ; the stack by 2!) + push ax ;pop@2 + + mov bp,sp + + ; check which function to call + mov al, (0x04+0x08)[bp] ; read FunctionNum (14.2.5) + + cmp al, #(CODE_MAInitTPM) ; MAInitTPM? + jne _madrivercall_goon_1 + + pop ax ;@2 + pop bp ;@1 + jmp _MAInitTPM ; EXIT + +_madrivercall_goon_1: + cmp al, #(CODE_MAHashAllExtendTPM) + jne _madrivercall_goon_2 + + pop ax ;@2 + pop bp ;@1 + jmp _MAHashAllExtendTPM ; EXIT + +_madrivercall_goon_2: + cmp al, #(CODE_MAPhysicalPresenceTPM) + jne _madrivercall_goon_3 + + pop ax ;@2 + pop bp ;@1 + jmp _MAPhysicalPresenceTPM ; EXIT + +_madrivercall_goon_3: + cmp al, #(CODE_MAIsTPMPresent) + jne _madrivercall_goon_4 + + pop ax ;@2 + pop bp ;@1 + jmp _MAIsTPMPresent ; EXIT + +_madrivercall_goon_4: + cmp al, #(CODE_MAHashAll) + jne _madrivercall_goon_5 + + pop ax ;@2 + pop bp ;@1 + jmp _MAHashAll ; EXIT + +_madrivercall_goon_5: + cmp al, #(CODE_MATransmit) + jne _madrivercall_goon_6 + + pop ax ;@2 + pop bp ;@1 + jmp _MATransmit ; EXIT + +_madrivercall_goon_6: + pop ax ;@2 + pop bp ;@1 + retf ; EXIT + + + +_MAInitTPM: + ; specs: 14.2.7.1 + ; Input: + ; stack layout as in 14.2.5 + ; DL = bMAInitTPMFctID; 14.2.6.3 + ; Output: + ; DL = return code + push ebp ;pop@1 + + call switch_to_protmode + START_PM_CODE + + ;let bp point to the return address + ;need this to call _MAInitTPM32 + + mov bp, sp + add bp, #4 + + call _MAInitTPM32 + + call switch_to_realmode + END_PM_CODE + + pop ebp ;@1 + retf + + + + +_MAInitTPM32: + START_PM_CODE + ; is anything selected according to 14.2.6.3? + cmp dl, #0 + jne _mainittpm32_goon_1 + ret ;EXIT + +_mainittpm32_goon_1: + push eax ;pop@1 + push ecx ;pop@2 + push ebx ;pop@3 + + mov eax, #CMD_TPM_Startup_0x01 + SEGMENT_OFFSET + ; copy buffer onto stack, 12 bytes + mov ecx, #12 + sub esp, ecx ;adjust@4 + +_mainittpm32_loop_1: + dec ecx + cmp ecx, #0 + jl _mainittpm32_goon_2 + mov bl, [eax+ecx] + mov [esp+ecx], bl + + jmp _mainittpm32_loop_1 + +_mainittpm32_goon_2: + ; stick startup type into command + mov #11[eax], dl + + ; send the command + ; edx: buffer address with data to send to the TPM + ; ecx: length of output buffer + ; ebx: pointer for output buffer + mov edx, eax + mov ecx, #10 ; 10 bytes response + + sub esp, ecx ; adjust@5 + mov ebx, esp ; Calc32Simple + + call _MATransmit32 + ; result code is in dl; becomes the result code of this function + + add esp, ecx ;@5 + add esp, #12 ;@4 + + pop ebx ;@3 + pop ecx ;@2 + pop eax ;@1 + ret + + END_PM_CODE + + +_MAHashAllExtendTPM: + ; 14.2.7.2 + ; Input: + ; stack layout as in 14.2.5 + ; edx = pointer to start addess of input buffer + ; ecx = pcrIndex and Length of input buffer + ; Stack layout as described in 14.2.5 + ; Output: + ; DL = return code + push ebp ;pop@1 + push eax ;pop@2 + + call switch_to_protmode + + START_PM_CODE + + ;let ebp point to the return address + ;need this to call _MAHashAll32 + mov ebp, esp + add ebp, #4 + + ;build 'extend' request and response on the stack + + sub esp, #(30+34) ;adjust@A + mov ebx, esp ; Calc32Simple + + ; let ebx point to where the hash goes into the Extend command + add ebx, #14 + + push edx ;pop@B + push ecx ;pop@C + and ecx, #0xffff ;only length is interesting + + ; Calculate the hash over the data + call _MAHashAll32 + ; error code in edx + and edx, edx + je _mahashallextendtpm_ok_0 + + pop ecx ;@C + pop edx ;@B + + mov edx, #(TPM_GENERAL_ERROR) + jmp _mahashallextend_exit_1 ; EXIT + +_mahashallextendtpm_ok_0: + pop ecx ;@C + pop edx ;@B + + ; build extend function + sub ebx, #14 + + mov byte 0[ebx], #0x0 ;tag + mov byte 1[ebx], #0xc1 + + mov dword 2[ebx], #0 ;length of packet + mov byte 5[ebx], #34 + + mov dword 6[ebx], #0 ;ordinal + mov byte 9[ebx], #0x14 ;ordinal 0x14=Extend + + ; write PCR + ror ecx, #16 + mov dword 10[ebx], #0 ;ordinal + mov byte 12[ebx], ch + mov byte 13[ebx], cl + rol ecx, #16 + + ; send the command + ; edx: buffer address with data to send to the TPM + ; ecx: length of output buffer + ; ebx: pointer for output buffer + push ebx ;pop@B + push ecx ;pop@C + push edx ;pop@D + + mov edx, ebx + + mov ecx, #30 + add ebx, #34 + + call _MATransmit32 + ; error code is in dl + mov al, dl + + pop edx ;@D + pop ecx ;@C + pop ebx ;@B + + mov dl, al + +_mahashallextend_exit_1: + add esp, #(30+34) ;@A + + call switch_to_realmode + + END_PM_CODE + + pop eax ;@2 + pop ebp ;@1 + retf + + + +_MAPhysicalPresenceTPM: + ; 14.2.7.3 + ; Input: + ; stack layout as in 14.2.5 + ; dx = bMAPhyPresenceTPMCmdId; 14.2.6.4 + ; Ouput: + ; dx = return code + push ebp ;pop@1 + push eax ;pop@2 + + call switch_to_protmode + START_PM_CODE + + ;let ebp point to the return address + ;need this to call _MAHashAll32 + mov ebp, esp + add ebp, #4 + + ;build 'physical presence' request and response on the stack + + push edx ;pop@A + push ecx ;pop@B + push ebx ;pop@C + + sub esp, #(12+10) ;adjust @D + + ; build command + mov ebx, esp ; Calc32Simple + + mov byte #0[ebx], #0 ;tag + mov byte #1[ebx], #0xc1 ; + + mov dword #2[ebx], #0 ;length + mov #5[ebx], #12 + + mov byte #6[ebx], #0x4 ;ordinal + mov byte #7[ebx], #0 + mov byte #8[ebx], #0 + mov byte #9[ebx], #0xa + + mov byte #10[ebx], dh ;flags + mov byte #11[ebx], dl + + ; send the command + ; edx: buffer address with data to send to the TPM + ; ecx: length of output buffer + ; ebx: pointer for output buffer + mov edx, ebx ;request buffer ptr + add ebx, #12 ;pointer to output buffer + mov ecx, #10 ;length for response + + call _MATransmit32 + + ;error code is in dl + mov al, dl + + add esp, #(12+10) ;@D + + pop ebx ;@C + pop ecx ;@B + pop edx ;@A + + mov dl, al + + call switch_to_realmode + END_PM_CODE + + pop eax ;@2 + pop ebp ;@1 + retf + + + +_MAIsTPMPresent: + ; Input: + ; stack layout as in 14.2.5 + ; nothing else + ; Output: + ; dx: 0, no TPM available, 1 TPM is available + + push ebp ;pop@1 + + call switch_to_protmode + START_PM_CODE + + mov bp, sp + add bp, #4 + + call _MAIsTPMPresent32 + + call switch_to_realmode + END_PM_CODE + + pop ebp ;@1 + retf ; EXIT + + + + +_MAIsTPMPresent32: + + START_PM_CODE + + push eax ;pop@1 + + mov eax, dword (0x04)[bp] ; pointer to BIOS Header + mov eax, dword 0x8[eax] ; base address + + ; Input : eax : base address of TPM + ; Output : dx : 0: no TPM present, 1 TPM is present + call TPM_IsPresentTPM32 + + pop eax ;@1 + ret + + END_PM_CODE + + +_MAHashAll: + ; Input: + ; stack layout as in 14.2.5 + ; edx: buffer address with data to hash + ; ecx: length of data to hash + ; ebx: pointer for result + ; Output + ; edx: Error code + push ebp ;pop@1 + + call switch_to_protmode + START_PM_CODE + + ;let ebp point to the return address + ;need this to call _MAHashAll32 + xor ebp, ebp + mov bp, sp + add bp, #4 + + call _MAHashAll32 + ; error code is in edx + + call switch_to_realmode + END_PM_CODE + + pop ebp ;@1 + retf + + +_MAHashAll32: + ; Input: + ; edx: buffer address with data to hash + ; ecx: length of data to hash + ; ebx: pointer for result + ; ebp: pointer to stack layout as in 14.2.5 + ; Output + ; edx: Error code + START_PM_CODE +#if BX_TCGBIOS_SHA1_NO_HARDWARE + call switch_to_realmode + END_PM_CODE + + push ebx ; pop@1 + push ecx ; pop@2 + push edx ; pop@3 + call _sha1 + + add esp, #12 ; @3,2,1 + + call switch_to_protmode + START_PM_CODE + xor edx, edx + ret +#else + + push eax ;pop@1 + push ebx ;pop@2 + push ecx ;pop@3 + push edi ;pop@4 + push esi ;pop@5 + + push ebx ;pop@A + push ecx ;pop@B + push edx ;pop@C + + mov edx, #CMD_TPM_SHA1Start + SEGMENT_OFFSET + + mov ecx, #14 + + sub esp, ecx ;adjust@D + + mov ebx, esp ; Calc32Simple + + call _MATransmit32 + ; error code is in dl + + add esp, ecx ;@D + + ; check 'dl' + cmp dl, #0 + beq _mahashall32_goon_0 + + ; error related to transmission + pop edx ;@C + pop ecx ;@B + pop ebx ;@A + jmp _mahashall32_goon_4 ;@5-@1 and EXIT + +_mahashall32_goon_0: + ; no transmission error + ; check result of command + cmp dword ptr #6[ebx], dword #0 + je _mahashall32_result_ok + + ; clear what is to be the hash + mov #10[ebx], dword #0 + mov #14[ebx], dword #0 + mov #18[ebx], dword #0 + mov #22[ebx], dword #0 + mov #26[ebx], dword #0 + + ; result indicates failure + pop edx ;@C + pop ecx ;@B + pop ebx ;@A + + mov edx, #(TCG_PC_TPMERROR | (TCG_TCG_COMMAND_ERROR << 16)) + + jmp _mahashall32_goon_4 + +_mahashall32_result_ok: + mov al, #11[ebx] + mov ah, #10[ebx] + rol eax, #16 + mov al, #13[ebx] ;read number of bytes that can + mov ah, #12[ebx] ;be sent to SHA1Update in one chunk + + pop edx ;@C + pop ecx ;@B + pop ebx ;@A + + ; write the number of bytes that SHA1Update can have into the + ; result array + ; use the result array as follows: + ; bytes 0-3: max. size of chunk + ; bytes 4-7: bytes left to send + + ; limit the amount of bytes to send to between 0x40 and 0xfc0 + cmp eax, #0x40 + jge _mahashall32_ok_0 + mov eax, #0x40 + +_mahashall32_ok_0: + cmp eax, #0xc00 + jle _mahashall32_ok_1 + + mov eax, #0xc00 + +_mahashall32_ok_1: + mov 0[ebx],eax + mov 4[ebx],ecx + mov esi, edx ;index into source data array + +_mahashall32_loop_1: + mov ecx, 4[ebx] ; bytes left to send + ; still more than 64 bytes to send? + cmp ecx, #64 + jle _mahashall32_sha1complete + + ; determine how many bytes to send in command + and ecx, #0xffffffc0 ; get multiple of 64 + ; less than in one chunk? + cmp ecx, 0[ebx] ; compare against max chunk size + jle _mahashall32_goon_1 ; less than max chunk size + + ; transmit 'max. chunk size' bytes in this step + mov ecx, 0[ebx] ; limit to max chunk size +_mahashall32_goon_1: + ; ecx contains number of bytes to transmit now + ; esi points at index in source + + ; update the number of bytes to still send next time + mov eax, 4[ebx] ; currently left to send + sub eax, ecx ; minus what will be sent now + mov 4[ebx], eax ; write back + + ; make room on the stack for the command head only + sub esp, #14 ; adjust@A + mov edi, esp ; Calc32Simple + + push ecx ;pop@B + + ;initialize command on stack + mov 0[edi], byte #0x00 ;Tag + mov 1[edi], byte #0xc1 ;Tag + + add ecx, #14 ;total length of packet (bulk + hdr) + + mov 5[edi], cl ;write length of packet + mov 4[edi], ch + ror ecx, #16 + mov 3[edi], cl + mov 2[edi], ch + ror ecx, #16 + + sub ecx, #14 ;number of bulk data + + mov 6[edi], dword #0 ;command code + mov byte 9[edi], #0xA1 ;SHA1Update + + mov #13[edi], cl ;write length of bulk data + mov #12[edi], ch + ror ecx, #16 + mov #11[edi], cl + mov #10[edi], ch + rol ecx, #16 + + push edx ;pop@C + + ;create structure for sending chunks on stack + ; end marker + push dword #0 ;adjust@D + + ; length of bulk data to send + push cx ;adjust@D + ; pointer to bulk + push esi ;adjust@D + + ; update source pointer by the amount that was just sent in the body + add esi, ecx + + ; SHA1_Update header + push word #14 ;adjust@D + ; SHA1_Update ptr + + push edi ;adjust@D + + mov edx, esp ; Calc32Simple + + ; create room for response on stack + push ebx ;pop@E + mov ecx, #10 ;10 bytes for response + + sub esp, ecx ;adjust@F + + mov ebx, esp ; Calc32Simple + + ; edx : pointer to parts for command + ; ecx : size for response + ; ebx : pointer for response + call _MATransmitMultiple32 + + add esp, ecx ;@F + pop ebx ;@E + add esp, #(4 + 2 * 6) ;@D + pop edx ;@C + pop ecx ;@B + add esp, #14 ;@A + + jmp _mahashall32_loop_1 + + ; Send the SHA1Complete message with the rest of the data +_mahashall32_sha1complete: + ;ecx contains amount of bytes to send now + ;esi points at next byte in bulk to send + + push ebx ;pop @A + ;build the command on the stack + + ; make room on the stack for the command including bulk + sub esp, #14 ;adjust@B + sub esp, ecx ;adjust@C + mov edi, esp ; Calc32Simple + + push ecx ;pop@D + + ;initialize command on stack + mov byte 0[edi], #0x00 ;tag + mov byte 1[edi], #0xc1 ;tag + + add ecx, #14 ;total length of packet + + mov 5[edi], cl ;write length of packet + mov 4[edi], ch ;lower 16 bits + ror ecx, #16 + mov 3[edi], cl ;upper 16 bits + mov 2[edi], ch + ror ecx, #16 + + sub ecx, #14 ;number of bulk data + + mov 6[edi], dword #0 ;command code + mov byte 9[edi], #0xA2 ;SHA1Complete + + mov #10[edi], dword #0x0 + mov #13[edi], cl ;write length of bulk data + + push edx ;pop@E + mov edx, ecx + + ; copy the bulk - 'ecx' bytes +_mahashall32_loop_3: + dec ecx + cmp ecx, #0 + jl _mahashall32_goon_3 + mov al, [esi + ecx] + mov #14[edi + ecx], al + jmp _mahashall32_loop_3 + +_mahashall32_goon_3: + mov ecx, edx ;amount of bytes sent + + ; update source pointer by the amount that was just copied + add esi, ecx + + ; prepare registers for sending command + ; edx : command + ; ecx : size for response + ; ebx : pointer for response + mov edx, edi + + mov ecx, #30 ; 30 bytes for response + + sub esp, ecx ;adjust@F + + mov ebx, esp ; Calc32Simple + + call _MATransmit32 + + mov edi, ebx + + add esp, ecx ;@F + pop edx ;@E + pop ecx ;@D + add esp, ecx ;@C + add esp, #14 ;@B + pop ebx ;@A + + ; no error occurred + xor edx, edx + + ; Should have result hash on the stack now at 10[edi] + ; copy response into output buffer - 20 bytes + mov ecx, #20 +_mahashall32_loop_4: + dec ecx + cmp ecx, #0 + jl _mahashall32_goon_4 + mov al, #10[edi+ecx] + mov [ebx+ecx], al + jmp _mahashall32_loop_4 + + +_mahashall32_goon_4: + pop esi ;@5 + pop edi ;@4 + pop ecx ;@3 + pop ebx ;@2 + pop eax ;@1 + ret +#endif + END_PM_CODE + + +wait_pit_ms_32: + ; PIT docu at + ; http://bochs.sourceforge.net/techspec/intel-82c54-timer.pdf.gz + START_PM_CODE + + push ebx ;pop@1 + push ecx ;pop@2 + push edx ;pop@3 + + shl eax, #10 ; scale the ms by ~PIT frequency + mov ebx, eax ; timeout to ebx + + xor ecx, ecx + + mov al, #(0x80+0x10) ;ctr2, lsb, mode 0, 16bit countdown + outb 0x43, al + xor al, al + outb 0x42, al ; start countdown at 0 + +wait_pit_ms_32_loop_1: + ; time expired? + cmp ebx, #0 + jle wait_pit_ms_32_exit_1 + + ; still need to wait + mov al, #0x80 ;ctr2, counter latch + outb 0x43, al + inb al, 0x42 ;current counter value + + sub cl, al ; delta since last read + sub ebx, ecx ; decrement total wait time by delta + mov cl, al ; remember current state + + jmp wait_pit_ms_32_loop_1 + +wait_pit_ms_32_exit_1: + ; stop counter 2 + xor al, al ;(0x80+0x10) + outb 0x42, al + + pop edx + pop ecx + pop ebx + ret + + END_PM_CODE + + +_MATransmit: + ; Input: + ; stack layout as in 14.2.5 + ; edx: buffer address with data to send to the TPM + ; ecx: length of output buffer + ; ebx: pointer for output buffer + ; Output: + ; dl: Error code + push ebp ;pop@1 + + ; real_to_prot trashes edx, need to save it here + call switch_to_protmode + START_PM_CODE + + mov ebp, esp + add ebp, #4 + + call _MATransmit32 + ; error code is in dl + + call switch_to_realmode + END_PM_CODE + + pop ebp ;@1 + retf + + + + ; MATransmit with access to 32bit addresses. + ; Input: + ; edx: address of buffer for TPM command to send + ; ecx: length of output buffer for response + ; ebx: result buffer pointer + ; bp: pointing to stack layout as described in 14.2.5 + ; 4[bp] must contain pointer to BIOS header! + ; Ouput: + ; dl: Error code +_MATransmit32: + + START_PM_CODE + + push eax ;pop@1 + + ; address of buffer to send into eax + mov eax, edx + + ; address of TPM into edx + mov edx, dword 0x04[bp] ; pointer to BIOS Header + mov dword edx, dword 0x8[edx] ; base address of TPM + + push eax ;pop@2 + + ; activate the TPM + call TPM_ActivateTPM32 + cmp al, #1 + je matransmit32_goon_0 + + ; TPM not active + pop eax ;@2 + + mov dl, #TCG_FATAL_COM_ERROR + + jmp matransmit32_exit_0 ; EXIT + +matransmit32_goon_0: + pop eax ;@2 + + ; send command + ; eax : data + ; edx : addr of TPM + ; ecx : length of data to send + push ecx ;pop@3 + mov ch, 2[eax] + mov cl, 3[eax] + rol ecx, #16 + mov ch, 4[eax] + mov cl, 5[eax] + call TPM_SendDataTPM32 + + pop ecx ;@3 + + push edx ;pop@A + ; wait for data to become valid + call TPM_WaitDataValidTPM32 + ; error code in dl, 0 for success + cmp dl, #0 + je matransmit32_goon_1 + + ; data not valid + + pop edx ;@A + + mov dl, #TCG_BLOCK_READ_TIMEOUT + + jmp matransmit32_exit_0 ; EXIT + +matransmit32_goon_1: + pop edx ;@A + push edx ;pop@B + ; wait for response to become ready + call TPM_WaitResponseReadyTPM32 + cmp dl, #0 + je matransmit32_goon_2 + + pop edx ;@B + ; data not ready + + mov dl, #TCG_BLOCK_READ_TIMEOUT + + jmp matransmit32_exit_0 ; EXIT + +matransmit32_goon_2: + pop edx ;@B + push edx ;pop@C + ; now read the response + + call TPM_ReadResponseTPM32 + + pop edx ;@C + + call TPM_ReadyTPM32 + mov dl, #TPM_OK + + jmp matransmit32_exit_1 ; EXIT + +matransmit32_exit_0: + call TPM_ReadyTPM32 + +matransmit32_exit_1: + pop eax ;@1 + + ; result in 'dl' + ret + + END_PM_CODE + + + + ; MATransmitMultiple32 with access to 32bit addresses. + ; Input: + ; edx: address of structure with TPM command blocks to send + ; (4 byte pointer to data, 2 byte describing size) + ; ecx: length of output buffer for response + ; ebx: result buffer pointer + ; bp: pointing to stack layout as described in 14.2.5 + ; 4[bp] must contain pointer to BIOS header! + ; Ouput: + ; dl: Error code +_MATransmitMultiple32: + + START_PM_CODE + + push eax ;pop@1 + + ; address of buffer to send from edx into eax + mov eax, edx + + ; address of TPM into edx + mov edx, dword 0x04[bp] ; pointer to BIOS Header + mov dword edx, dword 0x8[edx] ; base address of TPM + + push eax ;pop@2 + + ; activate the TPM + call TPM_ActivateTPM32 + cmp al, #1 + je matransmitmultiple32_goon_0 + + ; TPM not active + pop eax ;@2 + + mov dl, #TCG_FATAL_COM_ERROR + + jmp matransmitmultiple32_exit_0 ; EXIT + +matransmitmultiple32_goon_0: + pop eax ;@2 + + push ecx ;pop@A1 + xor ecx, ecx +matransmitmultiple32_loop_0: + push eax ;pop@A2 + push ecx ;pop@A3 + push ebx ;pop@A4 + ; send command + ; eax : data + ; edx : addr of TPM + ; ecx : length of data to send + + ; actual buffer with data into eax + mov ebx, [eax + ecx] + and ebx, ebx + je matransmitmultiple32_goon_3 + + ; length of data to send into ecx + mov cx, 4[eax + ecx] + and ecx, #0xffff + + mov eax, ebx + call TPM_SendDataTPM32 + + pop ebx ;@A4 + pop ecx ;@A3 + pop eax ;@A2 + + ; next chunk + add ecx, #6 + + jmp matransmitmultiple32_loop_0 ; LOOP + +matransmitmultiple32_goon_3: + pop ebx ;@A4 + pop ecx ;@A3 + pop eax ;@A2 + pop ecx ;@A1 + + push edx ;pop@A + ; wait for data to become valid + call TPM_WaitDataValidTPM32 + ; error code in dl, 0 for success + cmp dl, #0 + je matransmitmultiple32_goon_1 + + ; data not valid + + pop edx ;@A + + mov dl, #TCG_BLOCK_READ_TIMEOUT + + jmp matransmitmultiple32_exit_0 + +matransmitmultiple32_goon_1: + pop edx ;@A + push edx ;pop@B + ; wait for response to become ready + call TPM_WaitResponseReadyTPM32 + cmp dl, #0 + je matransmitmultiple32_goon_2 + + pop edx ;@B + ; data not ready + + mov dl, #TCG_BLOCK_READ_TIMEOUT + + jmp matransmitmultiple32_exit_0 + +matransmitmultiple32_goon_2: + pop edx ;@B + push edx ;pop@C + ; now read the response + + call TPM_ReadResponseTPM32 + + pop edx ;@C + + call TPM_ReadyTPM32 + mov dl, #TPM_OK + + jmp matransmitmultiple32_exit_1 + +matransmitmultiple32_exit_0: + call TPM_ReadyTPM32 + +matransmitmultiple32_exit_1: + pop eax ;@1 + + ; result in 'dl' + ret + + END_PM_CODE + + + + +CMD_TPM_Startup_0x01_IPB: + .word 0x8+0xc, 0x00, 4+10, 0x00 +CMD_TPM_Startup_0x01: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x99, 0x00, 0x01 + +CMD_TSC_PhysicalPresence_0x20_IPB: + .word 0x8+0xc, 0x00, 4+10, 0x00 +CMD_TSC_PhysicalPresence_0x20: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x00, 0x20 + +CMD_TSC_PhysicalPresence_0x08_IPB: + .word 0x8+0xc, 0x00, 4+10, 0x00 +CMD_TSC_PhysicalPresence_0x80: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x00, 0x08 + +CMD_TSC_PhysicalPresence_0x100_IPB: + .word 0x8+0xc, 0x00, 4+10, 0x00 +CMD_TSC_PhysicalPresence_0x100: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x01, 0x00 + +CMD_TSC_PhysicalPresence_0x10_IPB: + .word 0x8+0xc, 0x00, 4+10, 0x00 +CMD_TSC_PhysicalPresence_0x10: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x0a, 0x00, 0x10 + +CMD_TPM_PhysicalEnable_IPB: + .word 0x8+0xa, 0x00, 4+10, 0x00 +CMD_TPM_PhysicalEnable: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x6f + +CMD_TPM_PhysicalSetDeactivated_0x00_IPB: + .word 0x8+0xb, 0x00, 4+10, 0x00 +CMD_TPM_PhysicalSetDeactivated_0x00: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x72, 0x00 + +CMD_TPM_SHA1Start_IPB: + .word 0x8+0xa, 0x00, 4+10, 0x00 +CMD_TPM_SHA1Start: + .byte 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xa0 + + +/* command list table referencing above input blocks */ +__TCG_CommandList: + .word CMD_TPM_Startup_0x01_IPB + .word CMD_TSC_PhysicalPresence_0x20_IPB + .word CMD_TSC_PhysicalPresence_0x08_IPB + .word CMD_TSC_PhysicalPresence_0x100_IPB + .word CMD_TSC_PhysicalPresence_0x10_IPB + .word CMD_TPM_PhysicalEnable_IPB + .word CMD_TPM_PhysicalSetDeactivated_0x00_IPB + .word CMD_TPM_SHA1Start_IPB + + +_TCG_SendCommand: + ; parameters + ; edx: Constant defining which command to send from the list + ; ds:si : pointer to memory for output block + ; ecx : size of output block (0 for no output block provided) + ; The output block MUST be of the size required for the + ; function that is called; MAX is currently 30 + ; A minimum of 14 bytes must be provided, if any is + ; provided; 0 may be passed otherwise so that the function + ; allocates memory on the stack + ; Return + ; eax: return error code + ; A TPM error will be indictad by + ; (tpm_error_code << 16 | 0x4) + ; + ; ds:si : updated output block if one was provided + push si ;pop@1 + push cx ;pop@3 + push edx ;pop@4 + push bx ;pop@5 + push es ;pop@6 + + ; move the index to eax + mov eax, edx + + ; get the address of the actual command + ; data are located in the code segment! + mov di, #__TCG_CommandList + shl ax, #1 + mov bx, ax + seg cs + mov di, [di+bx] + + mov ax, cs + mov es, ax + + ; did user provide buffer? + and ecx, ecx + je _tcg_sendcommand_allocbuffer + + ; check whether we have at least 4+10 bytes for output + ; 4 bytes are for the plain output header, 10 bytes + ; so we can receive the TPM error code + cmp ecx, #14 + jge _tcg_sendcommand_goon_1 + + ; not enough space for output buffer + mov eax, #((TCG_INVALID_COM_REQUEST << 16 ) | TCG_PC_TPMERROR) + jmp _tcg_sendcommand_goon_2 ; EXIT + +_tcg_sendcommand_allocbuffer: + push ds ;@7 + + ; determine length of output needed command + seg es + mov dx, 0x4[di] + + ; create an Output block on the stack and + + add dx, #0x4 ; plain Header of Output block + sub sp, dx ;adjust@8 + + mov ax, ss + mov ds, ax + mov si, sp + +_tcg_sendcommand_goon_1: + ; ds:si pointer to output block + ; es:di pointer to input block + call TCG_PassThroughToTPM + + ; direct error from the PassThrough? + and eax, eax + jne _tcg_sendcommand_goon_3 + + ; and error indicated by the tpm ? + seg ds + mov eax, #(4+6)[si] + cmp eax, #0 + je _tcg_sendcommand_goon_3 + + ; TPM indicated error code; make it visible to the caller + rol eax, #16 + mov ax, #4 + +_tcg_sendcommand_goon_3: + ; did user provide output buffer? if yes, I did not allocate one + and ecx, ecx + jne _tcg_sendcommand_goon_2 + + add sp, dx ;@8 + pop ds ;@7 + +_tcg_sendcommand_goon_2: + pop es ;@6 + pop bx ;@5 + pop edx ;@4 + pop cx ;@3 + pop si ;@1 + ret + + + + +_TCG_TransmitBuffer: + ; Input + ; es:di : pointer to buffer to send to TPM; must be a TPM formatted + ; command + ; ds:si : pointer to memory for output block + ; ecx : size of output block (0 for no output block provided) + ; Output + ; eax: return error code + ; ds:si : updated output block if one was provided + push cx ;pop@1 + push edx ;pop@2 + push bx ;pop@3 + push bp ;pop@4 + + ; determine length of input block needed for command + seg es + mov dl, 0x5[di] + seg es + mov dh, 0x4[di] + add dx, #8 + + ; create an Output block on the stack and + sub sp, dx ;adjust@5 + mov bp, sp + push dx ;pop@6 + + mov 0x0[bp], dx ; IPBlength + xor bx,bx + mov 0x2[bp], bx ; Reserved + mov 0x4[bp], cx ; Output block length + mov 0x6[bp], bx ; Reserved + + ; copy command buffer into 0x8[es:bp] + sub dx, #8 + mov bx, dx ; bx is counter + + push es ;pop@7 + push di ;pop@8 + push si ;pop@9 + push ds ;pop@10 + + mov si, bp + mov ax, ss + mov ds, ax + + ; copy ommand into PassThroughInput block +_tcg_transmitbuffer_loop_1: + and bx, bx + je _tcg_transmitbuffer_goon_1 + dec bx + + seg es + mov al, [di+bx] + mov 0x8[si+bx], al + + jmp _tcg_transmitbuffer_loop_1 + +_tcg_transmitbuffer_goon_1: + pop ds ;@10 + pop si ;@9 + ; es:di pointer to block to send to TPM + ; ds:si pointer to output block for result + mov ax, ss + mov es, ax + mov di, bp + + call TCG_PassThroughToTPM + + pop di ;@8 + pop es ;@7 + pop dx ;@6 + add sp, dx ;@5 + pop bp ;@4 + pop bx ;@3 + pop edx ;@2 + pop cx ;@1 + ret + + +_tpm_initialize_tpm: + ; Initialize the TPM by sending the initial command + ; sequence of depending on physical presence + ; TPM_Startup(0x1) + ; if pp: + ; TSC_PhysicalPresence(0x20) + ; TSC_PhysicalPresence(0x08) + ; TPM_PhysicalEnable() + ; TPM_PhysicalSetDeactivated(FALSE) + ; else + ; TSC_PhysicalPresence(0x100) + ; TSC_PhysicalPresence(0x10) + ; to it. + ; + ; Inputs + ; al : 0 for no physical presence, 1 for physical presence + ; Ouputs: + ; eax : errorcode, '0' for no encountered error + + push ebx ;pop@1 + push ecx ;pop@2 + push edx ;pop@3 + + mov bl, al ;physical presence status + ; no buffer is provided for result + xor ecx, ecx + + mov edx, #IDX_CMD_TPM_Startup_0x01 + call _TCG_SendCommand + ; if this one fails, it is ok. + ; and eax, eax + ; jne _tpm_initializetpm_exit_1 + + cmp bl, #0 ;physical presence? + je _tpm_initialize_tpm_no_pp + + mov edx, #IDX_CMD_TSC_PhysicalPresence_0x20 + call _TCG_SendCommand + and eax, eax + jne _tpm_initializetpm_exit_1 + + mov edx, #IDX_CMD_TSC_PhysicalPresence_0x08 + call _TCG_SendCommand + and eax, eax + jne _tpm_initializetpm_exit_1 + + mov edx, #IDX_CMD_TPM_PhysicalEnable + call _TCG_SendCommand + and eax, eax + jne _tpm_initializetpm_exit_1 + + mov edx, #IDX_CMD_TPM_PhysicalSetDeactivated_0x00 + call _TCG_SendCommand + jmp _tpm_initializetpm_exit_1 + +_tpm_initialize_tpm_no_pp: + mov edx, #IDX_CMD_TSC_PhysicalPresence_0x100 + call _TCG_SendCommand + and eax, eax + jne _tpm_initializetpm_exit_1 + + mov edx, #IDX_CMD_TSC_PhysicalPresence_0x10 + call _TCG_SendCommand + and eax, eax + jne _tpm_initializetpm_exit_1 + +_tpm_initializetpm_exit_1: + pop edx ;@3 + pop ecx ;@2 + pop ebx ;@1 + ret + + + +_tcpa_initialize_tpm: + push eax ;pop@1 + push bx ;pop@2 + +#if BX_TCGBIOS_PHYSICAL_PRESENCE + xor bx, bx + + call switch_to_protmode + START_PM_CODE + + ; wait for 1000 ms to give user a chance to press a key + mov eax, #1000 + call wait_pit_ms_32 + + call switch_to_realmode + END_PM_CODE + + ; determine whether a key has been pressed + mov ah, #1 + int #0x16 + je _tcpa_initialize_tpm_no_key + + ; currently any key press is sufficient to assert physical presence + call _print_physicalpresence + mov bx, #1 ; physical presence +_tcpa_initialize_tpm_loop_1: + mov ah, #0 ; drain key presses + int #0x16 + + mov ah, #1 ; more keystrokes? + int #0x16 + jne _tcpa_initialize_tpm_loop_1 ; drain more + +_tcpa_initialize_tpm_no_key: + mov ax, bx +#else + mov ax, #1 +#endif + call _tpm_initialize_tpm + pop bx ;@2 + pop eax ;@1 + ret + + ASM_END + +#if BX_TCGBIOS_PHYSICAL_PRESENCE + void print_physicalpresence() + { + printf("Physical presence asserted\n"); + } +#endif + + // GLUE CODE section for transition between C code and + // TCG BIOS calling convention + + ASM_START +_tcg_callfunc_es_di_ds_si_ebx_ecx_edx: + ; + ; Call a function with parameters in registers + ; es,di,ds,si,ebx,ecx,edx + ; Input: + ; The address of the function to be call is expected in 'ax' + ; bp : expected to be set when this function is called + ; + push es + push di + push ds + push si + push ebx + push ecx + push edx + + push ax + + mov ax, 4[bp] + mov es, ax + mov ax, 6[bp] + mov di, ax + mov ax, 8[bp] + mov ds, ax + mov ax, 10[bp] + mov si, ax + mov ebx, dword ptr 12[bp] + mov ecx, dword ptr 16[bp] + mov edx, dword ptr 20[bp] + + pop ax + seg cs + call ax + + pop edx + pop ecx + pop ebx + pop si + pop ds + pop di + pop es + + // 32 bit return code is expected in ax/dx by the 'C' interface + ror eax, #16 + mov dx, ax + ror eax, #16 + ret + ASM_END + + + // 12.5 TCG_StatusCheck -- Glue code + Bit32u + _TCG_StatusCheck() + { + ASM_START + call TCG_StatusCheck + + // 32 bit return code is expected in ax/dx by the 'C' interface + ror eax, #16 + mov dx, ax + rol eax, #16 + ASM_END + } + + + // 12.6 TCG_HashLogExtendEvent - Glue function + Bit32u + _TCG_HashLogExtendEvent(es, di, ds, si, ebx, ecx, edx) + Bit16u es; + Bit16u di; + Bit16u ds; + Bit16u si; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + { + ASM_START + push bp ;pop@1 + mov bp, sp + + mov ax, #TCG_HashLogExtendEvent + call _tcg_callfunc_es_di_ds_si_ebx_ecx_edx + + pop bp ;@1 + ASM_END + } + + + // 12.7 TCG_PassThroughToTPM - Glue function + Bit32u + _TCG_PassThroughToTPM(es, di, ds, si, ebx, ecx, edx) + Bit16u es; + Bit16u di; + Bit16u ds; + Bit16u si; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + { + ASM_START + push bp + mov bp, sp + + mov ax, #TCG_PassThroughToTPM + call _tcg_callfunc_es_di_ds_si_ebx_ecx_edx + + pop bp + ASM_END + } + + // 12.8 TCG_ShutdownPreBootInterface - Glue function + Bit32u + _TCG_ShutdownPreBootInterface(ebx) + Bit32u ebx; + { + ASM_START + push bp ;pop@1 + mov bp, sp + + push ebx ;pop@2 + + mov ebx, dword ptr 4[bp] + call TCG_ShutdownPreBootInterface + + pop ebx ;@2 + pop bp ;@1 + + // 32 bit return code is expected in ax/dx by the 'C' interface + ror eax, #16 + mov dx, ax + ror eax, #16 + ASM_END + } + + + // 12.9 TCG_HashLogEvent - Glue function + Bit32u + _TCG_HashLogEvent(es, di, ds, si, ebx, ecx, edx) + Bit16u es; + Bit16u di; + Bit16u ds; + Bit16u si; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + { + ASM_START + push bp ;pop@1 + mov bp, sp + + mov ax, #TCG_HashLogEvent + call _tcg_callfunc_es_di_ds_si_ebx_ecx_edx + + pop bp ;@1 + ASM_END + } + + + // 12.10 TCG_HashAll - Glue Function + Bit32u + _TCG_HashAll(es, di, ds, si, ebx, ecx, edx) + Bit16u es; + Bit16u di; + Bit16u ds; + Bit16u si; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + { + ASM_START + push bp ;pop@1 + mov bp, sp + + mov ax, #TCG_HashAll + call _tcg_callfunc_es_di_ds_si_ebx_ecx_edx + + pop bp ;@1 + ASM_END + } + + + // 12.11 TCG_TSS - Glue function + Bit32u + _TCG_TSS(es, di, ds, si, ebx, ecx, edx) + Bit16u es; + Bit16u di; + Bit16u ds; + Bit16u si; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + { + ASM_START + push bp ;pop@1 + mov bp, sp + + mov ax, #TCG_TSS + call _tcg_callfunc_es_di_ds_si_ebx_ecx_edx + + pop bp ;@1 + ASM_END + } + + + // 12.12 TCG_CompactHashLogExtendEvent - Glue function + Bit32u + _TCG_CompactHashLogExtendEvent(es, di, esi, ebx, ecx, edx, edx_ptr) + Bit16u es; + Bit16u di; + Bit32u esi; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + Bit16u edx_ptr; + { + ASM_START + push bp ;pop@1 + mov bp, sp + + push es ;pop@2 + push di ;pop@3 + push esi ;pop@4 + push ebx ;pop@5 + push ecx ;pop@6 + push edx ;pop@7 + + mov ax, 4[bp] + mov es, ax + mov ax, 6[bp] + mov di, ax + mov esi, dword ptr 8[bp] + mov ebx, dword ptr 12[bp] + mov ecx, dword ptr 16[bp] + mov edx, dword ptr 20[bp] + call TCG_CompactHashLogExtendEvent + + push bp ;pop@8 + mov bp, word ptr 24[bp] ; edx_ptr + mov [bp], edx + pop bp ;@8 + + pop edx ;@7 + pop ecx ;@6 + pop ebx ;@5 + pop esi ;@4 + pop di ;@3 + pop es ;@2 + + pop bp ;@1 + + // 32 bit return code is expected in ax/dx by the 'C' interface + ror eax, #16 + mov dx, ax + ror eax, #16 + ASM_END + } + + + // 12.12 TCG_SendCommand - Glue function + Bit32u + __TCG_SendCommand(edx) + Bit32u edx; + { + ASM_START + push bp ;pop@1 + mov bp, sp + + push edx ;pop@2 + + mov edx, dword ptr 4[bp] + call _TCG_SendCommand + + pop edx ;@2 + pop bp ;@1 + + // 32 bit return code is expected in ax/dx by the 'C' interface + ror eax, #16 + mov dx, ax + ror eax, #16 + ASM_END + } + + +/* + * Store registers in the EBDA; used to keep the registers' + * content in a well-defined place during protected mode execution + */ + void +store_segment_registers(ss, cs, ds, es, esp_hi) + Bit16u ss, cs, ds, es, esp_hi; +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + write_word(ebda_seg, &EbdaData->tcpa.reg_ss, ss); + write_word(ebda_seg, &EbdaData->tcpa.reg_cs, cs); + write_word(ebda_seg, &EbdaData->tcpa.reg_ds, ds); + write_word(ebda_seg, &EbdaData->tcpa.reg_es, es); + write_word(ebda_seg, &EbdaData->tcpa.esp_hi, esp_hi); +} + + + +/* + * get the segment register 'cs' value from the EBDA + */ +Bit16u +get_register_cs() +{ + return read_word_from_ebda(&EbdaData->tcpa.reg_cs); +} + +/* + * get the segment register 'ds' value from the EBDA + */ +Bit16u +get_register_ds() +{ + return read_word_from_ebda(&EbdaData->tcpa.reg_ds); +} + +/* + * get the segment register 'es' value from the EBDA + */ +Bit16u +get_register_es() +{ + return read_word_from_ebda(&EbdaData->tcpa.reg_es); +} + +/* + * get the upper 16 bits of the esp from the EBDA + */ +Bit16u +get_register_esp_hi() +{ + return read_word_from_ebda(&EbdaData->tcpa.esp_hi); +} + +/* + * Check whether the TCG interface has been shut down + */ +Bit16u +TCG_IsShutdownPreBootInterface() +{ + Bit16u res = read_word_from_ebda(&EbdaData->tcpa.flags); + res &= STATUS_FLAG_SHUTDOWN; + return res; +} + +/* + * Make the TCG interface as having been shut down + */ +void +TCG_ShutdownPreBootInterface() +{ + Bit16u ebda_seg = read_word(0x0040, 0x000E); + Bit16u flags = read_word(ebda_seg, &EbdaData->tcpa.flags); + write_word(ebda_seg, flags | STATUS_FLAG_SHUTDOWN); +} + +/* + * C-dispatcher for the TCG BIOS functions + */ + void +int1a_function32(regs, ES, DS, FLAGS) + pushad_regs_t regs; + Bit16u ES, DS, FLAGS; +{ + switch (regs.u.r8.ah) { + case 0xbb: + /* + * all functions except for TCG_StatusCheck need to have the + * TCG_MAGIC in 'ebx'. + */ + if (regs.u.r8.al != 0 && + regs.u.r32.ebx != TCG_MAGIC) { + SET_CF(); + return; + } + switch(regs.u.r8.al) { + case 0x00: + regs.u.r32.eax = _TCG_StatusCheck(); + if (regs.u.r32.eax == 0L) { + regs.u.r32.eax = 0L; + regs.u.r32.ebx = TCG_MAGIC; + regs.u.r8.ch = TCG_VERSION_MAJOR; + regs.u.r8.cl = TCG_VERSION_MINOR; + regs.u.r32.edx = 0x0L; + regs.u.r32.esi = tcpa_get_lasa_base_ptr(); + regs.u.r32.edi = tcpa_get_lasa_last_ptr(); + CLEAR_CF(); + } + break; + + case 0x01: + regs.u.r32.eax = + _TCG_HashLogExtendEvent(ES, + regs.u.r16.di, + DS, + regs.u.r16.si, + regs.u.r32.ebx, + regs.u.r32.ecx, + regs.u.r32.edx); + CLEAR_CF(); + break; + + case 0x02: + regs.u.r32.eax = + _TCG_PassThroughToTPM(ES, + regs.u.r16.di, + DS, + regs.u.r16.si, + regs.u.r32.ebx, + regs.u.r32.ecx, + regs.u.r32.edx); + CLEAR_CF(); + break; + + case 0x03: + regs.u.r32.eax = + _TCG_ShutdownPreBootInterface(regs.u.r32.ebx); + CLEAR_CF(); + break; + + case 0x04: + regs.u.r32.eax = + _TCG_HashLogEvent(ES, + regs.u.r16.di, + DS, + regs.u.r16.si, + regs.u.r32.ebx, + regs.u.r32.ecx, + regs.u.r32.edx); + CLEAR_CF(); + break; + + case 0x05: + regs.u.r32.eax = + _TCG_HashAll(ES, + regs.u.r16.di, + DS, + regs.u.r16.si, + regs.u.r32.ebx, + regs.u.r32.ecx, + regs.u.r32.edx); + CLEAR_CF(); + break; + + case 0x06: + regs.u.r32.eax = + _TCG_TSS(ES, + regs.u.r16.di, + DS, + regs.u.r16.si, + regs.u.r32.ebx, + regs.u.r32.ecx, + regs.u.r32.edx); + CLEAR_CF(); + break; + + case 0x07: + regs.u.r32.eax = + _TCG_CompactHashLogExtendEvent(ES, + regs.u.r16.di, + regs.u.r32.esi, + regs.u.r32.ebx, + regs.u.r32.ecx, + regs.u.r32.edx, + ®s.u.r32.edx); + CLEAR_CF(); + break; + + case 0x80: + regs.u.r32.eax = + __TCG_SendCommand(regs.u.r32.edx); + CLEAR_CF(); + break; + + default: + SET_CF(); + } + + default: + SET_CF(); + break; + } +} Index: root/xen-unstable.hg/tools/firmware/rombios/tcgbios.h =================================================================== --- /dev/null +++ root/xen-unstable.hg/tools/firmware/rombios/tcgbios.h @@ -0,0 +1,177 @@ +#ifndef TCGBIOS_H +#define TCGBIOS_H + +/* supported TPM interfaces */ +#define TPM_TIS 0x2 /* tpm_tis.h */ +#define TPM_ATMEL 0x1 /* tpm_atmel.h */ + +/* TCPA ACPI definitions */ +#define TCPA_ACPI_CLASS_CLIENT 0 +#define TCPA_ACPI_CLASS_SERVER 1 + +/* Define for section 12.3 */ +#define TCG_PC_OK 0x0 +#define TCG_PC_TPMERROR 0x1 +#define TCG_PC_LOGOVERFLOW 0x2 +#define TCG_PC_UNSUPPORTED 0x3 + +#define TPM_ALG_SHA 0x4 + +#define TCG_MAGIC 0x41504354L +#define TCG_VERSION_MAJOR 1 +#define TCG_VERSION_MINOR 2 + +#define TPM_OK 0x0 +#define TPM_RET_BASE 0x1 +#define TCG_GENERAL_ERROR (TPM_RET_BASE + 0x0) +#define TCG_TPM_IS_LOCKED (TPM_RET_BASE + 0x1) +#define TCG_NO_RESPONSE (TPM_RET_BASE + 0x2) +#define TCG_INVALID_RESPONSE (TPM_RET_BASE + 0x3) +#define TCG_INVALID_ACCESS_REQUEST (TPM_RET_BASE + 0x4) +#define TCG_FIRMWARE_ERROR (TPM_RET_BASE + 0x5) +#define TCG_INTEGRITY_CHECK_FAILED (TPM_RET_BASE + 0x6) +#define TCG_INVALID_DEVICE_ID (TPM_RET_BASE + 0x7) +#define TCG_INVALID_VENDOR_ID (TPM_RET_BASE + 0x8) +#define TCG_UNABLE_TO_OPEN (TPM_RET_BASE + 0x9) +#define TCG_UNABLE_TO_CLOSE (TPM_RET_BASE + 0xa) +#define TCG_RESPONSE_TIMEOUT (TPM_RET_BASE + 0xb) +#define TCG_INVALID_COM_REQUEST (TPM_RET_BASE + 0xc) +#define TCG_INVALID_ADR_REQUEST (TPM_RET_BASE + 0xd) +#define TCG_WRITE_BYTE_ERROR (TPM_RET_BASE + 0xe) +#define TCG_READ_BYTE_ERROR (TPM_RET_BASE + 0xf) +#define TCG_BLOCK_WRITE_TIMEOUT (TPM_RET_BASE + 0x10) +#define TCG_CHAR_WRITE_TIMEOUT (TPM_RET_BASE + 0x11) +#define TCG_CHAR_READ_TIMEOUT (TPM_RET_BASE + 0x12) +#define TCG_BLOCK_READ_TIMEOUT (TPM_RET_BASE + 0x13) +#define TCG_TRANSFER_ABORT (TPM_RET_BASE + 0x14) +#define TCG_INVALID_DRV_FUNCTION (TPM_RET_BASE + 0x15) +#define TCG_OUTPUT_BUFFER_TOO_SHORT (TPM_RET_BASE + 0x16) +#define TCG_FATAL_COM_ERROR (TPM_RET_BASE + 0x17) +#define TCG_INVALID_INPUT_PARA (TPM_RET_BASE + 0x18) +#define TCG_TCG_COMMAND_ERROR (TPM_RET_BASE + 0x19) +#define TCG_INTERFACE_SHUTDOWN (TPM_RET_BASE + 0x20) +//define TCG_PC_UNSUPPORTED (TPM_RET_BASE + 0x21) +#define TCG_PC_TPM_NOT_PRESENT (TPM_RET_BASE + 0x22) +#define TCG_PC_TPM_DEACTIVATED (TPM_RET_BASE + 0x23) + + +#define TPM_INVALID_ADR_REQUEST TCG_INVALID_ADR_REQUEST +#define TPM_IS_LOCKED TCG_TPM_IS_LOCKED +#define TPM_INVALID_DEVICE_ID TCG_INVALID_DEVICE_ID +#define TPM_INVALID_VENDOR_ID TCG_INVALID_VENDOR_ID +//define TPM_RESERVED_REG_INVALID +#define TPM_FIRMWARE_ERROR TCG_FIRMWARE_ERROR +#define TPM_UNABLE_TO_OPEN TCG_UNABLE_TO_OPEN +#define TPM_UNABLE_TO_CLOSE TCG_UNABLE_TO_CLOSE +#define TPM_INVALID_RESPONSE TCG_INVALID_RESPONSE +#define TPM_RESPONSE_TIMEOUT TCG_RESPONSE_TIMEOUT +#define TPM_INVALID_ACCESS_REQUEST TCG_INVALID_ACCESS_REQUEST +#define TPM_TRANSFER_ABORT TCG_TRANSFER_ABORT +#define TPM_GENERAL_ERROR TCG_GENERAL_ERROR + +#define TPM_ST_CLEAR 0x0 +#define TPM_ST_STATE 0x1 +#define TPM_ST_DEACTIVATED 0x2 + +/* event types: 10.4.1 / table 11 */ +#define EV_SEPARATOR 4 +#define EV_ACTION 5 +#define EV_EVENT_TAG 6 +#define EV_COMPACT_HASH 12 +#define EV_IPL 13 +#define EV_IPL_PARTITION_DATA 14 + + +// MA Driver defines +#define CODE_MAInitTPM 0x01 +#define CODE_MAHashAllExtendTPM 0x02 +#define CODE_MAPhysicalPresenceTPM 0x03 +/* vendor specific ones */ +#define CODE_MAIsTPMPresent 0x80 +#define CODE_MAHashAll 0x81 +#define CODE_MATransmit 0x82 + +/* + indices for commands to be sent via proprietary + _TCG_SendCommand function + */ +#define IDX_CMD_TPM_Startup_0x01 0 +#define IDX_CMD_TSC_PhysicalPresence_0x20 1 +#define IDX_CMD_TSC_PhysicalPresence_0x08 2 +#define IDX_CMD_TSC_PhysicalPresence_0x100 3 +#define IDX_CMD_TSC_PhysicalPresence_0x10 4 +#define IDX_CMD_TPM_PhysicalEnable 5 +#define IDX_CMD_TPM_PhysicalSetDeactivated_0x00 6 +#define IDX_CMD_TPM_SHA1Start 7 + + +/* hardware registers for TPM TIS */ +#define TPM_ACCESS 0x0 +#define TPM_INT_ENABLE 0x8 +#define TPM_INT_VECTOR 0xc +#define TPM_INT_STATUS 0x10 +#define TPM_INTF_CAPABILITY 0x14 +#define TPM_STS 0x18 +#define TPM_DATA_FIFO 0x24 +#define TPM_DID_VID 0xf00 +#define TPM_RID 0xf04 + +/* address of locality 0 (TIS) */ +#define TPM_TIS_BASE_ADDRESS 0xfed40000 +#define ACPI_2_0_TCPA_SIGNATURE ASCII32('T','C','P','A') /* "TCPA" */ + +#define ASCII32(a,b,c,d) ((((Bit32u)a) << 0) | (((Bit32u)b) << 8) | \ + (((Bit32u)c) << 16) | (((Bit32u)d) << 24) ) + +/* extension for the EBDA */ +typedef struct { + Bit32u tcpa_ptr; + Bit32u lasa_last_ptr; + Bit16u entry_count; +} tcpa_acpi_t; + +typedef struct { + tcpa_acpi_t tcpa_acpi; + Bit16u flags; + Bit16u reg_ss; + Bit16u reg_cs; + Bit16u reg_ds; + Bit16u reg_es; + Bit16u esp_hi; +} tcpa_t; + +#define STATUS_FLAG_SHUTDOWN (1 << 0) + + +/* some utility functions */ +Bit8u read_byte_addr32(addr); +Bit32u read_dword_addr32(addr); +void write_byte_addr32(addr, val); +Bit16u strlen(); + +/* tcpa ACPI related functions */ +Bit32u tcpa_get_lasa_base_ptr(); +Bit32u tcpa_get_lasa_last_ptr(); +Bit16u check_tcpa(); +Bit32u tcpa_extend_acpi_log(entry_ptr); +void tcpa_acpi_init(); +Bit16u tcpa_add_measurement_to_log(); +void tcpa_add_measurement(); +void tcpa_calling_int19h(); +void tcpa_returned_int19h(); +void tcpa_add_event_separators(); +void tcpa_start_option_rom_scan(); +void tcpa_wake_event(); +void tcpa_option_rom(); +void tcpa_ipl(); + +/* misc prototypes */ +void store_segment_registers(ss, cs, ds, es); +Bit16u get_register_ss(); +Bit16u get_register_cs(); +Bit16u get_register_ds(); +Bit16u get_register_es(); +Bit32u _TCG_HashAll(); + + +#endif Index: root/xen-unstable.hg/tools/firmware/rombios/tpm_atmel.c =================================================================== --- /dev/null +++ root/xen-unstable.hg/tools/firmware/rombios/tpm_atmel.c @@ -0,0 +1,355 @@ +/* + * Implementation of a low-level Atmel TPM driver + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Stefan Berger + */ + ASM_START + + ; a macro for reading the address of the data register from the TPM +MACRO GET_PORT_ADDR_DX + push eax ;pop@1 + xor eax, eax + mov al, #9 ; select register 9 + outb 0x4e, al + inb al, 0x4f ; read hi(address) + rol ax, #8 + mov al, #8 ; select register 8 + outb 0x4e, al + inb al, 0x4f ; read lo(address) + mov edx, eax ; edx contains (port) address now + pop eax ;@1 +MEND + + ;######################################################### + ; Device-specific code + ; TPM ATMEL interface + ;######################################################### +TPM_ActivateTPM32: + ; Activate the TPM via its interface + ; Input: + ; edx : base address of TPM (locality 0) [ignored here] + ; Output + ; ax : 0 = TPM not ready, 1 = TPM is ready + + START_PM_CODE + push ebx ;pop@1 + push ecx ;pop@2 + push edx ;pop@3 + + GET_PORT_ADDR_DX + + ; send an abort for any previously not-read response + mov al, #1 + inc dx + outb dx, al ; an ABORT + dec dx + + mov ecx, #20 + mov bl, #0x8 ;TPM Ready + call tpm_wait_state32 + + pop edx ;@3 + pop ecx ;@2 + pop ebx ;@1 + ret + + END_PM_CODE + +TPM_ReadyTPM32: + ; Activate the TPM via its interface + ; Input: + ; edx : base address of TPM (locality 0) [ignored here] + ; Output + ; ax : 0 = TPM not ready, 1 = TPM is ready + + START_PM_CODE + + call TPM_ActivateTPM32 + ret + + END_PM_CODE + + +TPM_SendDataTPM32: + ; Send a command (partial or complete) to the TPM + ; Input + ; edx : base address of TPM (locality 0) [ignored here] + ; eax : pointer to data to send + ; ecx : number of bytes to send + ; Output + ; none so far (!!!) + + START_PM_CODE + + push ecx ;pop@1 + push ebx ;pop@2 + push edx ;pop@3 + push eax ;pop@4 + + GET_PORT_ADDR_DX + + xor ebx, ebx + +tpm_senddatatpm32_loop_1: + + push eax ;pop@5 + mov al, [eax + ebx] + outb dx, al ;write to output port + pop eax ;@5 + + inc ebx + cmp ecx, ebx + je tpm_senddatatpm32_done + jmp tpm_senddatatpm32_loop_1 + +tpm_senddatatpm32_done: + pop eax ;@4 + pop edx ;@3 + pop ebx ;@2 + pop ecx ;@1 + ret + + END_PM_CODE + + +TPM_ReadResponseTPM32: + ; Input: + ; edx: base address of TPM [ignored here] + ; ebx: pointer to output buffer + ; ecx: length of output buffer + ; Output + ; none so far (!!!) + + START_PM_CODE + + push esi ;pop@1 + push edx ;pop@2 + push eax ;pop@3 + + GET_PORT_ADDR_DX + + xor esi, esi + +tpm_readresponsetpm32_loop_1: + ;return buffer exhausted? + cmp ecx, esi + je tpm_readresponsetpm32_end_1 + + push eax ;pop@A + push ecx ;pop@B + push ebx ;pop@C + + mov ecx, #1 + mov bl, #0x2 ;Data available mask + call tpm_wait_state32 + + cmp eax, #0 + je tpm_readresponsetpm32_end_2 ; timeout occurred + + ; data is available + + ; read data from TPM and write it into buffer + + pop ebx ;@C + + inb al, dx + mov [ebx + esi], al + + ; now only get eax/ecx back! + pop ecx ;@B + pop eax ;@A + + inc esi + jmp tpm_readresponsetpm32_loop_1 + +tpm_readresponsetpm32_end_2: + pop ebx ;@C + pop ecx ;@B + pop eax ;@A + +tpm_readresponsetpm32_end_1: + pop eax ;@3 + pop edx ;@2 + pop esi ;@1 + ret + + END_PM_CODE + +TPM_WaitDataValidTPM32: + ; Input + ; edx : base address of TPM (locality 0) [ignored here] + ; Ouput: + ; dl : error code, 0 for success + + START_PM_CODE + + mov dx, #0 + ret + + END_PM_CODE + +TPM_WaitResponseReadyTPM32: + ; Input: + ; edx: base of TPM [ignored here] + ; Ouput + ; dl : error code, 0 for success + + START_PM_CODE + + push eax ;pop@A + push ecx ;pop@B + push ebx ;pop@C + + GET_PORT_ADDR_DX + + mov ecx, #2000 ;2000ms + mov bl, #0x2 ;Data available mask + call tpm_wait_state32 + + mov dl, #0 + + cmp al, #0 + jne tpm_waitresponsereadytpm32_end_1 ; timeout NOT occurred + + mov dl, #(TCG_NO_RESPONSE) + +tpm_waitresponsereadytpm32_end_1: + ; data is available + pop ebx ;@C + pop ecx ;@B + pop eax ;@A + + ret + + END_PM_CODE + +TPM_Atmel_signature: + ; index , expected value + .byte 0x4, 0x41 + .byte 0x5, 0x54 + .byte 0x6, 0x4d + .byte 0x7, 0x4c + .byte 0x0, 0x1 + .byte 0x1, 0x1 + .byte 0xff + +TPM_IsPresentTPM32: + ; Simple test whether the TPM device is + ; available on this platfrom. + ; + ; Input: + ; edx: base of TPM [ignored here] + ; Output: + ; dx : 0 TPM is NOT present, != 0 TPM is present + + START_PM_CODE + + call TPM_ActivateTPM32 + + push ecx ;pop@1 + push ebx ;pop@2 + push eax ;pop@3 + + xor dx, dx ; assuming no TPM here + + mov ebx, #TPM_Atmel_signature + SEGMENT_OFFSET + xor ecx, ecx + +tpm_ispresenttpm32_loop_1: + mov al, [ebx + ecx] + outb 0x4e, al + inb al, 0x4f + + inc ecx + cmp [ebx + ecx], al + jne tpm_ispresenttpm32_notfound + + inc ecx + cmp byte #0[ebx + ecx], #0xff + jne tpm_ispresenttpm32_loop_1 + + mov dx, #1 ; a TPM is here + +tpm_ispresenttpm32_notfound: + + pop eax ;@3 + pop ebx ;@2 + pop ecx ;@1 + ret + + END_PM_CODE + + +tpm_wait_state32: + ; Wait for the state of the TPM or until the timeout + ; has been reached + ; Parameters: + ; Input: + ; edx : address of locality to be used [ignored here] + ; ecx : timeout in ms + ; bl : mask for 1[eax] to wait for + ; Result: + ; eax : 1 = state is reached, 0 = timeout occurred + + START_PM_CODE + + push ecx ;pop@1 + push ebx ;pop@2 + push edx ;pop@3 + +tpm_wait_state32_loop_1: + ; timeout reached? + dec ecx + cmp ecx, #0xffffffff + jne tpm_wait_state32_goon_1 + + xor eax, eax ;timeout has been reached + jmp tpm_wait_state32_exit_1 + +tpm_wait_state32_goon_1: + + ; read the state + inc dx + inb al, dx ; read state information + dec dx + + and al, bl ; mask the expected state + cmp al, bl ; compare against expected state + jne tpm_wait_state32_goon_2 + + ; state has been reached + mov eax, #1 + jmp tpm_wait_state32_exit_1 + +tpm_wait_state32_goon_2: + ; need to wait a little longer + mov eax, #1 + call wait_pit_ms_32 + + jmp tpm_wait_state32_loop_1 + +tpm_wait_state32_exit_1: + pop edx ;@3 + pop ebx ;@2 + pop ecx ;@1 + ret + + END_PM_CODE + + ASM_END Index: root/xen-unstable.hg/tools/firmware/rombios/tpm_tis.c =================================================================== --- /dev/null +++ root/xen-unstable.hg/tools/firmware/rombios/tpm_tis.c @@ -0,0 +1,347 @@ +/* + * Implementation of a low-level TPM TIS driver + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006 + * + * Author: Stefan Berger + */ + ASM_START + + ;######################################################### + ; Device-specific code + ; TPM-TIS interface + ;######################################################### +TPM_ActivateTPM32: + ; activate the TPM + ; Input: + ; edx : base address of TPM (locality 0) + ; Output: + ; eax : 0 = TPM not ready, 1 = TPM is ready + + START_PM_CODE + + push bx ;pop@1 + push ecx ;pop@2 + + mov bl, #0x2 ; request access to locality + mov TPM_ACCESS[edx], bl + + mov bl, TPM_ACCESS[edx] ; got it? + and bl, #0x20 + jne tpm_activatetpm32_goon_0 + + xor eax, eax + jmp tpm_activatetpm32_exit_0 + +tpm_activatetpm32_goon_0: + mov bl, #%01000000 ; make TPM ready + mov TPM_STS[edx],bl ; p. 53 + + mov ecx, #100 ; wait 100ms for TPM to be ready + mov bl, #%01000000 ; active signal + + call tpm_wait_sts32 + // return eax as returned from tpm_wait_sts32 + +tpm_activatetpm32_exit_0: + pop ecx ;@2 + pop bx ;@1 + ret + + END_PM_CODE + + + +TPM_ReadyTPM32: + ; put the TPM into ready state so it accepts commands + ; Input: + ; edx : base address of TPM (locality 0) + ; Output: + ; eax : 0 = TPM not ready, 1 = TPM is ready + START_PM_CODE + + push bx ;pop@1 + push ecx ;pop@2 + + mov bl, #%01000000 ; make TPM ready + mov TPM_STS[edx],bl ; p. 53 + + mov ecx, #100 ; wait 100ms for TPM to be ready + mov bl, #%01000000 ; active signal + + call tpm_wait_sts32 + // return eax as returned from tpm_wait_sts32 + + pop ecx ;@2 + pop bx ;@1 + ret + + END_PM_CODE + + + +TPM_SendDataTPM32: + ; Send a command to the TPM + ; Input + ; edx : base address of TPM (locality 0) + ; eax : pointer to data to send + ; ecx : number of bytes to send + ; Output + ; none so far (!!!) + + START_PM_CODE + + push bx ;pop@1 + push esi ;pop@2 + push ecx ;pop@3 + + xor esi, esi + +tpm_sendcommandtpm32_loop_1: + mov bl, TPM_STS+1[edx] ; read burst + mov bh, TPM_STS+2[edx] ; + and bx, bx + jne tpm_sendcommandtpm32_goon_2 + push eax ;pop@A + mov eax, #1 + call wait_pit_ms_32 + pop eax ;@A + jmp tpm_sendcommandtpm32_loop_1 + +tpm_sendcommandtpm32_goon_2: + +tpm_sendcommandtpm32_loop_2: + ; can send data + push cx ;pop@B + mov cl,[eax+esi] + mov TPM_DATA_FIFO[edx],cl ; p. 54 + pop cx ;@B + inc esi + + ; anything left to transmit? + cmp ecx, esi + + je tpm_sendcommandtpm32_exit_1 + + ; yes. any burst room left? + dec bx + and bx, bx + jne tpm_sendcommandtpm32_loop_2 + + ; must get burst room again + jmp tpm_sendcommandtpm32_loop_1 + +tpm_sendcommandtpm32_exit_1: + pop ecx ;@3 + pop esi ;@2 + pop bx ;@1 + ret + + END_PM_CODE + +TPM_ReadResponseTPM32: + ; Input: + ; edx: base address of TPM + ; ebx: pointer to output buffer + ; ecx: length of output buffer + ; Output + ; none so far (!!!) + + START_PM_CODE + + push esi ;pop@1 + push eax ;pop@2 + + xor esi, esi + +tpm_readresponsetpm32_loop_1: + ; return buffer exhausted? + cmp ecx, esi + je tpm_readresponsetpm32_end_1 + + mov al, TPM_DATA_FIFO[edx] ; read data + mov [ebx+esi], al ; write to output buffer + + inc esi + ; still data available? - nothing left if flag is cleared + mov al, TPM_STS[edx] ; read flags + and al, #%00010000 ; dataAvail? + je tpm_readresponsetpm32_end_1 + jmp tpm_readresponsetpm32_loop_1 + +tpm_readresponsetpm32_end_1: + pop eax ;@2 + pop esi ;@1 + ret + + END_PM_CODE + +TPM_WaitDataValidTPM32: + ; Input + ; edx : base address of TPM (locality 0) + ; Ouput: + ; dl : error code, 0 for success + + START_PM_CODE + + push ebx ;pop@A + push ecx ;pop@B + push eax ;pop@C + + mov bl, #%10000000 ;dataValid mask + mov ecx, #1000 ;max wait 1000ms + call tpm_wait_sts32 ;wait for TPM state + + mov dl, #0 + + ; state reached? + cmp al, #1 + je tpm_waitdatavalidtpm32_goon_1 + + mov dl, #(TCG_NO_RESPONSE) + +tpm_waitdatavalidtpm32_goon_1: + pop eax ;@C + pop ecx ;@B + pop ebx ;@A + + ret + + END_PM_CODE + +TPM_WaitResponseReadyTPM32: + ; Input: + ; edx: base of TPM + ; Ouput + ; dl : error code, 0 for success + + START_PM_CODE + + push ebx ;pop@A + push ecx ;pop@B + push eax ;pop@C + + ; all data transmitted and TPM is happy + ; start processing the command on the TPM + mov cl, #%00100000 + mov TPM_STS[edx], cl ; p. 53 (tpmGo) + + mov bl, #%00010000 ;dataAvail mask + mov ecx, #2000 ;max wait 2000ms + call tpm_wait_sts32 ;wait for TPM state + + mov dl, #0 + ; state reached? + cmp al, #1 + je tpm_waitresponsereadytpm32_goon_3 + + mov dl, #(TCG_NO_RESPONSE) + +tpm_waitresponsereadytpm32_goon_3: + ; data is indicated as being available + pop eax ;@C + pop ecx ;@B + pop ebx ;@A + ret + + END_PM_CODE + +TPM_IsPresentTPM32: + ; Simple test whether the TPM device (TPM TIS) is + ; available on this platfrom. + ; + ; Input: + ; edx: base of TPM + ; Output: + ; dx : 0 TPM is NOT present, != 0 TPM is present + + START_PM_CODE + + push ecx ;pop@1 + + mov ecx, #TPM_DID_VID[edx] ; read TPM_DID_VID_0 + + ; Assuming that a TPM is present + mov dx, #1 + + cmp ecx, #0 ; only '0' and '-1' are not recognized as TPMs + jne tpm_ispresenttpm32_exit_1 + cmp ecx, #0xffffffff + jne tpm_ispresenttpm32_exit_1 + + ; no TPM present + xor dx, dx + +tpm_ispresenttpm32_exit_1: + + pop ecx ;@1 + ret + + END_PM_CODE + +tpm_wait_sts32: + ; Wait for the state of the TPM or until the timeout + ; has been reached + ; Parameters: + ; Input: + ; edx : address of locality to be used + ; ecx : timeout in ms + ; bl : mask for TP_STS + ; Result: + ; eax : 1 = state is reached, 0 = timeout occurred + + START_PM_CODE + + push ecx ;pop@1 + push ebx ;pop@2 + +tpm_wait_sts32_loop_1: + ; timeout reached? + dec ecx + cmp ecx, #0xffffffff + jne tpm_wait_sts32_goon_1 + + xor eax, eax ;timeout has been reached + jmp tpm_wait_sts32_exit_1 + +tpm_wait_sts32_goon_1: + ; read the state + mov bh, TPM_STS[edx] + and bh, bl + cmp bh, bl + jne tpm_wait_sts32_goon_2 + + ; state has been reached + mov eax, #1 + jmp tpm_wait_sts32_exit_1 + +tpm_wait_sts32_goon_2: + ; need to wait a little longer + + mov eax, #1 + call wait_pit_ms_32 + + jmp tpm_wait_sts32_loop_1 + +tpm_wait_sts32_exit_1: + pop ebx ;@2 + pop ecx ;@1 + ret + + END_PM_CODE + + ASM_END