diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2015-05-21 09:12:50 +0200 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2015-05-21 11:44:57 +0200 |
commit | c9b6e84c9284e5283439135c226638b06ac05193 (patch) | |
tree | 54345135a63c6d4372afcde92b2004367eba789d /pflash.c | |
parent | 25aa128050881068db3ca3212550024214bfad34 (diff) | |
download | qboot-c9b6e84c9284e5283439135c226638b06ac05193.zip qboot-c9b6e84c9284e5283439135c226638b06ac05193.tar.gz qboot-c9b6e84c9284e5283439135c226638b06ac05193.tar.bz2 |
initial support for pflash / cbfs
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'pflash.c')
-rw-r--r-- | pflash.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/pflash.c b/pflash.c new file mode 100644 index 0000000..950bc74 --- /dev/null +++ b/pflash.c @@ -0,0 +1,63 @@ +#include <stddef.h> +#include "bios.h" +#include "pflash.h" +#include "stdio.h" + +#define CLEAR_STATUS_CMD 0x50 +#define READ_STATUS_CMD 0x70 +#define QUERY_CMD 0x98 +#define READ_ARRAY_CMD 0xff + +static void *pflash_detect(uint8_t *top_addr) +{ + volatile uint8_t *p = top_addr; + uint8_t save; + int i, blocks, sector_len; + + /* The low byte of the address is part of the command, so it must be 0. */ + if ((uintptr_t)p & 256) + panic(); + + p -= 256; + for (i = 0; i < 256; i++) + if (p[i] != CLEAR_STATUS_CMD) + break; + if (i == 256) + return NULL; + + save = p[i]; + p[i] = CLEAR_STATUS_CMD; + if (p[i] == CLEAR_STATUS_CMD) { + /* behaves as RAM */ + p[i] = save; + return NULL; + } + p[i] = READ_STATUS_CMD; + if (p[i] != 0) { + /* doesn't behave as flash */ + return NULL; + } + + /* 0x2d-0x2e: blocks_per_device - 1, little endian */ + /* 0x2f-0x30: sector_len / 256 */ + p[i] = QUERY_CMD; + blocks = p[0x2d] + (p[0x2e] << 8) + 1; + sector_len = (p[0x2f] + (p[0x30] << 8)) << 8; + + p[i] = READ_ARRAY_CMD; + return top_addr - blocks * sector_len; +} + +void *pflash_base(int n, size_t *size) +{ + uint8_t *top = NULL; + uint8_t *prev; + while (n-- >= 0) { + prev = top; + top = pflash_detect(top); + if (!top) + return NULL; + *size = prev - top; + } + return top; +} |